Пишешь код и видишь результат

Пришла мне тут в голову интересная идея. Нужно написать генератор кода на C++ (по данным, вытягиваемым из базы данных), так что я установил Jinja2, навострил текстовые редакторы и подумал: «А не будет ли круто сразу видеть результат, прямо по ходу написания кода? Зачем мне два монитора, в конце концов — на одном буду писать, а второй пусть результат кажет.»

Сказано — сделано. Идея потихоньку назревала: работаю я на Gentoo Linux, а ядро Linux умеет inotify — систему оповещения об изменениях в файлах, и существуют обвязки вокруг неё для Python. Раз мы будем смотреть не просто текст, а код, то сразу вспоминается Pygments — будем и расцвечивать его, не отходя от кассы. Ну и выводить всё в терминал будем не просто так, а через стандартный **less**.

Итак, в общих чертах: запускаем сценарий, который в бесконечной петле будет следить за изменениями во всех нужных файлах и, когда файлы изменяются, перезагружать модуль, запускать главную функцию, раскрашивать результат, скармливать его less‘у и ждать следующего события.

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)

За комментарии считайте записи для журналирования (следил за ними через tail -f в третьем терминале)

Пара примечаний:

  • существует минимум два комплекта обвязок Python+inotify: pyinotify, побольше и посерьёзней, inotifyx, помоложе и попроще; я выбрал вторую, по причине обещанной простоты и лёгкости;

  • данные для less мы пишем в стандартный ввод (stdin.write()), а не «общаемся» с ним через Popen.communicate(), потому что метод communicate() ждёт ответа от процесса, а нам того не надобно;

  • уловка, которая мне нравится в этом (довольно прямолинейном, в общем-то), сценарийчике: после скармливания строки less‘у, мы вешаем ему на стандартный ввод (который был каналом — pipe) стандартный ввод интерпретатора (фактически, терминала). Если этого не сделать, то less будет глух к командам извне — нажатия кнопок в терминале до него просто не дойдут.

Сценарийчик (уже безжалостно перепиленный) для использования в командной строке на GitHub

Комментарии

Comments powered by Disqus