Как отслеживать файлы Python на предмет изменений? - PullRequest
4 голосов
/ 05 октября 2010

Я хочу перезапустить свое веб-приложение Python, если код изменился.Но может быть большое количество файлов, которые можно изменить, поскольку файлы в импортированных модулях могут измениться ...

Как получить фактические имена файлов из импортированных пакетов / модулей?можно ли эффективно обнаруживать измененные файлы Python?Есть ли библиотека для этого?

Ответы [ 5 ]

6 голосов
/ 19 ноября 2010

Бесстыдная вилка.Есть также http://github.com/gorakhargosh/watchdog, над которым я работаю, чтобы сделать именно это.

HTH.

1 голос
/ 29 ноября 2012

Вот пример того, как это можно реализовать с помощью pyinotify (т. Е. В Linux).

from importlib import import_module

class RestartingLauncher:

    def __init__(self, module_name, start_function, stop_function, path="."):
        self._module_name = module_name
        self._filename = '%s.py' % module_name
        self._start_function = start_function
        self._stop_function = stop_function
        self._path = path
        self._setup()

    def _setup(self):
        import pyinotify
        self._wm = pyinotify.WatchManager()

        self._notifier = pyinotify.ThreadedNotifier(
                self._wm, self._on_file_modified)
        self._notifier.start()

        # We monitor the directory (instead of just the file) because
        # otherwise inotify gets confused by editors such a Vim.
        flags = pyinotify.EventsCodes.OP_FLAGS['IN_MODIFY']
        wdd = self._wm.add_watch(self._path, flags)

    def _on_file_modified(self, event):
        if event.name == self._filename:
            print "File modification detected. Restarting application..."
            self._reload_request = True
            getattr(self._module, self._stop_function)()

    def run(self):
        self._module = import_module(self._module_name)

        self._reload_request = True
        while self._reload_request:
            self._reload_request = False
            reload(self._module)
            getattr(self._module, self._start_function)()

        print 'Bye!'
        self._notifier.stop()

def launch_app(module_name, start_func, stop_func):
    try:
        import pyinotify
    except ImportError:
        print 'Pyinotify not found. Launching app anyway...'
        m = import_module(self._module_name)
        getattr(m, start_func)()
    else:
        RestartingLauncher(module_name, start_func, stop_func).run()

if __name__ == '__main__':
    launch_app('example', 'main', 'force_exit')

Параметры в вызове launch_app - это имя файла (без ".py"),функция для запуска выполнения и функция, которая каким-то образом останавливает выполнение.

Вот глупый пример «приложения», которое можно (повторно) запустить с использованием предыдущего кода:

run = True

def main():
    print 'in...'
    while run: pass
    print 'out'

def force_exit():
    global run
    run = False

В типичном приложении, где вы хотели бы использовать это, вы, вероятно, имели бы какой-то основной цикл.Вот более реальный пример для приложения на базе GLib / GTK +:

from gi.repository import GLib

GLib.threads_init()
loop = GLib.MainLoop()

def main():
    print "running..."
    loop.run()

def force_exit():
    print "stopping..."
    loop.quit()

Та же концепция работает для большинства других циклов (Clutter, Qt и т. Д.).

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

Примечание. Весь код в этом ответе выпущен под лицензией ISC (в дополнение к Creative Commons).

1 голос
/ 05 октября 2010

gamin - это еще один вариант, который немного менее специфичен для Linux.

1 голос
/ 05 октября 2010

Я не уверен, как бы вы реализовали операцию «перезагрузить приложение» в ваших обстоятельствах; перезагрузка измененного модуля со встроенным reload, вероятно, не обрезает его.

Но что касается определения того, было ли изменение, то одним из способов приблизиться к нему было бы следующее.

  • Большинство модулей Python имеют атрибут __file__.
  • Все загруженные модули хранятся в sys.modules.
  • Мы можем пройти через sys.modules через некоторый интервал и искать изменения на диске для каждого модуля по очереди

Иногда __file__ указывает на файл .pyc вместо файла .py, поэтому вам, возможно, придется отрубить конечный символ c. Иногда файл .pyc существует, но .py не существует; в надежной системе вам придется это учитывать.

Подтверждение концепции кода для этого (не робастного):

_module_timestamps = {}
_checking = False

def run_checker():
    global _checking
    _checking = True
    while _checking:
        for name, module in sys.modules.iteritems():
            if hasattr(module, '__file__'):
                filename = module.__file__
                if filename.endswith('.pyc'):
                    filename = filename[:-1]
                mtime = os.stat(filename).st_mtime
                if name not in _module_timestamps:
                    _module_timestamps[name] = mtime
                else:
                    if mtime > _module_timestamps[name]:
                        do_reload(name)
            else:
                'module %r has no file attribute' % (name,)
        time.sleep(1)

def do_reload(modname):
    print 'I would reload now, because of %r' % (modname,)

check_thread = threading.Thread(target=run_checker)
check_thread.daemon = True
check_thread.start()

try:
    while 1:
        time.sleep(0.1)
except KeyboardInterrupt:
    print '\nexiting...'
0 голосов
/ 05 октября 2010

Это зависит от операционной системы.Для Linux есть inotify, см. Например http://github.com/rvoicilas/inotify-tools/

...