xreload.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Alternative to reload().
  4. This works by executing the module in a scratch namespace, and then
  5. patching classes, methods and functions in place. This avoids the
  6. need to patch instances. New objects are copied into the target
  7. namespace.
  8. Some of the many limitiations include:
  9. - Global mutable objects other than classes are simply replaced, not patched
  10. - Code using metaclasses is not handled correctly
  11. - Code creating global singletons is not handled correctly
  12. - Functions and methods using decorators (other than classmethod and
  13. staticmethod) is not handled correctly
  14. - Renamings are not handled correctly
  15. - Dependent modules are not reloaded
  16. - When a dependent module contains 'from foo import bar', and
  17. reloading foo deletes foo.bar, the dependent module continues to use
  18. the old foo.bar object rather than failing
  19. - Frozen modules and modules loaded from zip files aren't handled
  20. correctly
  21. - Classes involving __slots__ are not handled correctly
  22. '''
  23. import imp
  24. import sys
  25. from marshal import load
  26. from inspect import getdoc, isclass, isfunction, ismethod
  27. from .deep import clsdict, selfname
  28. from .six import (
  29. method_func, func_code, _func_code, _func_defaults, func_defaults)
  30. def xreload(module):
  31. '''Reload `module` in place, updating classes, methods and functions.'''
  32. # Get the module name, e.g. 'foo.bar.whatever'
  33. modname = selfname(module)
  34. # Get the module namespace (dict) early; this is part of the type check
  35. modns = clsdict(module)
  36. # Parse it into package name and module name, e.g. 'foo.bar' and 'whatever'
  37. i = modname.rfind('.')
  38. if i >= 0:
  39. pkgname, modname = modname[:i], modname[i + 1:]
  40. else:
  41. pkgname = None
  42. # Compute the search path
  43. if pkgname:
  44. # We're not reloading the package, only the module in it
  45. pkg = sys.modules[pkgname]
  46. path = pkg.__path__ # Search inside the package
  47. else:
  48. # Search the top-level module path
  49. pkg = path = None # Make find_module() uses the default search path
  50. # Find the module; may raise ImportError
  51. (stream, filename, (_, _, kind)) = imp.find_module(modname, path)
  52. # Turn it into a code object
  53. try:
  54. # Is it Python source code or byte code read from a file?
  55. if kind not in (imp.PY_COMPILED, imp.PY_SOURCE):
  56. # Fall back to built-in reload()
  57. return reload(module)
  58. if kind == imp.PY_SOURCE:
  59. source = stream.read()
  60. code = compile(source, filename, 'exec')
  61. else:
  62. code = load(stream)
  63. finally:
  64. if stream:
  65. stream.close()
  66. # Execute the code. We copy the module dict to a temporary; then clear the
  67. # module dict; then execute the new code in the module dict; then swap
  68. # things back and around. This trick (due to Glyph Lefkowitz) ensures that
  69. # the (readonly) __globals__ attribute of methods and functions is set to
  70. # the correct dict object.
  71. tmpns = modns.copy()
  72. modns.clear()
  73. modns['__name__'] = tmpns['__name__']
  74. exec(code, modns)
  75. # Now we get to the hard part
  76. oldnames = set(tmpns)
  77. newnames = set(modns)
  78. # Update attributes in place
  79. for name in oldnames & newnames:
  80. modns[name] = _update(tmpns[name], modns[name])
  81. # Done!
  82. return module
  83. def _update(oldobj, newobj):
  84. '''
  85. Update oldobj, if possible in place, with newobj.
  86. If oldobj is immutable, this simply returns newobj.
  87. Args:
  88. oldobj: the object to be updated
  89. newobj: the object used as the source for the update
  90. Returns:
  91. either oldobj, updated in place, or newobj.
  92. '''
  93. if oldobj is newobj:
  94. # Probably something imported
  95. return newobj
  96. if type(oldobj) is not type(newobj):
  97. # Cop-out: if the type changed, give up
  98. return newobj
  99. if hasattr(newobj, '__reload_update__'):
  100. # Provide a hook for updating
  101. return newobj.__reload_update__(oldobj)
  102. if isclass(newobj):
  103. return _update_class(oldobj, newobj)
  104. if isfunction(newobj):
  105. return _update_function(oldobj, newobj)
  106. if ismethod(newobj):
  107. return _update_method(oldobj, newobj)
  108. if isinstance(newobj, classmethod):
  109. return _update_classmethod(oldobj, newobj)
  110. if isinstance(newobj, staticmethod):
  111. return _update_staticmethod(oldobj, newobj)
  112. # Not something we recognize, just give up
  113. return newobj
  114. # All of the following functions have the same signature as _update()
  115. def _update_function(oldfunc, newfunc):
  116. '''Update a function object.'''
  117. setattr(oldfunc, '__doc__', getdoc(newfunc))
  118. clsdict(oldfunc).update(clsdict(newfunc))
  119. setattr(oldfunc, _func_code, func_code(newfunc))
  120. setattr(oldfunc, _func_defaults, func_defaults(newfunc))
  121. return oldfunc
  122. def _update_method(oldmeth, newmeth):
  123. '''Update a method object.'''
  124. # XXX What if im_func is not a function?
  125. _update(method_func(oldmeth), method_func(newmeth))
  126. return oldmeth
  127. def _update_class(oldclass, newclass):
  128. '''Update a class object.'''
  129. olddict = clsdict(oldclass)
  130. newdict = clsdict(newclass)
  131. oldnames = set(olddict)
  132. newnames = set(newdict)
  133. for name in newnames - oldnames:
  134. setattr(oldclass, name, newdict[name])
  135. for name in oldnames - newnames:
  136. delattr(oldclass, name)
  137. for name in oldnames & newnames - {'__dict__', '__doc__'}:
  138. setattr(oldclass, name, _update(olddict[name], newdict[name]))
  139. return oldclass
  140. def _update_classmethod(oldcm, newcm):
  141. '''Update a classmethod update.'''
  142. # While we can't modify the classmethod object itself (it has no mutable
  143. # attributes), we *can* extract the underlying function (by calling
  144. # __get__(), which returns a method object) and update it in-place. We
  145. # don't have the class available to pass to __get__() but any object except
  146. # None will do.
  147. _update(oldcm.__get__(0), newcm.__get__(0))
  148. return newcm
  149. def _update_staticmethod(oldsm, newsm):
  150. '''Update a staticmethod update.'''
  151. # While we can't modify the staticmethod object itself (it has no mutable
  152. # attributes), we *can* extract the underlying function (by calling
  153. # __get__(), which returns it) and update it in-place. We don't have the
  154. # class available to pass to __get__() but any object except None will do.
  155. _update(oldsm.__get__(0), newsm.__get__(0))
  156. return newsm