首页 > 代码库 > hotfix

hotfix

#-*- encoding: utf-8 -*- importer

import sys
import imp

class finder( object ):
def setup_old_module( self, name ):
old_module = self.get_old_module( name )
if not old_module:
return

old_module.__dict__.pop( ‘_reload_all‘, None )
# 看起来load_module只是改变了module.__dict__并没有创建新的module
sys.modules[ name ] = old_module # for imp.load_module to reload the module

def modify_module( self, name, module ):
modify_module = getattr( sys, ‘modify_module‘, None )
if not modify_module:
return module

return modify_module( name, module )

def get_old_module( self, name ):
get_old_module = getattr( sys, ‘get_old_module‘, None )
if not get_old_module:
return

return get_old_module( name )

class builtin_finder( finder ):
def find_module( self, full_name, paths ):
try:
module = None
for name in full_name.split( ‘.‘ ):
module = self.get_module( name, module )

if module:
return loader( module )

return None
except ImportError:
old_module = self.get_old_module( full_name )
if old_module:
print ‘unchanged module‘, full_name
return loader( old_module )
else:
raise

def get_module( self, name, parent ):
parent_name = getattr( parent, ‘__name__‘, ‘‘ )
parent_path = getattr( parent, ‘__path__‘, None )

if parent_name:
full_name = parent_name + ‘.‘ + name
else:
full_name = name

module = sys.modules.get( full_name )
if module:
return module

return self.create_module( name, full_name, parent_path )

def create_module( self, name, full_name, parent_path ):
self.setup_old_module( full_name )

fd, path, des = imp.find_module( name, parent_path )
module = imp.load_module( full_name, fd, path, des )

self.modify_module( full_name, module )
#print ‘===‘, name, full_name, parent_path
return module

# to do: implement the source finder
class source_finder( finder ):
pass

class loader( object ):
def __init__( self, module ):
self.module = module

def load_module( self, name ):
return self.module

sys.meta_path = [ builtin_finder() ]

 

# -*- coding: utf-8 -*- reload_helper
import sys, os

def clear_old_modules():
from servercommon import Netease
print ‘clearOldModules %d‘ % len(Netease.oldModulesMap)

# 删除__builtins__以外的属性
for old_module in Netease.oldModulesMap.itervalues():
attr_list = dir(old_module)
for attr_name in attr_list:
if attr_name == ‘__builtins__‘:
continue
delattr(old_module, attr_name)

# 删除__builtins_
for old_module in Netease.oldModulesMap.itervalues():
attr_list = dir(old_module)
for attr_name in attr_list:
delattr(old_module, attr_name)

Netease.oldModulesMap = {}
print ‘clearOldModules finished‘

"""
we record the old modules in unload_modules function"
def mark_old_modules():
print ‘markOldModules‘
from servercommon import Netease
#clear_old_modules(component)

modules_map = {}

for name, v in sys.modules.iteritems():
if v and hasattr(v, ‘__file__‘) and (not v.__file__.endswith(‘.so‘)):
old_module = sys.modules[name]
modules_map[name] = old_module

Netease.oldModulesMap = modules_map
"""

def unmark_old_modules():
print ‘unMarkOldModules‘
from servercommon import Netease
Netease.oldModulesMap = {}

def _get_module_paths(module_dir):
module_path_set = set()
try:
files = os.listdir(module_dir)
except:
print "error in generate_module_list for directory:", module_dir
return set()
for file_name in files:
name_list = file_name.split(‘.‘)
if len(name_list) == 2:
module_name = name_list[0]
extension = name_list[1]
if extension in ("py", "pyc") and module_name != ‘__init__‘:
# 取绝对路径
module_path = os.path.realpath(os.path.join(module_dir, file_name))
# 这里用string.lower只是为了解决在win上的大小写问题, 实际上在linux下可以不需要
module_path_set.add(module_path.lower())
# module_path_set.discard(‘__init__‘)
module_path_set.add(os.path.join( module_dir, ‘__init__.py‘ ))
module_path_set.add(os.path.join( module_dir, ‘__init__.pyc‘ ))
return module_path_set

def unload_modules(module_dir):
"""reload指定目录下所有已经被import的模块"""
from servercommon import Netease

