设为首页 收藏本站
查看: 1029|回复: 0

[经验分享] Python自动重新加载模块(autoreload module)

[复制链接]

尚未签到

发表于 2015-4-22 09:27:35 | 显示全部楼层 |阅读模式
守护进程模式
  使用python开发后台服务程序的时候,每次修改代码之后都需要重启服务才能生效比较麻烦。看了一下Python开源的Web框架(Django、Flask等)都有自己的自动加载模块功能(autoreload.py),都是通过subprocess模式创建子进程,主进程作为守护进程,子进程中一个线程负责检测文件是否发生变化,如果发生变化则退出,主进程检查子进程的退出码(exist code)如果与约定的退出码一致,则重新启动一个子进程继续工作。
  自动重新加载模块代码如下:


DSC0000.gif DSC0001.gif


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""This module is used to test how to reload the modules automatically when any
changes is detected.
"""
__author__="Wenjun Xiao"
import os,sys,time,subprocess,thread
def iter_module_files():
for module in sys.modules.values():
filename = getattr(module, '__file__', None)
if filename:
if filename[-4:] in ('.pyo', '.pyc'):
filename = filename[:-1]
yield filename
def is_any_file_changed(mtimes):
for filename in iter_module_files():
try:
mtime = os.stat(filename).st_mtime
except IOError:
continue
old_time = mtimes.get(filename, None)
if old_time is None:
mtimes[filename] = mtime
elif mtime > old_time:
return 1
return 0
def start_change_detector():
mtimes = {}
while 1:
if is_any_file_changed(mtimes):
sys.exit(3)
time.sleep(1)
def restart_with_reloader():
while 1:
args = [sys.executable] + sys.argv
new_env = os.environ.copy()
new_env['RUN_FLAG'] = 'true'
exit_code = subprocess.call(args, env=new_env)
if exit_code != 3:
return exit_code
def run_with_reloader(runner):
if os.environ.get('RUN_FLAG') == 'true':
thread.start_new_thread(runner, ())
try:
start_change_detector()
except KeyboardInterrupt:
pass
else:
try:
sys.exit(restart_with_reloader())
except KeyboardInterrupt:
pass
autoreload.py  测试的主模块如下:





#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Runner for testing autoreload module."""
__author__="Wenjun Xiao"
import os,time
def runner():
print "[%s]enter..." % os.getpid()
while 1:
time.sleep(1)
print "[%s]runner." % os.getpid()
if __name__ == '__main__':
from autoreload import run_with_reloader
run_with_reloader(runner)
runner.py  运行runner.py:



promissing@ubuntu:python-autoreload$ python runner.py
[11743]enter...
  主程序已经运行,只不过是一致在循环,可以查看此时有两个进程:



promissing@ubuntu:~$ ps -aux|grep runner[.py]
promiss+ 11742  0.0  0.2  10928  4208 pts/0    S+   19:34   0:00 python runner.py
promiss+ 11743  0.0  0.1  20152  4092 pts/0    Sl+  19:34   0:00 /usr/bin/python runner.py
  在编辑器中打开runner.py做一些可见的修改(增加一条打印语句)如下:



# runner.py
...
def runner():
print "[%s]enter..." % os.getpid()
print "[%s]Runner has changed." % os.getpid()
while 1:
time.sleep(1)
print "[%s]runner." % os.getpid()
...
  保存之后查看运行运行情况:



promissing@ubuntu:python-autoreload$ python runner.py
[11743]enter...
[11772]enter...
[11772]Runner has changed.
  可以看到新增的语句已经生效,继续看进程情况:



promissing@ubuntu:~$ ps -aux|grep runner[.py]
promiss+ 11742  0.0  0.2  10928  4220 pts/0    S+   19:34   0:00 python runner.py
promiss+ 11772  0.0  0.1  20152  4092 pts/0    Sl+  19:37   0:00 /usr/bin/python runner.py
  可以对比两次的进程,可以看到使用守护进程模式可以简单的实现模块自动重新加载功能。
  使用守护进程模式,有一种情况比较麻烦:如果主进程由于其他原因退出了,那么子进程还在运行:



promissing@ubuntu:~$ kill 11742
promissing@ubuntu:~$ ps -aux|grep runner[.py]
promiss+ 11772  0.0  0.1  20152  4092 pts/0    Sl   19:37   0:00 /usr/bin/python runner.py
  为了重启服务还需要通过其他方式找到子进程并结束它可以。

守护进程模式-退出问题
    为了解决由于守护进程退出,而导致子进程没有退出的问题,一种比较简单的解决方法就是在守护进程退出的时候也把子进程结束:



# autoreload.py
...
import signal
...
_sub_proc = None
def signal_handler(*args):
global _sub_proc
if _sub_proc:
print "[%s]Stop subprocess:%s" % (os.getpid(), _sub_proc.pid)
_sub_proc.terminate()
sys.exit(0)
def restart_with_reloader():
signal.signal(signal.SIGTERM, signal_handler)
    while 1:
args = [sys.executable] + sys.argv
new_env = os.environ.copy()
new_env['RUN_FLAG'] = 'true'
global _sub_proc
_sub_proc = subprocess.Popen(args, env=new_env)
exit_code = _sub_proc.wait()
        if exit_code != 3:
return exit_code
...
  运行,查看效果(这次没有测试修改):



promissing@ubuntu:python-autoreload$ python runner.py
[12425]enter...
[12425]Runner has changed.
[12424]Stop subprocess:12425
  另一个控制台执行的命令如下:



promissing@ubuntu:~$ ps -aux|grep runner[.py]
promiss+ 12424  0.2  0.2  10928  4224 pts/0    S+   20:26   0:00 python runner.py
promiss+ 12425  0.2  0.1  20152  4092 pts/0    Sl+  20:26   0:00 /usr/bin/python runner.py
promissing@ubuntu:~$ kill 12424
promissing@ubuntu:~$ ps -aux|grep runner[.py]
promissing@ubuntu:~$
  已经达到我们需要的功能了吗?等等,在控制台上运行工程总是能很好的工作,如果是在IDE中呢?由于IDE中输入输出是重定向处理的,比如,在Sublime中就没有办法获取到输出信息。
  因此还需要进一步完善输出的问题。

守护进程模式-输出问题
  解决输出问题,也很简单,修改如下:



# autoreload.py
...
def restart_with_reloader():
signal.signal(signal.SIGTERM, signal_handler)
while 1:
args = [sys.executable] + sys.argv
new_env = os.environ.copy()
new_env['RUN_FLAG'] = 'true'
global _sub_proc
_sub_proc = subprocess.Popen(args, env=new_env, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
read_stdout(_sub_proc.stdout)
exit_code = _sub_proc.wait()
if exit_code != 3:
return exit_code
...
def read_stdout(stdout):
while 1:
data = os.read(stdout.fileno(), 2**15)
if len(data) > 0:
sys.stdout.write(data)
else:
stdout.close()
sys.stdout.flush()
break
  经过以上修改,也适合在IDE中使用守护进程模式了。
  源代码:https://github.com/wenjunxiao/python-autoreload
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-59488-1-1.html 上篇帖子: python None与Null 下篇帖子: arcgis与python(zz)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表