学以致用

focus on Python , C++, and some interest in Go and R

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

相信很多使用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:\Users\JerryKwan\Desktop>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

posted on 2014-01-06 14:39  Jerry.Kwan  阅读(594)  评论(0编辑  收藏  举报