【发布时间】:2011-05-29 16:24:52
【问题描述】:
我目前正在使用 Flask,但无法弄清楚调试机制是如何工作的。更准确地说,当我用我的应用程序保存 python 文件时,我不需要重新启动服务器,它会在我发出请求时自动加载。所以我的问题是正在运行的程序如何知道它已更改并响应更改?
【问题讨论】:
我目前正在使用 Flask,但无法弄清楚调试机制是如何工作的。更准确地说,当我用我的应用程序保存 python 文件时,我不需要重新启动服务器,它会在我发出请求时自动加载。所以我的问题是正在运行的程序如何知道它已更改并响应更改?
【问题讨论】:
内置的reload() 函数可以为您做到这一点。这个函数很可能是 Flask 重新加载代码的原因(在注意到它在磁盘上以某种方式发生了变化之后)。
【讨论】:
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
【讨论】: