最近在用 grpc1,发现 grpc 的 Python server 目前还没有像 Flask 那样的修改后自动 reload ,开发不是很方便。
所以就看看有什么比较好的实现,发现 werkzeug2已经有个比较好的实现,而且 Flask 用的就是它。就不用重复发明轮子了。
假设我们的启动 server 的代码写在了run_server里面,我们可以将其传入到 werkzeug 的run_with_reloader,就会拥有监控文件改变自动 reload 的功能。
1 2 3 4 5 6 7 |
from werkzeug._reloader import run_with_reloader
main_func = partial(run_server, grpc_host, grpc_port, concurrent)
if autoreload:
run_with_reloader(main_func)
else:
main_func()
|
原理
入口程序(主进程)进入run_with_reloader后,因为此时环境变量中没有WERKZEUG_RUN_MAIN,所以不会运行主逻辑run_server。而是会取出自己的命令行参数,设置好WERKZEUG_RUN_MAIN环境变量,通过 subprocess 创建一个自己,这个时候子进程判断设置了WERKZEUG_RUN_MAIN,此时才运行真正的程序逻辑。
werkzeug 的 reloader 封装了 Stat 和 WatchDog 两种 reloader ,当子进程监控到文件改变后,会调用trigger_reload退出自己。然后主进程判断特殊的返回码3后再次启动子进程。
从而完成了监控文件改变,自动 reload 自己的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class ReloaderLoop(object):
# ...
def restart_with_reloader(self):
"""Spawn a new Python interpreter with the same arguments as this one,
but running the reloader thread.
"""
while 1:
_log('info', ' * Restarting with %s' % self.name)
args = _get_args_for_reloading()
new_environ = os.environ.copy()
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
# ...
exit_code = subprocess.call(args, env=new_environ, close_fds=False)
if exit_code != 3:
return exit_code
def trigger_reload(self, filename):
self.log_reload(filename)
sys.exit(3)
def run_with_reloader(main_func, extra_files=None, interval=1,
reloader_type='auto'):
"""Run the given function in an independent python interpreter."""
import signal
reloader = reloader_loops[reloader_type](extra_files, interval)
signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
try:
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
t = threading.Thread(target=main_func, args=())
t.setDaemon(True)
t.start()
reloader.run()
else:
sys.exit(reloader.restart_with_reloader())
except KeyboardInterrupt:
pass
|
Links
PermaLink:
http://everet.org/python-autoreload.html
Tags:
Python
由 udpwork.com 聚合
|
评论: 0
|
要! 要! 即刻! Now!