blob: 921d87e3089548fd82407118eac583f5d84d9954 [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()
Guido van Rossum5c0a6de2007-02-25 21:22:21 +000058 # Execute the code. We copy the module dict to a temporary; then
59 # clear the module dict; then execute the new code in the module
60 # dict; then swap things back and around. This trick (due to
61 # Glyph Lefkowitz) ensures that the (readonly) __globals__
62 # attribute of methods and functions is set to the correct dict
63 # object.
64 tmpns = modns.copy()
65 modns.clear()
66 modns["__name__"] = tmpns["__name__"]
67 exec(code, modns)
Guido van Rossumebbc01e2007-02-25 05:08:26 +000068 # Now we get to the hard part
Guido van Rossum5c0a6de2007-02-25 21:22:21 +000069 oldnames = set(tmpns)
70 newnames = set(modns)
71 # Update attributes in place
Guido van Rossumebbc01e2007-02-25 05:08:26 +000072 for name in oldnames & newnames:
Guido van Rossum5c0a6de2007-02-25 21:22:21 +000073 modns[name] = _update(tmpns[name], modns[name])
Guido van Rossumebbc01e2007-02-25 05:08:26 +000074 # Done!
75 return mod
76
77
78def _update(oldobj, newobj):
79 """Update oldobj, if possible in place, with newobj.
80
81 If oldobj is immutable, this simply returns newobj.
82
83 Args:
84 oldobj: the object to be updated
85 newobj: the object used as the source for the update
86
87 Returns:
88 either oldobj, updated in place, or newobj.
89 """
Guido van Rossum9a7e7742007-02-25 16:19:20 +000090 if oldobj is newobj:
91 # Probably something imported
92 return newobj
Guido van Rossumebbc01e2007-02-25 05:08:26 +000093 if type(oldobj) is not type(newobj):
94 # Cop-out: if the type changed, give up
95 return newobj
96 if hasattr(newobj, "__reload_update__"):
97 # Provide a hook for updating
98 return newobj.__reload_update__(oldobj)
99 if isinstance(newobj, types.ClassType):
100 return _update_class(oldobj, newobj)
101 if isinstance(newobj, types.FunctionType):
102 return _update_function(oldobj, newobj)
103 if isinstance(newobj, types.MethodType):
104 return _update_method(oldobj, newobj)
Guido van Rossum9a7e7742007-02-25 16:19:20 +0000105 if isinstance(newobj, classmethod):
106 return _update_classmethod(oldobj, newobj)
107 if isinstance(newobj, staticmethod):
108 return _update_staticmethod(oldobj, newobj)
109 # XXX How to support decorators?
Guido van Rossumebbc01e2007-02-25 05:08:26 +0000110 # Not something we recognize, just give up
111 return newobj
112
113
Guido van Rossum9a7e7742007-02-25 16:19:20 +0000114# All of the following functions have the same signature as _update()
115
116
Guido van Rossumebbc01e2007-02-25 05:08:26 +0000117def _update_function(oldfunc, newfunc):
118 """Update a function object."""
119 oldfunc.__doc__ = newfunc.__doc__
120 oldfunc.__dict__.update(newfunc.__dict__)
Neal Norwitz221085d2007-02-25 20:55:47 +0000121 oldfunc.__code__ = newfunc.__code__
122 oldfunc.__defaults__ = newfunc.__defaults__
Guido van Rossumebbc01e2007-02-25 05:08:26 +0000123 # XXX What else?
124 return oldfunc
125
126
127def _update_method(oldmeth, newmeth):
128 """Update a method object."""
129 # XXX What if im_func is not a function?
Guido van Rossum9a7e7742007-02-25 16:19:20 +0000130 _update(oldmeth.im_func, newmeth.im_func)
Guido van Rossumebbc01e2007-02-25 05:08:26 +0000131 return oldmeth
132
133
134def _update_class(oldclass, newclass):
135 """Update a class object."""
136 # XXX What about __slots__?
137 olddict = oldclass.__dict__
138 newdict = newclass.__dict__
139 oldnames = set(olddict)
140 newnames = set(newdict)
141 for name in newnames - oldnames:
142 setattr(oldclass, name, newdict[name])
143 for name in oldnames - newnames:
144 delattr(oldclass, name)
145 for name in oldnames & newnames - {"__dict__", "__doc__"}:
Guido van Rossum9a7e7742007-02-25 16:19:20 +0000146 setattr(oldclass, name, _update(olddict[name], newdict[name]))
Guido van Rossumebbc01e2007-02-25 05:08:26 +0000147 return oldclass
Guido van Rossum9a7e7742007-02-25 16:19:20 +0000148
149
150def _update_classmethod(oldcm, newcm):
151 """Update a classmethod update."""
152 # While we can't modify the classmethod object itself (it has no
153 # mutable attributes), we *can* extract the underlying function
154 # (by calling __get__(), which returns a method object) and update
155 # it in-place. We don't have the class available to pass to
156 # __get__() but any object except None will do.
157 _update(oldcm.__get__(0), newcm.__get__(0))
158 return newcm
159
160
161def _update_staticmethod(oldsm, newsm):
162 """Update a staticmethod update."""
163 # While we can't modify the staticmethod object itself (it has no
164 # mutable attributes), we *can* extract the underlying function
165 # (by calling __get__(), which returns it) and update it in-place.
166 # We don't have the class available to pass to __get__() but any
167 # object except None will do.
168 _update(oldsm.__get__(0), newsm.__get__(0))
169 return newsm