Как отслеживать изменения в файлах Python?

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

Как получить фактические имена файлов из импортированных пакетов / модулей?

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


person deamon    schedule 05.10.2010    source источник
comment
Кто-то уже указал вам на Django (stackoverflow.com/questions/3862871), в котором есть код для этого.   -  person Glenn Maynard    schedule 05.10.2010
comment
Спасибо, что упомянули механизм Django. Это было спусковым крючком для этого вопроса (но не ответом).   -  person deamon    schedule 05.10.2010
comment
Нет, механизм Django - это ответ. Код для этого находится прямо в Django, о котором вы упоминали.   -  person Glenn Maynard    schedule 06.10.2010
comment
Вы правы: code.djangoproject.com/browser/django / trunk / django / utils /   -  person deamon    schedule 06.10.2010


Ответы (5)


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

HTH.

person GoraKhargosh    schedule 19.11.2010
comment
И я только что написал прекрасный маленький скрипт с помощью Watchdog: zolomon.com/wp/?p=382 - это настоящая прелесть! - person Zolomon; 14.12.2010
comment
Есть ли примеры использования сторожевого таймера для перезапуска существующего процесса в случае изменения каких-либо его исходных файлов? - person Eddie Parker; 03.04.2020

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

person Ignacio Vazquez-Abrams    schedule 05.10.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...'
person Matt Anderson    schedule 05.10.2010

Вот пример того, как это можно реализовать с помощью 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).

person Siegfried Gevatter    schedule 28.11.2012

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

person fschmitt    schedule 05.10.2010