python加载模块原理-Python自动重新加载模块详解(autoreload module)

守护进程模式

使用python开发后台服务程序的时候,每次修改代码之后都需要重启服务才能生效比较麻烦。

看了一下Python开源的Web框架(Django、Flask等)都有自己的自动加载模块功能(autoreload.py),都是通过subprocess模式创建子进程,主进程作为守护进程,子进程中一个线程负责检测文件是否发生变化,如果发生变化则退出,主进程检查子进程的退出码(exist code)如果与约定的退出码一致,则重新启动一个子进程继续工作。

自动重新加载模块代码如下:

autoreload.py

#!/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

测试的主模块如下:

runner.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:

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做一些可见的修改(增加一条打印语句)如下:

```python

# 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```

可以对比两次的进程,可以看到使用守护进程模式可以简单的实现模块自动重新加载功能。

使用守护进程模式,有一种情况比较麻烦:如果主进程由于其他原因退出了,那么子进程还在运行:

```python

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中使用守护进程模式了。

以上这篇Python自动重新加载模块详解(autoreload module)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持python博客。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值