相信很多使用python者都对reload方法比较熟悉了,通过不间断地reload可以实现某一module的热更新,主要就能在不重启应用的情况下实现部分模块的更新。但这种方法仅限于reload当前工作目录下的module,对于通过setuptools安装的egg包就不灵了,具体分析如下:
(1)python 的import或者reload都是根据sys.path来进行查找相关module的,找到第一个匹配的module之后就进行加载然后退出此操作
(2)sys.path是有一定的顺序的
(3)通过setuptools 的easy_install进行安装的package,每个版本都保留在site-packages中
(4)sys.path正常情况下只有在python进程启动的时候进行赋值
(5)通过easy_install进行安装后easy_install.pth中记录的已经是最新版的package,但如果此时没有重启python进程,则sys.path中记录的还是原有的package位置,这时reload的话首先找到的还是老版本的package,也就是说新版并未生效
怎么解决这一问题?怎么在不重启程序的情况下使用新版的package?
通过研究python代码可以知道,python在启动的时候会加载 site.py,site.py中有一个main()函数负责对sys.path进行赋值操作,为实现我们在不重启程序就能使用最新版package的目的,可以通过显式调用site.py中的main()函数来操作,具体代码如下:
# -*- coding: utf-8 -*- import sys import site import time import os.path import copy def reload_module(module_str): ''' reload module ''' paths_old = copy.deepcopy(sys.path) # invoke site.main to get updated sys.path site.main() paths_new = copy.deepcopy(sys.path) updated = [] # parse updated packages for item in paths_new[len(paths_old):]: _, egg_name = os.path.split(item) # egg_name like this : requests-1.1.0-py2.7.egg # parse real package name if len(egg_name) > 0: package_name = egg_name.split('-')[0] updated.append(package_name) # erase old version packages sys.path = [] for item in paths_new[:len(paths_old)]: _, egg_name = os.path.split(item) if len(egg_name) > 0: package_name = egg_name.split('-')[0] if package_name in updated: # a new version is already exists, so erase the old one pass else: sys.path.append(item) # append the updated sys.path.extend(updated) if sys.modules.has_key(module_str): print 'need reload' reload(sys.modules[module_str]) else: print 'need import' try: exec 'import %s' %module_str except Exception, e: print e def main(): import requests while True: time.sleep(2) reload_module('requests') print requests.__version__ if __name__ == '__main__': main()
如果在程序运行过程中安装了新版的requests,则程序运行结果如下所示:
C:UsersJerryKwanDesktop>python get_current_version.py
need reload
1.2.0
need reload
1.2.0
need reload
1.2.0
need reload
1.2.0
need reload
1.2.3
need reload
1.2.3
need reload
1.2.3