module_path_set= _get_module_paths(module_dir)
module_name_list = []
# 先删掉旧的模块
for module_name, module in sys.modules.items():
try:
if module == None or getattr(module, ‘__file__‘, None) == None:
continue
# 这里用string.lower只是为了解决在win上的大小写问题, 实际上在linux下可以不需要
module_path = os.path.realpath(module.__file__).lower()
if module_path in module_path_set:
print "deleting:", module_name
# mark the unloaded modules to reload
Netease.oldModulesMap[module_name] = sys.modules[module_name]
del sys.modules[module_name]
module_name_list.append(module_name)
except Exception, e:
print "error in reload_modules: ", e.message
import traceback
traceback.print_exc()
continue
return module_name_list

def reload_modules(module_dir):
module_name_list = unload_modules(module_dir)
module_list = []
# 重新import新的模块
for module_name in module_name_list:
try:
print "reloading:", module_name
new_module = __import__(module_name, fromlist = [‘‘])
module_list.append(new_module)
except Exception, e:
print "error in reload_modules: ", e.message
import traceback
traceback.print_exc()
continue
print "reload_modules result: ", module_list
return module_list

def reload_module_list(module_dir_list):
module_name_lists = []
for module_dir in module_dir_list:
module_name_lists.append(unload_modules(module_dir))
module_list = []
for module_name_list in module_name_lists:
_list = []
# 重新import新的模块
for module_name in module_name_list:
try:
print "reloading:", module_name
new_module = __import__(module_name, fromlist = [‘‘])
_list.append(new_module)
except Exception, e:
print "error in reload_modules: ", e.message
import traceback
traceback.print_exc()
continue
print "reload_modules result: ", _list
module_list.extend(_list)
return module_list

 

 

 

 

 

 

 

 

 

# -*- encoding:utf-8 -*- reloadmgr

import sys
import imp
import inspect

_origin_modules = {
‘reload_mgr‘, ‘__builtins__‘, ‘__builtin__‘, ‘sys‘, ‘site‘, ‘__main__‘, ‘exceptions‘, ‘pyexpat‘, ‘pyexpat.errors‘,
‘pyexpat.model‘, ‘select‘, ‘_socket‘, ‘importer‘, ‘sys‘, ‘imp‘, ‘inspect‘, ‘_multibytecodec‘, ‘_codecs_cn‘,
‘BigWorld‘, ‘C_ui‘, ‘ResMgr‘, ‘Sound‘, ‘guis.xnui‘, ‘guis.factory‘, ‘Crypto.Hash._MD4‘, ‘Crypto.Hash._SHA384‘,
‘_counter‘, ‘hotfix.importer‘, ‘hotfix.reload_mgr‘, ‘google.protobuf.pyext._message‘, ‘Timer‘, ‘world‘, ‘render‘,
‘cocosui‘, ‘math‘, ‘math3d‘, ‘game3d‘, ‘gc‘, ‘json‘, ‘rpyc‘, ‘google‘, ‘collision‘, ‘io‘, ‘_io‘, ‘os‘, ‘audio‘, ‘game‘,
}

_ignore_attrs = {
‘__module__‘, ‘_reload_all‘, ‘__dict__‘, ‘__weakref__‘, ‘__doc__‘,
}

_old_modules = None
_module_infos = {}

def start_reload( *names ):
global _old_modules
global _module_infos

print ‘********** start reload script ***************‘
_old_modules = dict( sys.modules )
store_module_infos()

sys.get_old_module = get_old_module
sys.modify_module = modify_module
sys.reloading = True

modules = get_reload_module()#把特定文件夹下、类型module的module得到
for name in modules: # pop the module to be reloaded
sys.modules.pop( name )

# print ‘==============‘, modules
for name in modules:
try:
new_module = __import__( name )
except ImportError:
sys.modules[ name ] = _old_modules[ name ]

# sys.get_old_module = None
# sys.modify_module = None
# sys.reloading = False

# _old_modules = None
# _module_infos = {}

print ‘********** reload script successed ***************‘

def name_is_valid(name):#sys.module的key名字都是以文件夹为起始比如entities.avatar
valid_prefix = [‘component‘, ‘scene‘, ‘ctrls‘, ‘data‘, ‘commoncs‘, ‘entity‘,
‘gui‘, ‘network‘, ‘physics‘,‘stubs‘,‘impavatar‘,‘core‘]

