imp.reload可以重载模块,但是关键问题是已经实例化过的类是不会变的
比如:
- class AA:
- def testaa():
- DEBUG_MSG("test")
- self.a=AA()
复制代码
使用imp.reload热更新后a还是不会变,需要重新把self.a.__class__=AA才能完成热更新
这样非常得不方便
一种可行的做法就是实例化类的时候把类都存到一个地方,然后热更新的时候全部把它们的__class__修改一下就行了
一开始用的装饰器的方式,但是有一些局限性,还是用继承的方式
- g_allReloadObjectList=[]
- class Reload:
- __reload__=True
- def __init__(self):
- g_allReloadObjectList.append(weakref.ref(self))
复制代码
创建类的时候就像:
- class AA(Reload):
- def __init__(self):
- Reload.__init__(self)
复制代码
就可以在实例化类的时候把实例存到g_allReloadObjectList里面
下面的代码写得比较垃圾,但是简单易懂:
reloadAllModule的功能是遍历base cell common user_type几个文件夹的所有文件,然后全部重载,感觉有点暴力,可以优化成只加载修改过的文件
同时把文件里需要热更新的类存到g_allClasses里
然后把g_allReloadObjectList里存的那些的实例的__class__全部改成g_allClasses里的
reloadSystem.py
- import inspect
- import os
- import importlib
- import imp
- import copy
- import weakref
- import KBEngine
- from KBEDebug import *
- if 0:
- from baseapp import KBEngine
- if 0:
- from cellapp import KBEngine
- #需要把此文件加入热更新黑名单
- _blackList=["reloadSystem"]
- g_allClasses={}
- g_allReloadObjectList=[]
- class Reload:
- __reload__=True
- def __init__(self):
- g_allReloadObjectList.append(weakref.ref(self))
- def reloadAllModule():
- global g_allClasses
- g_allClasses={}
- #base cell common user_type文件夹内的所有py全部重载
- if KBEngine.component=="baseapp":
- sb=KBEngine.getResFullPath("base/kbemain.py")
- sb=sb[:-10]
- ll=len(sb)
- for s in KBEngine.listPathRes("base","py"):
- s=s[ll:-3]
- s=s.replace('/','.')
- mo=importlib.import_module(s)
- _reloadModule(mo)
- elif KBEngine.component=="cellapp":
- sb=KBEngine.getResFullPath("cell/kbemain.py")
- sb=sb[:-10]
- ll=len(sb)
- for s in KBEngine.listPathRes("cell","py"):
- s=s[ll:-3]
- s=s.replace('/','.')
- mo=importlib.import_module(s)
- _reloadModule(mo)
- #reload common
- sb=KBEngine.getResFullPath("common/cfunc.py")
- sb=sb[:-8]
- ll=len(sb)
- for s in KBEngine.listPathRes("common","py"):
- s=s[ll:-3]
- s=s.replace('/','.')
- mo=importlib.import_module(s)
- _reloadModule(mo)
- #reload userType
- sb=KBEngine.getResFullPath("user_type/UserType.py")
- sb=sb[:-11]
- ll=len(sb)
- for s in KBEngine.listPathRes("user_type","py"):
- s=s[ll:-3]
- s=s.replace('/','.')
- mo=importlib.import_module(s)
- _reloadModule(mo)
- _reloadAllObjects()
- """
- 重载模块,然后把模块里面的需要重载的类全部加
- """
- def _reloadModule(mo):
- if mo.__name__ in _blackList:
- return
- imp.reload(mo)
- for name,Cls in inspect.getmembers(mo):
- if getattr(Cls,'__reload__',False):
- path=inspect.getsourcefile(Cls)
- g_allClasses[path+Cls.__name__]=Cls
- def _reloadAllObjects():
- delList=[]
- for ref in g_allReloadObjectList:
- obj=ref()
- if obj:
- n=inspect.getsourcefile(obj.__class__)+obj.__class__.__name__
- if n in g_allClasses:
- obj.__class__=g_allClasses[n]
- else:
- WARNING_MSG("not find Class "+n)
- else:
- delList.append(ref)
- for ref in delList:
- g_allReloadObjectList.remove(ref)
复制代码
这样就实现了即时切换__class__
并且可以用Tornado做一个类来接受socket命令然后调用globalData通知全部进程热更新
然后在ide里的externalTools里做个发送socket的小工具,并且设置快捷键
然后只要写了点代码后按一下快捷键,服务端所有进程瞬间更新到最新的代码,生活质量提升了不止一个档次
如果谁有更好的热更新方案欢迎补充