##// END OF EJS Templates
autoreload docstring
vivainio2 -
Show More
@@ -1,239 +1,244 b''
1 """
1 """
2 IPython extension: autoreload modules before executing the next line
2 IPython extension: autoreload modules before executing the next line
3
3
4 Try::
5
6 %autoreload?
7
8 for documentation.
4 """
9 """
5
10
6 # Pauli Virtanen <pav@iki.fi>, 2008.
11 # Pauli Virtanen <pav@iki.fi>, 2008.
7 # Thomas Heller, 2000.
12 # Thomas Heller, 2000.
8 #
13 #
9 # This IPython module is written by Pauli Virtanen, based on the autoreload
14 # This IPython module is written by Pauli Virtanen, based on the autoreload
10 # code by Thomas Heller.
15 # code by Thomas Heller.
11
16
12 #------------------------------------------------------------------------------
17 #------------------------------------------------------------------------------
13 # Autoreload functionality
18 # Autoreload functionality
14 #------------------------------------------------------------------------------
19 #------------------------------------------------------------------------------
15
20
16 import time, os, threading, sys, types, imp, inspect, traceback, atexit
21 import time, os, threading, sys, types, imp, inspect, traceback, atexit
17
22
18 def _get_compiled_ext():
23 def _get_compiled_ext():
19 """Official way to get the extension of compiled files (.pyc or .pyo)"""
24 """Official way to get the extension of compiled files (.pyc or .pyo)"""
20 for ext, mode, typ in imp.get_suffixes():
25 for ext, mode, typ in imp.get_suffixes():
21 if typ == imp.PY_COMPILED:
26 if typ == imp.PY_COMPILED:
22 return ext
27 return ext
23
28
24 PY_COMPILED_EXT = _get_compiled_ext()
29 PY_COMPILED_EXT = _get_compiled_ext()
25
30
26 class ModuleReloader(object):
31 class ModuleReloader(object):
27 skipped = {}
32 skipped = {}
28 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
33 """Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
29
34
30 modules = {}
35 modules = {}
31 """Modules specially marked as autoreloadable."""
36 """Modules specially marked as autoreloadable."""
32
37
33 skip_modules = {}
38 skip_modules = {}
34 """Modules specially marked as not autoreloadable."""
39 """Modules specially marked as not autoreloadable."""
35
40
36 check_all = True
41 check_all = True
37 """Autoreload all modules, not just those listed in 'modules'"""
42 """Autoreload all modules, not just those listed in 'modules'"""
38
43
39 def check(self, check_all=False):
44 def check(self, check_all=False):
40 """Check whether some modules need to be reloaded."""
45 """Check whether some modules need to be reloaded."""
41
46
42 if check_all or self.check_all:
47 if check_all or self.check_all:
43 modules = sys.modules.keys()
48 modules = sys.modules.keys()
44 else:
49 else:
45 modules = self.modules.keys()
50 modules = self.modules.keys()
46
51
47 for modname in modules:
52 for modname in modules:
48 m = sys.modules.get(modname, None)
53 m = sys.modules.get(modname, None)
49
54
50 if modname in self.skip_modules:
55 if modname in self.skip_modules:
51 continue
56 continue
52
57
53 if not hasattr(m, '__file__'):
58 if not hasattr(m, '__file__'):
54 continue
59 continue
55
60
56 if m.__name__ == '__main__':
61 if m.__name__ == '__main__':
57 # we cannot reload(__main__)
62 # we cannot reload(__main__)
58 continue
63 continue
59
64
60 filename = m.__file__
65 filename = m.__file__
61 dirname = os.path.dirname(filename)
66 dirname = os.path.dirname(filename)
62 path, ext = os.path.splitext(filename)
67 path, ext = os.path.splitext(filename)
63
68
64 if ext.lower() == '.py':
69 if ext.lower() == '.py':
65 ext = PY_COMPILED_EXT
70 ext = PY_COMPILED_EXT
66 filename = os.path.join(dirname, path + PY_COMPILED_EXT)
71 filename = os.path.join(dirname, path + PY_COMPILED_EXT)
67
72
68 if ext != PY_COMPILED_EXT:
73 if ext != PY_COMPILED_EXT:
69 continue
74 continue
70
75
71 try:
76 try:
72 pymtime = os.stat(filename[:-1]).st_mtime
77 pymtime = os.stat(filename[:-1]).st_mtime
73 if pymtime <= os.stat(filename).st_mtime:
78 if pymtime <= os.stat(filename).st_mtime:
74 continue
79 continue
75 if self.skipped.get(filename[:-1], None) == pymtime:
80 if self.skipped.get(filename[:-1], None) == pymtime:
76 continue
81 continue
77 except OSError:
82 except OSError:
78 continue
83 continue
79
84
80 try:
85 try:
81 superreload(m)
86 superreload(m)
82 if filename[:-1] in self.skipped:
87 if filename[:-1] in self.skipped:
83 del self.skipped[filename[:-1]]
88 del self.skipped[filename[:-1]]
84 except:
89 except:
85 self.skipped[filename[:-1]] = pymtime
90 self.skipped[filename[:-1]] = pymtime
86
91
87 def update_function(old, new, attrnames):
92 def update_function(old, new, attrnames):
88 for name in attrnames:
93 for name in attrnames:
89 setattr(old, name, getattr(new, name))
94 setattr(old, name, getattr(new, name))
90
95
91 def superreload(module, reload=reload):
96 def superreload(module, reload=reload):
92 """Enhanced version of the builtin reload function.
97 """Enhanced version of the builtin reload function.
93
98
94 superreload replaces the class dictionary of every top-level
99 superreload replaces the class dictionary of every top-level
95 class in the module with the new one automatically,
100 class in the module with the new one automatically,
96 as well as every function's code object.
101 as well as every function's code object.
97
102
98 """
103 """
99
104
100 module = reload(module)
105 module = reload(module)
101
106
102 # iterate over all objects and update them
107 # iterate over all objects and update them
103 count = 0
108 count = 0
104 for name, new_obj in module.__dict__.items():
109 for name, new_obj in module.__dict__.items():
105 key = (module.__name__, name)
110 key = (module.__name__, name)
106 if _old_objects.has_key(key):
111 if _old_objects.has_key(key):
107 for old_obj in _old_objects[key]:
112 for old_obj in _old_objects[key]:
108 if type(new_obj) == types.ClassType:
113 if type(new_obj) == types.ClassType:
109 old_obj.__dict__.update(new_obj.__dict__)
114 old_obj.__dict__.update(new_obj.__dict__)
110 count += 1
115 count += 1
111 elif type(new_obj) == types.FunctionType:
116 elif type(new_obj) == types.FunctionType:
112 update_function(old_obj,
117 update_function(old_obj,
113 new_obj,
118 new_obj,
114 "func_code func_defaults func_doc".split())
119 "func_code func_defaults func_doc".split())
115 count += 1
120 count += 1
116 elif type(new_obj) == types.MethodType:
121 elif type(new_obj) == types.MethodType:
117 update_function(old_obj.im_func,
122 update_function(old_obj.im_func,
118 new_obj.im_func,
123 new_obj.im_func,
119 "func_code func_defaults func_doc".split())
124 "func_code func_defaults func_doc".split())
120 count += 1
125 count += 1
121
126
122 return module
127 return module
123
128
124 reloader = ModuleReloader()
129 reloader = ModuleReloader()
125
130
126 #------------------------------------------------------------------------------
131 #------------------------------------------------------------------------------
127 # IPython monkey-patching
132 # IPython monkey-patching
128 #------------------------------------------------------------------------------
133 #------------------------------------------------------------------------------
129
134
130 import IPython.iplib
135 import IPython.iplib
131
136
132 autoreload_enabled = False
137 autoreload_enabled = False
133
138
134 def runcode_hook(self):
139 def runcode_hook(self):
135 if not autoreload_enabled:
140 if not autoreload_enabled:
136 raise IPython.ipapi.TryNext
141 raise IPython.ipapi.TryNext
137 try:
142 try:
138 reloader.check()
143 reloader.check()
139 except:
144 except:
140 pass
145 pass
141
146
142
147
143 def enable_autoreload():
148 def enable_autoreload():
144 global autoreload_enabled
149 global autoreload_enabled
145 autoreload_enabled = True
150 autoreload_enabled = True
146
151
147
152
148 def disable_autoreload():
153 def disable_autoreload():
149 global autoreload_enabled
154 global autoreload_enabled
150 autoreload_enabled = False
155 autoreload_enabled = False
151
156
152 #------------------------------------------------------------------------------
157 #------------------------------------------------------------------------------
153 # IPython connectivity
158 # IPython connectivity
154 #------------------------------------------------------------------------------
159 #------------------------------------------------------------------------------
155
160
156 import IPython.ipapi
161 import IPython.ipapi
157 ip = IPython.ipapi.get()
162 ip = IPython.ipapi.get()
158
163
159 def autoreload_f(self, parameter_s=''):
164 def autoreload_f(self, parameter_s=''):
160 r""" %autoreload => Reload modules automatically
165 r""" %autoreload => Reload modules automatically
161
166
162 %autoreload
167 %autoreload
163 Reload all modules (except thoses excluded by %aimport) automatically now.
168 Reload all modules (except thoses excluded by %aimport) automatically now.
164
169
165 %autoreload 1
170 %autoreload 1
166 Reload all modules imported with %aimport every time before executing
171 Reload all modules imported with %aimport every time before executing
167 the Python code typed.
172 the Python code typed.
168
173
169 %autoreload 2
174 %autoreload 2
170 Reload all modules (except thoses excluded by %aimport) every time
175 Reload all modules (except thoses excluded by %aimport) every time
171 before executing the Python code typed.
176 before executing the Python code typed.
172
177
173 Reloading Python modules in a reliable way is in general
178 Reloading Python modules in a reliable way is in general
174 difficult, and unexpected things may occur. Some of the common
179 difficult, and unexpected things may occur. Some of the common
175 caveats relevant for 'autoreload' are:
180 caveats relevant for 'autoreload' are:
176
181
177 - Modules are not reloaded in any specific order, and no dependency
182 - Modules are not reloaded in any specific order, and no dependency
178 analysis is done. For example, modules with 'from xxx import foo'
183 analysis is done. For example, modules with 'from xxx import foo'
179 retain old versions of 'foo' when 'xxx' is autoreloaded.
184 retain old versions of 'foo' when 'xxx' is autoreloaded.
180 - Functions or objects imported from the autoreloaded module to
185 - Functions or objects imported from the autoreloaded module to
181 the interactive namespace are not updated.
186 the interactive namespace are not updated.
182 - C extension modules cannot be reloaded, and so cannot be
187 - C extension modules cannot be reloaded, and so cannot be
183 autoreloaded.
188 autoreloaded.
184 """
189 """
185 if parameter_s == '':
190 if parameter_s == '':
186 reloader.check(True)
191 reloader.check(True)
187 elif parameter_s == '0':
192 elif parameter_s == '0':
188 disable_autoreload()
193 disable_autoreload()
189 elif parameter_s == '1':
194 elif parameter_s == '1':
190 reloader.check_all = False
195 reloader.check_all = False
191 enable_autoreload()
196 enable_autoreload()
192 elif parameter_s == '2':
197 elif parameter_s == '2':
193 reloader.check_all = True
198 reloader.check_all = True
194 enable_autoreload()
199 enable_autoreload()
195
200
196 def aimport_f(self, parameter_s=''):
201 def aimport_f(self, parameter_s=''):
197 """%aimport => Import modules for automatic reloading.
202 """%aimport => Import modules for automatic reloading.
198
203
199 %aimport
204 %aimport
200 List modules to automatically import and not to import.
205 List modules to automatically import and not to import.
201
206
202 %aimport foo
207 %aimport foo
203 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
208 Import module 'foo' and mark it to be autoreloaded for %autoreload 1
204
209
205 %aimport -foo
210 %aimport -foo
206 Mark module 'foo' to not be autoreloaded for %autoreload 1
211 Mark module 'foo' to not be autoreloaded for %autoreload 1
207
212
208 """
213 """
209
214
210 modname = parameter_s
215 modname = parameter_s
211 if not modname:
216 if not modname:
212 to_reload = reloader.modules.keys()
217 to_reload = reloader.modules.keys()
213 to_reload.sort()
218 to_reload.sort()
214 to_skip = reloader.skip_modules.keys()
219 to_skip = reloader.skip_modules.keys()
215 to_skip.sort()
220 to_skip.sort()
216 if reloader.check_all:
221 if reloader.check_all:
217 print "Modules to reload:\nall-expect-skipped"
222 print "Modules to reload:\nall-expect-skipped"
218 else:
223 else:
219 print "Modules to reload:\n%s" % ' '.join(to_reload)
224 print "Modules to reload:\n%s" % ' '.join(to_reload)
220 print "\nModules to skip:\n%s" % ' '.join(to_skip)
225 print "\nModules to skip:\n%s" % ' '.join(to_skip)
221 elif modname.startswith('-'):
226 elif modname.startswith('-'):
222 modname = modname[1:]
227 modname = modname[1:]
223 try: del reloader.modules[modname]
228 try: del reloader.modules[modname]
224 except KeyError: pass
229 except KeyError: pass
225 reloader.skip_modules[modname] = True
230 reloader.skip_modules[modname] = True
226 else:
231 else:
227 try: del reloader.skip_modules[modname]
232 try: del reloader.skip_modules[modname]
228 except KeyError: pass
233 except KeyError: pass
229 reloader.modules[modname] = True
234 reloader.modules[modname] = True
230
235
231 mod = __import__(modname)
236 mod = __import__(modname)
232 ip.to_user_ns({modname: mod})
237 ip.to_user_ns({modname: mod})
233
238
234 def init():
239 def init():
235 ip.expose_magic('autoreload', autoreload_f)
240 ip.expose_magic('autoreload', autoreload_f)
236 ip.expose_magic('aimport', aimport_f)
241 ip.expose_magic('aimport', aimport_f)
237 ip.set_hook('pre_runcode_hook', runcode_hook)
242 ip.set_hook('pre_runcode_hook', runcode_hook)
238
243
239 init() No newline at end of file
244 init()
General Comments 0
You need to be logged in to leave comments. Login now