blob: ba5370e7f00354d70054847a221b0409aac819d7 [file] [log] [blame]
Guido van Rossumebbc01e2007-02-25 05:08:26 +00001"""Alternative to reload().
2
3This works by executing the module in a scratch namespace, and then
4patching classes, methods and functions. This avoids the need to
5patch instances. New objects are copied into the target namespace.
6"""
7
8import imp
9import sys
10import types
11
12
13def xreload(mod):
14 """Reload a module in place, updating classes, methods and functions.
15
16 Args:
17 mod: a module object
18
19 Returns:
20 The (updated) input object itself.
21 """
22 # Get the module name, e.g. 'foo.bar.whatever'
23 modname = mod.__name__
24 # Get the module namespace (dict) early; this is part of the type check
25 modns = mod.__dict__
26 # Parse it into package name and module name, e.g. 'foo.bar' and 'whatever'
27 i = modname.rfind(".")
28 if i >= 0:
29 pkgname, modname = modname[:i], modname[i+1:]
30 else:
31 pkgname = None
32 # Compute the search path
33 if pkgname:
34 # We're not reloading the package, only the module in it
35 pkg = sys.modules[pkgname]
36 path = pkg.__path__ # Search inside the package
37 else:
38 # Search the top-level module path
39 pkg = None
40 path = None # Make find_module() uses the default search path
41 # Find the module; may raise ImportError
42 (stream, filename, (suffix, mode, kind)) = imp.find_module(modname, path)
43 # Turn it into a code object
44 try:
45 # Is it Python source code or byte code read from a file?
46 # XXX Could handle frozen modules, zip-import modules
47 if kind not in (imp.PY_COMPILED, imp.PY_SOURCE):
48 # Fall back to built-in reload()
49 return reload(mod)
50 if kind == imp.PY_SOURCE:
51 source = stream.read()
52 code = compile(source, filename, "exec")
53 else:
54 code = marshal.load(stream)
55 finally:
56 if stream:
57 stream.close()
58 # Execute the code im a temporary namespace; if this fails, no changes
59 tmpns = {}
60 exec(code, tmpns)
61 # Now we get to the hard part
62 oldnames = set(modns)
63 newnames = set(tmpns)
64 # Add newly introduced names
65 for name in newnames - oldnames:
66 modns[name] = tmpns[name]
67 # Delete names that are no longer current
68 for name in oldnames - newnames - {"__name__"}:
69 del modns[name]
70 # Now update the rest in place
71 for name in oldnames & newnames:
72 modns[name] = _update(modns[name], tmpns[name])
73 # Done!
74 return mod
75
76
77def _update(oldobj, newobj):
78 """Update oldobj, if possible in place, with newobj.
79
80 If oldobj is immutable, this simply returns newobj.
81
82 Args:
83 oldobj: the object to be updated
84 newobj: the object used as the source for the update
85
86 Returns:
87 either oldobj, updated in place, or newobj.
88 """
89 if type(oldobj) is not type(newobj):
90 # Cop-out: if the type changed, give up
91 return newobj
92 if hasattr(newobj, "__reload_update__"):
93 # Provide a hook for updating
94 return newobj.__reload_update__(oldobj)
95 if isinstance(newobj, types.ClassType):
96 return _update_class(oldobj, newobj)
97 if isinstance(newobj, types.FunctionType):
98 return _update_function(oldobj, newobj)
99 if isinstance(newobj, types.MethodType):
100 return _update_method(oldobj, newobj)
101 # XXX Support class methods, static methods, other decorators
102 # Not something we recognize, just give up
103 return newobj
104
105
106def _update_function(oldfunc, newfunc):
107 """Update a function object."""
108 oldfunc.__doc__ = newfunc.__doc__
109 oldfunc.__dict__.update(newfunc.__dict__)
110 oldfunc.func_code = newfunc.func_code
111 oldfunc.func_defaults = newfunc.func_defaults
112 # XXX What else?
113 return oldfunc
114
115
116def _update_method(oldmeth, newmeth):
117 """Update a method object."""
118 # XXX What if im_func is not a function?
119 _update_function(oldmeth.im_func, newmeth.im_func)
120 return oldmeth
121
122
123def _update_class(oldclass, newclass):
124 """Update a class object."""
125 # XXX What about __slots__?
126 olddict = oldclass.__dict__
127 newdict = newclass.__dict__
128 oldnames = set(olddict)
129 newnames = set(newdict)
130 for name in newnames - oldnames:
131 setattr(oldclass, name, newdict[name])
132 for name in oldnames - newnames:
133 delattr(oldclass, name)
134 for name in oldnames & newnames - {"__dict__", "__doc__"}:
135 setattr(oldclass, name, newdict[name])
136 return oldclass