Change the code and see the result
An interesting idea came to me several days ago. I have to write C++ code generator (sourcing data from SQL database), so I installed Jinja2, sharpened text editors and then asked myself: „Wouldn’t it be beautiful to see the result just as I type Python code, portion by portion? There’s a use for two monitors: the first one displays coding, the second one keeps freshly generated result always ready for review.“
I liked and cherished the idea, and it was maturing: my workbox runs Gentoo Linux, and Linux kernel supports inotify — file change notification system, and there’s Python bindings for it. Moreover, since we want not plain text but source code to get displayed, Pygments spring into mind instantly — why not to pygmentize output? And, of course, standard for modern *nix distribuition but glorious neverthe**less** to page the result.
So the idea is basically this: we start watcher-script, which watches (looping endlessly) all the source files we want, and when files are changed, reloads modules, re-runs key function, pygmentizes result string, pipes it to less’s stdin through UNIX pipe and waits for next filechange event.
And here’s the code:
#!/usr/bin/env python # -*- coding: utf-8 -*- from inotifyx import * import sys, os, logging, subprocess import templater from pygments import highlight from pygments.lexers import HtmlLexer from pygments.formatters import TerminalFormatter # Logging is helpful in debugging LOG_FILENAME = '/tmp/debug_viewer.log' logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) # Initting inotifyx fd = init() watcher = add_watch(fd, 'templater.py', IN_MODIFY) try: # Starting infinite loop while True: logging.debug('Starting iteration\n') try: reload(templater) string = templater.render_template() highlighted = highlight(string, HtmlLexer(), TerminalFormatter()) logging.debug('Spawning less\n') less = subprocess.Popen(['less', '-'], stdin=subprocess.PIPE) logging.debug('less spawned, piping data\n') less.stdin.write(highlighted) less.stdin = sys.stdin except SyntaxError, e_text: print 'SyntaxError was raised' print SyntaxError logging.debug('Data piped, waiting for inotification\n') get_events(fd) logging.debug('Inotified, killing less\n') less.terminate() logging.debug('_______________________\n') except KeyboardInterrupt: less.terminate() os.close(fd)
Treat log messages I used for debugging (tail -f in other terminal) as a comments (-:E
Few things worth mentioning:
there’s two packages binding Python to inotify system: more complex and established pyinotify and newer and simpler inotifyx. I opted for second one, because of declared simplicity and speed
script stdin.writes() to spawned less process, not Popen.communicates(). This is because .communicate() method waits for returned data, and we don’t need this
the trick I like most in this quite straightforward script is binding terminal STDIN to less‘es one after piping in highlighted code. Before I did this, less was incontrollable, since keypresses didn’t reach it
Comments
Comments powered by Disqus