Python自动重新加载模块详解(autoreload module)

作者:promissing 时间:2023-09-19 04:36:12 

守护进程模式

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


# 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

来源:https://www.cnblogs.com/wenjunxiao/p/4093377.html

标签:Python,重新加载,autoreload,module
0
投稿

猜你喜欢

  • 详解Python进行数据相关性分析的三种方式

    2022-05-09 18:50:12
  • Python超简单容易上手的画图工具库(适合新手)

    2021-12-06 04:05:23
  • python实现小世界网络生成

    2022-07-22 07:54:46
  • Selenium chrome配置代理Python版的方法

    2022-02-24 14:26:08
  • Python+Pillow进行图形处理的示例详解

    2021-08-03 22:42:34
  • Python调用SQLPlus来操作和解析Oracle数据库的方法

    2024-01-27 19:17:06
  • Python win32com 操作Exce的l简单方法(必看)

    2022-12-04 10:20:36
  • Python爬虫爬取属于自己的地铁线路图

    2021-09-10 11:46:23
  • python数据挖掘使用Evidently创建机器学习模型仪表板

    2022-10-23 14:21:52
  • python中count函数知识点浅析

    2023-05-21 18:41:30
  • Pytorch中的Tensorboard与Transforms搭配使用

    2023-07-09 08:27:05
  • 使用sqlplus连接Oracle数据库问题

    2024-01-13 09:25:16
  • QT连接Oracle数据库并实现登录验证的操作步骤

    2024-01-27 13:06:44
  • mysql判断当前时间是否在开始与结束时间之间且开始与结束时间允许为空

    2024-01-23 17:35:43
  • python实现朴素贝叶斯分类器

    2022-12-05 03:35:50
  • Python爬虫自动化获取华图和粉笔网站的错题(推荐)

    2023-08-14 02:05:42
  • 使用python实现希尔、计数、基数基础排序的代码

    2023-07-12 09:02:24
  • asp通过数组给您的文件列表排序

    2007-10-22 13:45:00
  • 基于go手动写个转发代理服务的代码实现

    2024-04-25 15:11:15
  • Python logging简介详解

    2022-05-12 09:27:02
  • asp之家 网络编程 m.aspxhome.com