You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rebuild.py 9.1KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. # -*- test-case-name: twisted.test.test_rebuild -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. *Real* reloading support for Python.
  6. """
  7. # System Imports
  8. import sys
  9. import types
  10. import time
  11. import linecache
  12. from imp import reload
  13. try:
  14. # Python 2
  15. from types import InstanceType
  16. except ImportError:
  17. # Python 3
  18. pass
  19. # Sibling Imports
  20. from twisted.python import log, reflect
  21. from twisted.python.compat import _PY3
  22. lastRebuild = time.time()
  23. def _isClassType(t):
  24. """
  25. Compare to types.ClassType in a py2/3-compatible way
  26. Python 2 used comparison to types.ClassType to check for old-style
  27. classes Python 3 has no concept of old-style classes, so if
  28. ClassType doesn't exist, it can't be an old-style class - return
  29. False in that case.
  30. Note that the type() of new-style classes is NOT ClassType, and
  31. so this should return False for new-style classes in python 2
  32. as well.
  33. """
  34. _ClassType = getattr(types, 'ClassType', None)
  35. if _ClassType is None:
  36. return False
  37. return t == _ClassType
  38. class Sensitive(object):
  39. """
  40. A utility mixin that's sensitive to rebuilds.
  41. This is a mixin for classes (usually those which represent collections of
  42. callbacks) to make sure that their code is up-to-date before running.
  43. """
  44. lastRebuild = lastRebuild
  45. def needRebuildUpdate(self):
  46. yn = (self.lastRebuild < lastRebuild)
  47. return yn
  48. def rebuildUpToDate(self):
  49. self.lastRebuild = time.time()
  50. def latestVersionOf(self, anObject):
  51. """
  52. Get the latest version of an object.
  53. This can handle just about anything callable; instances, functions,
  54. methods, and classes.
  55. """
  56. t = type(anObject)
  57. if t == types.FunctionType:
  58. return latestFunction(anObject)
  59. elif t == types.MethodType:
  60. if anObject.__self__ is None:
  61. return getattr(anObject.im_class, anObject.__name__)
  62. else:
  63. return getattr(anObject.__self__, anObject.__name__)
  64. elif not _PY3 and t == InstanceType:
  65. # Kick it, if it's out of date.
  66. getattr(anObject, 'nothing', None)
  67. return anObject
  68. elif _isClassType(t):
  69. return latestClass(anObject)
  70. else:
  71. log.msg('warning returning anObject!')
  72. return anObject
  73. _modDictIDMap = {}
  74. def latestFunction(oldFunc):
  75. """
  76. Get the latest version of a function.
  77. """
  78. # This may be CPython specific, since I believe jython instantiates a new
  79. # module upon reload.
  80. dictID = id(oldFunc.__globals__)
  81. module = _modDictIDMap.get(dictID)
  82. if module is None:
  83. return oldFunc
  84. return getattr(module, oldFunc.__name__)
  85. def latestClass(oldClass):
  86. """
  87. Get the latest version of a class.
  88. """
  89. module = reflect.namedModule(oldClass.__module__)
  90. newClass = getattr(module, oldClass.__name__)
  91. newBases = [latestClass(base) for base in newClass.__bases__]
  92. try:
  93. # This makes old-style stuff work
  94. newClass.__bases__ = tuple(newBases)
  95. return newClass
  96. except TypeError:
  97. if newClass.__module__ in ("__builtin__", "builtins"):
  98. # __builtin__ members can't be reloaded sanely
  99. return newClass
  100. ctor = type(newClass)
  101. # The value of type(newClass) is the metaclass
  102. # in both Python 2 and 3, except if it was old-style.
  103. if _isClassType(ctor):
  104. ctor = getattr(newClass, '__metaclass__', type)
  105. return ctor(newClass.__name__, tuple(newBases),
  106. dict(newClass.__dict__))
  107. class RebuildError(Exception):
  108. """
  109. Exception raised when trying to rebuild a class whereas it's not possible.
  110. """
  111. def updateInstance(self):
  112. """
  113. Updates an instance to be current.
  114. """
  115. self.__class__ = latestClass(self.__class__)
  116. def __injectedgetattr__(self, name):
  117. """
  118. A getattr method to cause a class to be refreshed.
  119. """
  120. if name == '__del__':
  121. raise AttributeError("Without this, Python segfaults.")
  122. updateInstance(self)
  123. log.msg("(rebuilding stale {} instance ({}))".format(
  124. reflect.qual(self.__class__), name))
  125. result = getattr(self, name)
  126. return result
  127. def rebuild(module, doLog=1):
  128. """
  129. Reload a module and do as much as possible to replace its references.
  130. """
  131. global lastRebuild
  132. lastRebuild = time.time()
  133. if hasattr(module, 'ALLOW_TWISTED_REBUILD'):
  134. # Is this module allowed to be rebuilt?
  135. if not module.ALLOW_TWISTED_REBUILD:
  136. raise RuntimeError("I am not allowed to be rebuilt.")
  137. if doLog:
  138. log.msg('Rebuilding {}...'.format(str(module.__name__)))
  139. # Safely handle adapter re-registration
  140. from twisted.python import components
  141. components.ALLOW_DUPLICATES = True
  142. d = module.__dict__
  143. _modDictIDMap[id(d)] = module
  144. newclasses = {}
  145. classes = {}
  146. functions = {}
  147. values = {}
  148. if doLog:
  149. log.msg(' (scanning {}): '.format(str(module.__name__)))
  150. for k, v in d.items():
  151. if _isClassType(type(v)):
  152. # ClassType exists on Python 2.x and earlier.
  153. # Failure condition -- instances of classes with buggy
  154. # __hash__/__cmp__ methods referenced at the module level...
  155. if v.__module__ == module.__name__:
  156. classes[v] = 1
  157. if doLog:
  158. log.logfile.write("c")
  159. log.logfile.flush()
  160. elif type(v) == types.FunctionType:
  161. if v.__globals__ is module.__dict__:
  162. functions[v] = 1
  163. if doLog:
  164. log.logfile.write("f")
  165. log.logfile.flush()
  166. elif isinstance(v, type):
  167. if v.__module__ == module.__name__:
  168. newclasses[v] = 1
  169. if doLog:
  170. log.logfile.write("o")
  171. log.logfile.flush()
  172. values.update(classes)
  173. values.update(functions)
  174. fromOldModule = values.__contains__
  175. newclasses = newclasses.keys()
  176. classes = classes.keys()
  177. functions = functions.keys()
  178. if doLog:
  179. log.msg('')
  180. log.msg(' (reload {})'.format(str(module.__name__)))
  181. # Boom.
  182. reload(module)
  183. # Make sure that my traceback printing will at least be recent...
  184. linecache.clearcache()
  185. if doLog:
  186. log.msg(' (cleaning {}): '.format(str(module.__name__)))
  187. for clazz in classes:
  188. if getattr(module, clazz.__name__) is clazz:
  189. log.msg("WARNING: class {} not replaced by reload!".format(
  190. reflect.qual(clazz)))
  191. else:
  192. if doLog:
  193. log.logfile.write("x")
  194. log.logfile.flush()
  195. clazz.__bases__ = ()
  196. clazz.__dict__.clear()
  197. clazz.__getattr__ = __injectedgetattr__
  198. clazz.__module__ = module.__name__
  199. if newclasses:
  200. import gc
  201. for nclass in newclasses:
  202. ga = getattr(module, nclass.__name__)
  203. if ga is nclass:
  204. log.msg("WARNING: new-class {} not replaced by reload!".format(
  205. reflect.qual(nclass)))
  206. else:
  207. for r in gc.get_referrers(nclass):
  208. if getattr(r, '__class__', None) is nclass:
  209. r.__class__ = ga
  210. if doLog:
  211. log.msg('')
  212. log.msg(' (fixing {}): '.format(str(module.__name__)))
  213. modcount = 0
  214. for mk, mod in sys.modules.items():
  215. modcount = modcount + 1
  216. if mod == module or mod is None:
  217. continue
  218. if not hasattr(mod, '__file__'):
  219. # It's a builtin module; nothing to replace here.
  220. continue
  221. if hasattr(mod, '__bundle__'):
  222. # PyObjC has a few buggy objects which segfault if you hash() them.
  223. # It doesn't make sense to try rebuilding extension modules like
  224. # this anyway, so don't try.
  225. continue
  226. changed = 0
  227. for k, v in mod.__dict__.items():
  228. try:
  229. hash(v)
  230. except Exception:
  231. continue
  232. if fromOldModule(v):
  233. if _isClassType(type(v)):
  234. if doLog:
  235. log.logfile.write("c")
  236. log.logfile.flush()
  237. nv = latestClass(v)
  238. else:
  239. if doLog:
  240. log.logfile.write("f")
  241. log.logfile.flush()
  242. nv = latestFunction(v)
  243. changed = 1
  244. setattr(mod, k, nv)
  245. else:
  246. # Replace bases of non-module classes just to be sure.
  247. if _isClassType(type(v)):
  248. for base in v.__bases__:
  249. if fromOldModule(base):
  250. latestClass(v)
  251. if doLog and not changed and ((modcount % 10) == 0) :
  252. log.logfile.write(".")
  253. log.logfile.flush()
  254. components.ALLOW_DUPLICATES = False
  255. if doLog:
  256. log.msg('')
  257. log.msg(' Rebuilt {}.'.format(str(module.__name__)))
  258. return module