最近在用 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!