1 # -*- test-case-name: twisted.test.test_rebuild -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
7 *Real* reloading support for Python.
17 from twisted.python import log, reflect
19 lastRebuild = time.time()
24 A utility mixin that's sensitive to rebuilds.
26 This is a mixin for classes (usually those which represent collections of
27 callbacks) to make sure that their code is up-to-date before running.
30 lastRebuild = lastRebuild
32 def needRebuildUpdate(self):
33 yn = (self.lastRebuild < lastRebuild)
36 def rebuildUpToDate(self):
37 self.lastRebuild = time.time()
39 def latestVersionOf(self, anObject):
41 Get the latest version of an object.
43 This can handle just about anything callable; instances, functions,
47 if t == types.FunctionType:
48 return latestFunction(anObject)
49 elif t == types.MethodType:
50 if anObject.im_self is None:
51 return getattr(anObject.im_class, anObject.__name__)
53 return getattr(anObject.im_self, anObject.__name__)
54 elif t == types.InstanceType:
55 # Kick it, if it's out of date.
56 getattr(anObject, 'nothing', None)
58 elif t == types.ClassType:
59 return latestClass(anObject)
61 log.msg('warning returning anObject!')
66 def latestFunction(oldFunc):
68 Get the latest version of a function.
70 # This may be CPython specific, since I believe jython instantiates a new
72 dictID = id(oldFunc.func_globals)
73 module = _modDictIDMap.get(dictID)
76 return getattr(module, oldFunc.__name__)
79 def latestClass(oldClass):
81 Get the latest version of a class.
83 module = reflect.namedModule(oldClass.__module__)
84 newClass = getattr(module, oldClass.__name__)
85 newBases = [latestClass(base) for base in newClass.__bases__]
88 # This makes old-style stuff work
89 newClass.__bases__ = tuple(newBases)
92 if newClass.__module__ == "__builtin__":
93 # __builtin__ members can't be reloaded sanely
95 ctor = getattr(newClass, '__metaclass__', type)
96 return ctor(newClass.__name__, tuple(newBases), dict(newClass.__dict__))
99 class RebuildError(Exception):
101 Exception raised when trying to rebuild a class whereas it's not possible.
105 def updateInstance(self):
107 Updates an instance to be current.
110 self.__class__ = latestClass(self.__class__)
112 if hasattr(self.__class__, '__slots__'):
113 raise RebuildError("Can't rebuild class with __slots__ on Python < 2.6")
118 def __getattr__(self, name):
120 A getattr method to cause a class to be refreshed.
122 if name == '__del__':
123 raise AttributeError("Without this, Python segfaults.")
125 log.msg("(rebuilding stale %s instance (%s))" % (reflect.qual(self.__class__), name))
126 result = getattr(self, name)
130 def rebuild(module, doLog=1):
132 Reload a module and do as much as possible to replace its references.
135 lastRebuild = time.time()
136 if hasattr(module, 'ALLOW_TWISTED_REBUILD'):
137 # Is this module allowed to be rebuilt?
138 if not module.ALLOW_TWISTED_REBUILD:
139 raise RuntimeError("I am not allowed to be rebuilt.")
141 log.msg('Rebuilding %s...' % str(module.__name__))
143 ## Safely handle adapter re-registration
144 from twisted.python import components
145 components.ALLOW_DUPLICATES = True
148 _modDictIDMap[id(d)] = module
154 log.msg(' (scanning %s): ' % str(module.__name__))
155 for k, v in d.items():
156 if type(v) == types.ClassType:
157 # Failure condition -- instances of classes with buggy
158 # __hash__/__cmp__ methods referenced at the module level...
159 if v.__module__ == module.__name__:
162 log.logfile.write("c")
164 elif type(v) == types.FunctionType:
165 if v.func_globals is module.__dict__:
168 log.logfile.write("f")
170 elif isinstance(v, type):
171 if v.__module__ == module.__name__:
174 log.logfile.write("o")
177 values.update(classes)
178 values.update(functions)
179 fromOldModule = values.has_key
180 newclasses = newclasses.keys()
181 classes = classes.keys()
182 functions = functions.keys()
186 log.msg(' (reload %s)' % str(module.__name__))
190 # Make sure that my traceback printing will at least be recent...
191 linecache.clearcache()
194 log.msg(' (cleaning %s): ' % str(module.__name__))
196 for clazz in classes:
197 if getattr(module, clazz.__name__) is clazz:
198 log.msg("WARNING: class %s not replaced by reload!" % reflect.qual(clazz))
201 log.logfile.write("x")
204 clazz.__dict__.clear()
205 clazz.__getattr__ = __getattr__
206 clazz.__module__ = module.__name__
209 for nclass in newclasses:
210 ga = getattr(module, nclass.__name__)
212 log.msg("WARNING: new-class %s not replaced by reload!" % reflect.qual(nclass))
214 for r in gc.get_referrers(nclass):
215 if getattr(r, '__class__', None) is nclass:
219 log.msg(' (fixing %s): ' % str(module.__name__))
221 for mk, mod in sys.modules.items():
222 modcount = modcount + 1
223 if mod == module or mod is None:
226 if not hasattr(mod, '__file__'):
227 # It's a builtin module; nothing to replace here.
230 if hasattr(mod, '__bundle__'):
231 # PyObjC has a few buggy objects which segfault if you hash() them.
232 # It doesn't make sense to try rebuilding extension modules like
233 # this anyway, so don't try.
238 for k, v in mod.__dict__.items():
244 if type(v) == types.ClassType:
246 log.logfile.write("c")
251 log.logfile.write("f")
253 nv = latestFunction(v)
257 # Replace bases of non-module classes just to be sure.
258 if type(v) == types.ClassType:
259 for base in v.__bases__:
260 if fromOldModule(base):
262 if doLog and not changed and ((modcount % 10) ==0) :
263 log.logfile.write(".")
266 components.ALLOW_DUPLICATES = False
269 log.msg(' Rebuilt %s.' % str(module.__name__))