动态加载python源代码 [英] Dynamically loading python source code

查看:166
本文介绍了动态加载python源代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在玩Flask,我无法弄清调试机制是如何工作的。为了更准确地说,当我使用我的应用程序保存python文件时,我不需要重新启动服务器,当我发出请求时,它将被自动加载。所以我的问题是运行程序如何知道它已经改变了,并且对这些修改做出了回应?

解决方案

Flask正在使用Werkzug的底层 run_with_reloader 函数(在 serve.py 中找到)...它本身使用 restart_with_reloader reloader_loop 在同一个文件中创建的函数。



run_with_reloader 产生另一个python进程(再次运行Werkzug,所有相同的参数传递给第一个)并且这个新进程使用线程模块来生成运行服务器功能的新线程或子进程。然后运行 reloader_loop 并等待。



reloader_loop 简单地循环所有已导入的模块,并获得最后修改的日期。然后以指定的间隔(默认为1秒),它会再次检查所有文件,看看它们是否已被修改。如果有的话,当前正在运行的(从属)Werkzug进程退出(终止),退出代码为3.一旦它退出,它启动的线程或子进程(实际上正在进行工作)被终止以及。 主程序进程将检查退出代码是否为3.如果是,则它将生成一个新的从属子进程,就像以前一样。



以下是代码供参考:

  def reloader_loop(extra_files = None,interval = 1):
当这个函数从主线程运行时,它将强制其他
线程退出当任何模块当前加载更改

版权声明此功能基于来自
的autoreload.py,来自于现在已经死亡的WSGIKit的CherryPy trac

:param extra_files :

def iter_module_files():
sys.modules.values()中的模块
filename = getattr(module ,'__file__',无)
如果文件名:
old =无
而不是os.path.isfile(filename):
old = filename
filename = os .path.dirname(filename)
如果filename == old:
break
else:
如果文件名[-4:] in('.pyc','.pyo'):
filename = filename [: - 1]
yield filename

mtimes = {}
而1:
为链中的文件名(iter_module_files(),extra_files或()):
try:
mtime = os .stat(filename).st_mtime
除了OSError:
继续

old_time = mtimes.get(filename)
如果old_time为None:
mtimes [ filename] = mtime
continue
elif mtime> old_time:
_log('info','*检测到的%r更改,重新加载'%filename)
sys.exit(3)
time.sleep(interval)


def restart_with_reloader():
生成一个新的Python解释器,与这个参数相同,
但运行重新加载程序线程

while 1:
_log('info','*重新启动重新加载程序...')
args = [sys.executable] + sys.argv
new_environ = os.environ .copy()
new_environ ['WERKZEUG_RUN_MAIN'] ='true'

#一个奇怪的bug在Windows上。有时unicode字符串最终在
#环境中,subprocess.call不喜欢这个,将它们编码为
#到latin1并继续。
如果os.name =='nt':
为key,value在new_environ.iteritems()中:
如果isinstance(value,unicode):
new_environ [key] = value.encode('iso-8859-1')

exit_code = subprocess.call(args,env = new_environ)
如果exit_code!= 3:
返回exit_code


def run_with_reloader(main_func,extra_files = None,interval = 1):
在独立的python解释器中运行给定的函数。
如果os .environ.get('WERKZEUG_RUN_MAIN')=='true':
thread.start_new_thread(main_func,())
try:
reloader_loop(extra_files,interval)
除了KeyboardInterrupt :
return
try:
sys.exit(restart_with_reloader())
除了KeyboardInterrupt:
pass


I'm currently playing with Flask and I can't figure out how the debug mechanism is working. To be more precise when I save the python file with my application I don't need to restart the server, it will be loaded automatically when I make a request. So my question is how does the running program know it was changed and respond to that changes?

解决方案

Flask is using Werkzug's underlying run_with_reloader function (found in serving.py) ... which is itself using the restart_with_reloader and reloader_loop function created earlier in the same file.

run_with_reloader spawns another python process (running Werkzug again with all the same arguments that you passed to the first one) and this new processes uses the thread module to spawn a new thread or subprocess that runs your server function. It then runs the reloader_loop and waits.

reloader_loop simply loops through all the modules that have been imported and gets their last modified dates. Then at specified intervals (which defaults to 1 s) it checks all the files again to see if they've been modified. If they have, the currently running (slave) Werkzug process exits (terminates) with an exit code of 3. Once it exits, the thread or subprocess it started (which is actually doing the work) is terminated as well. The master process checks to see if the exit code was 3. If it was, it spawns a new slave subprocess, just as it did before. Otherwise, it exits with the same exit code.

Here's the code for reference:

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

这篇关于动态加载python源代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