#print "wdy name",name
for prefix in valid_prefix:
if name.startswith(prefix):
return True

return False

 

def get_reload_module():
global _old_modules
global _module_infos
result = []
names = _old_modules.iterkeys()
for name in names:
if name_is_valid(name):
module = _old_modules.get( name )
if inspect.ismodule( module ):
result.append( name )

return result

def get_invalid_module( names ):
result = []
names = _old_modules.keys()
for name in names:
if name.startswith(‘ClientUnits‘):
module = _old_modules.get( name )
if inspect.ismodule( module ):
result.append( name )

return result


result = []
names = _old_modules.keys()

for name in names:
if name in _origin_modules:
continue

module = _old_modules.get( name )
if inspect.ismodule( module ):
result.append( name )

return result

def store_module_infos():
for name, module in _old_modules.iteritems():
if module:
# 只是将old_modules的__dict__内容存储了下来
# 重新import时,module的地址没换,内容换了后再更新
_module_infos[ name ] = dict( module.__dict__ )

def get_old_module( name ):
return _old_modules.get( name )

def modify_module( name, module ):
global _old_modules
global _module_infos
old_infos = _module_infos.get( name )
if not old_infos:
return module

update_module( old_infos, module )

return module

def update_module( old_attrs, module ):
global _old_modules
global _module_infos
# 将旧的module里的class中func,attr替换成新的module里对应class的func,attr
# 保证module,实际上是namespace中的所有成员地址不变, 因此原有的所有对module
# 内变量的引用都不会因reload而发生不能使用的情况
for name, attr in inspect.getmembers( module ):
if isinstance( attr, type ) and attr is not type:
old_class = old_attrs.get( name )
if old_class:
update_type( old_class, attr, getattr( attr, ‘_reload_all‘, False ) )
setattr( module, name, old_class )
elif inspect.isfunction( attr ): # ???_reload_all?????????Ч?????
old_fun = old_attrs.get( name )
if not old_fun:
old_attrs[ name ] = attr
elif inspect.isfunction( old_fun ):
if not update_fun( old_fun, attr ):
old_attrs[ name ] = attr
else:
setattr( module, name, old_fun )

if not getattr( module, ‘_reload_all‘, False ):
module.__dict__.update( old_attrs )

def update_fun( old_fun, new_fun, update_cell_depth = 2 ):
old_cell_num = 0
if old_fun.func_closure:
old_cell_num = len( old_fun.func_closure )
new_cell_num = 0
if new_fun.func_closure:
new_cell_num = len( new_fun.func_closure )

if old_cell_num != new_cell_num:
return False
#print "Wdy update_funupdate_funupdate_funupdate_fun"
setattr( old_fun, ‘func_code‘, new_fun.func_code )
setattr( old_fun, ‘func_defaults‘, new_fun.func_defaults )
setattr( old_fun, ‘func_doc‘, new_fun.func_doc )
setattr( old_fun, ‘func_dict‘, new_fun.func_dict )

if not ( update_cell_depth and old_cell_num ):
return True

for index, cell in enumerate( old_fun.func_closure ):
if inspect.isfunction( cell.cell_contents ):
update_fun( cell.cell_contents, new_fun.func_closure[index].cell_contents, update_cell_depth - 1 )

return True

def update_type( old_class, new_class, reload_all ):
for name, attr in old_class.__dict__.items(): # delete function
if name in new_class.__dict__:
continue

if not inspect.isfunction( attr ):
continue

type.__delattr__( old_class, name )

for name, attr in new_class.__dict__.iteritems():
if name not in old_class.__dict__: # new attribute
setattr( old_class, name, attr )
continue

old_attr = old_class.__dict__[ name ]
new_attr = attr

# if type( old_attr ) != type( new_attr ): # different types
# setattr( old_class, name, attr )
# continue

if inspect.isfunction( old_attr ) and inspect.isfunction( new_attr ):
if not update_fun( old_attr, new_attr ):
setattr( old_class, name, new_attr )
elif isinstance( new_attr, staticmethod ) or isinstance( new_attr, classmethod ):
if not update_fun( old_attr.__func__, new_attr.__func__ ):
old_attr.__func__ = new_attr.__func__
elif reload_all and name not in _ignore_attrs:
setattr( old_class, name, attr )

hotfix