xreload.py (7439B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\codereload\xreload.py 2 import collections 3 import gc 4 import imp 5 import logging 6 import marshal 7 import sys 8 import types 9 import weakref 10 log = logging.getLogger(__name__) 11 12 def _expressyourself(obj): 13 try: 14 return obj.__class__ 15 except AttributeError: 16 return type(obj) 17 18 19 def _safestr(obj): 20 try: 21 return str(obj) 22 except Exception: 23 pass 24 25 try: 26 return repr(obj) 27 except Exception as ex: 28 expressedobj = _expressyourself(obj) 29 log.warn('Error converting to str. Type: %s Error: %r', expressedobj, ex) 30 return '<REPR ERROR> (%s)' % expressedobj 31 32 33 def xreload(mod, code = None): 34 modns = mod.__dict__ 35 if not code: 36 modname = mod.__name__ 37 i = modname.rfind('.') 38 if i >= 0: 39 pkgname, modname = modname[:i], modname[i + 1:] 40 else: 41 pkgname = None 42 if pkgname: 43 pkg = sys.modules[pkgname] 44 path = pkg.__path__ 45 else: 46 pkg = None 47 path = None 48 stream, filename, (suffix, mode, kind) = imp.find_module(modname, path) 49 try: 50 if kind not in (imp.PY_COMPILED, imp.PY_SOURCE): 51 return reload(mod) 52 if kind == imp.PY_SOURCE: 53 source = stream.read() + '\n' 54 code = compile(source, filename, 'exec') 55 else: 56 code = marshal.load(stream) 57 finally: 58 if stream: 59 stream.close() 60 61 tmpns = modns.copy() 62 log.debug('Moving over the following objects: %s', tmpns.keys()) 63 modns.clear() 64 preload = ['__name__', 65 '__file__', 66 '__path__', 67 '__package__', 68 '__loader__', 69 '__doc__'] 70 for name in preload: 71 if name in tmpns: 72 modns[name] = tmpns[name] 73 74 try: 75 exec code in modns 76 except Exception: 77 log.exception('Failed to reload module') 78 for name, value in tmpns.iteritems(): 79 modns[name] = value 80 81 return mod 82 83 for name, ob in tmpns.items(): 84 if isinstance(ob, types.ModuleType): 85 modns[name] = ob 86 87 oldnames = set(tmpns) 88 newnames = set(modns) 89 for name in oldnames & newnames: 90 log.debug('Assigning over %s: %s', name, _safestr(modns[name])[:160]) 91 modns[name] = _update(tmpns[name], modns[name]) 92 93 log.debug('Module already had: %s', oldnames - newnames) 94 log.debug('Module has now: %s', mod.__dict__.keys()) 95 process_deferred_reference_updates() 96 if hasattr(mod, '__reload_update__'): 97 mod.__reload_update__(tmpns) 98 return mod 99 100 101 def _update(oldobj, newobj): 102 if oldobj is newobj: 103 return newobj 104 if type(oldobj) is not type(newobj): 105 return newobj 106 if hasattr(newobj, '__reload_update__'): 107 return newobj.__reload_update__(oldobj) 108 if isinstance(newobj, types.ClassType): 109 return _update_class(oldobj, newobj) 110 if isinstance(newobj, types.TypeType): 111 return _update_class(oldobj, newobj) 112 if isinstance(newobj, types.FunctionType): 113 return _update_function(oldobj, newobj) 114 if isinstance(newobj, types.MethodType): 115 return _update_method(oldobj, newobj) 116 if isinstance(newobj, classmethod): 117 return _update_classmethod(oldobj, newobj) 118 if isinstance(newobj, staticmethod): 119 return _update_staticmethod(oldobj, newobj) 120 return newobj 121 122 123 def _update_function(oldfunc, newfunc): 124 oldfunc.__doc__ = newfunc.__doc__ 125 oldfunc.__dict__.update(newfunc.__dict__) 126 oldfunc.__defaults__ = newfunc.__defaults__ 127 if oldfunc.__closure__ != newfunc.__closure__: 128 update_global_references(oldfunc, newfunc) 129 return newfunc 130 if oldfunc.__code__ != newfunc.__code__: 131 try: 132 oldfunc.__code__ = newfunc.__code__ 133 except ValueError: 134 update_global_references(oldfunc, newfunc) 135 return newfunc 136 137 update_global_references(newfunc, oldfunc) 138 return oldfunc 139 140 141 def _update_method(oldmeth, newmeth): 142 _update(oldmeth.__func__, newmeth.__func__) 143 return oldmeth 144 145 146 def _update_class(oldclass, newclass): 147 olddict = oldclass.__dict__ 148 newdict = newclass.__dict__ 149 oldnames = set(olddict) 150 newnames = set(newdict) 151 modified_bases = [] 152 for new_base in newclass.__bases__: 153 for old_base in oldclass.__bases__: 154 if old_base is new_base or old_base.__name__ == new_base.__name__: 155 modified_bases.append(old_base) 156 break 157 else: 158 modified_bases.append(new_base) 159 160 oldclass.__bases__ = tuple(modified_bases) 161 restricted_names = set(['__dict__', '__doc__']) 162 for name in newnames - oldnames - restricted_names: 163 setattr(oldclass, name, newdict[name]) 164 165 for name in oldnames - newnames - restricted_names: 166 delattr(oldclass, name) 167 168 for name in oldnames & newnames - restricted_names: 169 try: 170 setattr(oldclass, name, _update(olddict[name], newdict[name])) 171 except AttributeError: 172 log.exception('Ignoring exception') 173 174 deferred_update_global_references(newclass, oldclass) 175 return oldclass 176 177 178 def _update_classmethod(oldcm, newcm): 179 _update(oldcm.__get__(0), newcm.__get__(0)) 180 return oldcm 181 182 183 def _update_staticmethod(oldsm, newsm): 184 _update(oldsm.__get__(0), newsm.__get__(0)) 185 return oldsm 186 187 188 _deferred_reference_updates = {} 189 190 def deferred_update_global_references(oldobj, newobj): 191 global _deferred_reference_updates 192 _deferred_reference_updates[oldobj] = newobj 193 194 195 def process_deferred_reference_updates(): 196 while _deferred_reference_updates: 197 oldobj, newobj = _deferred_reference_updates.popitem() 198 update_global_references(oldobj, newobj, ignore=_deferred_reference_updates) 199 200 201 def update_global_references(oldobj, newobj, ignore = None): 202 _update_weak_references(oldobj, newobj) 203 _update_references(oldobj, newobj, ignore=ignore) 204 205 206 def _update_references(oldobj, newobj, ignore = None): 207 referrers = gc.get_referrers(oldobj) 208 for referrer in referrers: 209 try: 210 if ignore and referrer in ignore: 211 continue 212 except TypeError: 213 continue 214 215 if isinstance(referrer, collections.MutableMapping): 216 for k, v in referrer.items(): 217 if v is oldobj: 218 referrer[k] = newobj 219 220 elif isinstance(referrer, collections.MutableSequence): 221 for i in xrange(len(referrer)): 222 if referrer[i] is oldobj: 223 referrer[i] = newobj 224 225 elif isinstance(referrer, collections.MutableSet): 226 referrer.remove(oldobj) 227 referrer.add(newobj) 228 elif isinstance(referrer, tuple): 229 new_tuple = tuple(((x if x is not oldobj else newobj) for x in referrer)) 230 _update_references(referrer, new_tuple, ignore=[referrers]) 231 _update_weak_references(referrer, new_tuple) 232 233 234 def _update_weak_references(old_obj, new_obj): 235 weak_refs = list(weakref.getweakrefs(old_obj)) 236 if len(weak_refs) == 0: 237 return 238 old_ref = weak_refs.pop() 239 while len(weak_refs) > 0: 240 new_ref = weakref.ref(new_obj) 241 _update_references(old_ref, new_ref, ignore=[weak_refs]) 242 try: 243 old_ref = weak_refs.pop() 244 except Exception: 245 pass