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 )

时间: 2024-10-08 16:08:21

hotfix的相关文章

Android热修复:Andfix和Hotfix,两种方案的比较与实现

Andfix和hotfix是两种android热修复框架. android的热修复技术我看的最早的应该是QQ空间团队的解决方案,后来真正需要了,才仔细调查,现在的方案中,阿里有两种Dexposed和Andfix框架,由于前一种不支持5.0以上android系统,所以阿里系的方案我们就看Andfix就好.Hotfix框架算是对上文提到的QQ空间团队理论实现.本文旨在写实现方案,捎带原理. Andfix 引入 框架官网:https://github.com/alibaba/AndFix 介绍是用英文

XenDesktop XenApp 7.6 建议hotfix - 2015年7月

VDA性能及稳定性增强,强烈建议目前所有项目中使用7.6并安装此hotfix: Hotfix ICATS760WX64032 - For VDA Core Services7.6 for Windows Server OS (64-bit) - English http://support.citrix.com/article/CTX142640 Hotfix ICATS760WX64006 - For VDA Core Services7.6 for Windows Server OS (64

Android热修复学习之旅——HotFix完全解析

在上一篇博客Android热修复学习之旅开篇--热修复概述中,简单介绍了各个热修复框架的原理,本篇博客我将详细分析QQ空间热修复方案. Android dex分包原理介绍 QQ空间热修复方案基于Android dex分包基础之上,简单概述android dex分包的原理就是:就是把多个dex文件塞入到app的classloader之中,但是android dex拆包方案中的类是没有重复的,如果classes.dex和classes1.dex中有重复的类,当classes.dex和classes1

解决之前上架的 App 在 iOS 9 会闪退问题 (更新:已有 Hotfix)

解决之前上架的 App 在 iOS 9 会闪退问题 (更新:已有 Hotfix) 最新更新:(2015.10.02) 开发环境: Delphi 10 Seattle OS X El Capitan v10.11 需使用下列 HotfixID: 30398, PAServer Hotfix for Delphi, C++Builder and RAD Studio 10 Seattle Xcode v7.0.1 iOS SDK v9.0 真机测试(以下机种皆不闪退): iPhone 3GS v6.

Android 的 hotfix 库 Amigo

Amigo,是一个Android 平台的hotfix库,由饿了么公司开源. 用法 在project 的build.gradle 中 dependencies {     classpath 'me.ele:amigo:0.0.5'   } 在module 的build.gradle 中 apply plugin: 'me.ele.amigo' 就这样轻松的集成了Amigo. 生效补丁包 补丁包生效有两种方式可以选择: 稍后生效补丁包 如果不想立即生效而是用户第二次打开App 时才打入补丁包(入门

Android热修复框架HotFix分析拓展

本文有两个目的:   1.是分析一下hotfix的实现,   2.换一种实现防止类打上CLASS_ISPREVERIFIED标识 首先分析一下hitfix的实现: hotfix工程结构   1.app是demo的工程 2.builSrc是操作class文件的封装类,做的操作是向class的构造函数中插入了这么一段代码   编辑class文件使用的工具是javassist. 3.hackdex工程是防止类呗打上impressive标识的类. 4.hotfixlib是动态加载工具的封装 动态修复有两

xlua中hotfix简单实用

tolua每次修改C#代码,tolua都需要生成代码,xlua无需生成,但是在最后实际发布时,xlua需要生成代码 这章主要是写一下hotfix实用 这个特性默认是关闭的,实用时需要在UNITY中添加HOTFIX_ENABLE宏,打开步骤(在Unity3D的File->Build Setting->Scripting Define Symbols下添加) 这个热补丁还依赖Cecil,添加HOTFIX_ENABLE宏之后,可能会报找不到Cecil.这时你需要到Unity安装目录下找到Mono.C

更新日期 2015年8月23日 - Citrix桌面虚拟化平台交付推荐版本及相关hotfix

更新日志: 2015年8月19日    增加NVIDIA最新vGPU for XenServer下载地址 2015年8月21日    增加Windows Server 2008R2 推荐安装补丁 2015年8月23日    更新XenServer 6.5 hotfix ++++++++++ 为了方便大家在使用Citrix产品时选择合适的版本及相应的hotfix补丁,特将相关内容整理如下,并不定期更新. 以下所有内容为个人经验分享,不代表任何Citrix官方建议. 目前Citrix桌面虚拟化平台中

用户说体验 | 关于阿里百川HotFix你需要了解的一些细节

最近很火的热修复技术,无意中了解到阿里百川也在做,而且Android.iOS两端都支持,所以决定试一试.试用一段时间后,感觉还不错,主要是他们有一个团队在不断维护更新这个产品,可以看到他们的版本更新记录.基本每月都有更新,从修复方法到新增类,问了客服据说后面还会把当前已有的限制全部去掉,要是真能实现这些还挺令人激动的.下面说说我接入使用的一些心得体会吧. 开发者通过淘宝账号注册即可成为阿里百川的用户,但是如果要使用阿里百川HotFix还需要申请开通,大概可以分下面几个步骤: 1.   注册百川

iOS 中的 HotFix 方案总结详解

相信HotFix大家应该都很熟悉了,今天主要对于最近调研的一些方案做一些总结.iOS中的HotFix方案大致可以分为四种: WaxPatch(Alibaba) Dynamic Framework(Apple) React Native(Facebook) JSPatch(Tencent) WaxPatch WaxPatch是一个通过Lua语言编写的iOS框架,不仅允许用户使用 Lua 调用 iOS SDK和应用程序内部的 API, 而且使用了 OC runtime 特性调用替换应用程序内部由 O