动态加载python源代码 [英] Dynamically loading python source code
问题描述
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屋!