【问题标题】:Dynamically loading python source code动态加载python源码
【发布时间】:2011-05-29 16:24:52
【问题描述】:

我目前正在使用 Flask,但无法弄清楚调试机制是如何工作的。更准确地说,当我用我的应用程序保存 python 文件时,我不需要重新启动服务器,它会在我发出请求时自动加载。所以我的问题是正在运行的程序如何知道它已更改并响应更改?

【问题讨论】:

    标签: python dynamic flask


    【解决方案1】:

    内置的reload() 函数可以为您做到这一点。这个函数很可能是 Flask 重新加载代码的原因(在注意到它在磁盘上以某种方式发生了变化之后)。

    问题How do I unload (reload) a Python module?对此有更多信息。

    【讨论】:

    • 你每天都会学到新东西! Flask 不会使用它来重新加载所有内容,但感谢您提供的信息! (请参阅我的回答,了解我 认为 正在发生的事情)。
    • 使用 reload() 会导致非常奇怪的行为,尤其是在大型应用程序中。例如,考虑某人使用 from-import 导入某些内容,然后有人重新加载该模块的情况。唯一明智的方法是像 werkzeug 那样使用单独的过程。
    【解决方案2】:

    Flask 正在使用 Werkzug 的底层 run_with_reloader 函数(在 serving.py 中找到)......它本身使用了之前在同一文件中创建的 restart_with_reloaderreloader_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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多