Динамическая загрузка исходного кода Python - PullRequest
6 голосов
/ 23 декабря 2010

Я сейчас играю с Flask и не могу понять, как работает механизм отладки.Чтобы быть более точным, когда я сохраняю файл python с моим приложением, мне не нужно перезагружать сервер, он будет загружен автоматически при выполнении запроса.Итак, мой вопрос: как работающая программа знает, что она была изменена, и реагирует на эти изменения?

Ответы [ 2 ]

7 голосов
/ 23 декабря 2010

Flask использует базовую функцию Werkzug run_with_reloader (найденную в serving.py) ..., которая сама использует функции restart_with_reloader и reloader_loop, созданные ранее в том же файле.

run_with_reloader порождает другой процесс python (снова запускает Werkzug со всеми теми же аргументами, которые вы передали первому), и этот новый процесс использует модуль thread для порождения нового потока или подпроцесса, который запускает вашсерверная функция.Затем он запускает reloader_loop и ждет.

reloader_loop просто проходит по всем импортированным модулям и получает даты их последнего изменения.Затем с заданными интервалами (по умолчанию 1 с) он снова проверяет все файлы, чтобы увидеть, были ли они изменены.Если они есть, текущий запущенный ( slave ) процесс Werkzug завершается (завершается) с кодом выхода 3. Как только он выходит, поток или подпроцесс, который он запустил (который фактически выполняет работу), завершается какЧто ж.Процесс master проверяет, был ли код выхода равен 3. Если это так, он запускает новый подпроцесс slave , как и раньше.В противном случае он завершается с тем же кодом выхода.

Вот код для справки:

def reloader_loop(extra_files=None, interval=1):
    """When this function is run from the main thread, it will force other
    threads to exit when any modules currently loaded change.

    Copyright notice.  This function is based on the autoreload.py from
    the CherryPy trac which originated from WSGIKit which is now dead.

    :param extra_files: a list of additional files it should watch.
    """
    def iter_module_files():
        for module in sys.modules.values():
            filename = getattr(module, '__file__', None)
            if filename:
                old = None
                while not os.path.isfile(filename):
                    old = filename
                    filename = os.path.dirname(filename)
                    if filename == old:
                        break
                else:
                    if filename[-4:] in ('.pyc', '.pyo'):
                        filename = filename[:-1]
                    yield filename

    mtimes = {}
    while 1:
        for filename in chain(iter_module_files(), extra_files or ()):
            try:
                mtime = os.stat(filename).st_mtime
            except OSError:
                continue

            old_time = mtimes.get(filename)
            if old_time is None:
                mtimes[filename] = mtime
                continue
            elif mtime > old_time:
                _log('info', ' * Detected change in %r, reloading' % filename)
                sys.exit(3)
        time.sleep(interval)


def restart_with_reloader():
    """Spawn a new Python interpreter with the same arguments as this one,
    but running the reloader thread.
    """
    while 1:
        _log('info', ' * Restarting with reloader...')
        args = [sys.executable] + sys.argv
        new_environ = os.environ.copy()
        new_environ['WERKZEUG_RUN_MAIN'] = 'true'

        # a weird bug on windows. sometimes unicode strings end up in the
        # environment and subprocess.call does not like this, encode them
        # to latin1 and continue.
        if os.name == 'nt':
            for key, value in new_environ.iteritems():
                if isinstance(value, unicode):
                    new_environ[key] = value.encode('iso-8859-1')

        exit_code = subprocess.call(args, env=new_environ)
        if exit_code != 3:
            return exit_code


def run_with_reloader(main_func, extra_files=None, interval=1):
    """Run the given function in an independent python interpreter."""
    if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
        thread.start_new_thread(main_func, ())
        try:
            reloader_loop(extra_files, interval)
        except KeyboardInterrupt:
            return
    try:
        sys.exit(restart_with_reloader())
    except KeyboardInterrupt:
        pass
0 голосов
/ 23 декабря 2010

Встроенная функция reload() может сделать это за вас. Вероятно, эта функция стоит за тем, что делает Flask для перезагрузки кода (заметив, что он каким-то образом изменился на диске).

Вопрос Как выгрузить (перезагрузить) модуль Python? содержит дополнительную информацию по этому вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...