##// END OF EJS Templates
synchronize with editor patch
synchronize with editor patch

File last commit:

r1037:ec42e737
r1241:d7f37776
Show More
ipy_autoreload.py
243 lines | 7.4 KiB | text/x-python | PythonLexer
"""
IPython extension: autoreload modules before executing the next line
Try::
%autoreload?
for documentation.
"""
# Pauli Virtanen <pav@iki.fi>, 2008.
# Thomas Heller, 2000.
#
# This IPython module is written by Pauli Virtanen, based on the autoreload
# code by Thomas Heller.
#------------------------------------------------------------------------------
# Autoreload functionality
#------------------------------------------------------------------------------
import time, os, threading, sys, types, imp, inspect, traceback, atexit
def _get_compiled_ext():
"""Official way to get the extension of compiled files (.pyc or .pyo)"""
for ext, mode, typ in imp.get_suffixes():
if typ == imp.PY_COMPILED:
return ext
PY_COMPILED_EXT = _get_compiled_ext()
class ModuleReloader(object):
skipped = {}
"""Modules that failed to reload: {module: mtime-on-failed-reload, ...}"""
modules = {}
"""Modules specially marked as autoreloadable."""
skip_modules = {}
"""Modules specially marked as not autoreloadable."""
check_all = True
"""Autoreload all modules, not just those listed in 'modules'"""
def check(self, check_all=False):
"""Check whether some modules need to be reloaded."""
if check_all or self.check_all:
modules = sys.modules.keys()
else:
modules = self.modules.keys()
for modname in modules:
m = sys.modules.get(modname, None)
if modname in self.skip_modules:
continue
if not hasattr(m, '__file__'):
continue
if m.__name__ == '__main__':
# we cannot reload(__main__)
continue
filename = m.__file__
dirname = os.path.dirname(filename)
path, ext = os.path.splitext(filename)
if ext.lower() == '.py':
ext = PY_COMPILED_EXT
filename = os.path.join(dirname, path + PY_COMPILED_EXT)
if ext != PY_COMPILED_EXT:
continue
try:
pymtime = os.stat(filename[:-1]).st_mtime
if pymtime <= os.stat(filename).st_mtime:
continue
if self.skipped.get(filename[:-1], None) == pymtime:
continue
except OSError:
continue
try:
superreload(m)
if filename[:-1] in self.skipped:
del self.skipped[filename[:-1]]
except:
self.skipped[filename[:-1]] = pymtime
def update_function(old, new, attrnames):
for name in attrnames:
setattr(old, name, getattr(new, name))
def superreload(module, reload=reload):
"""Enhanced version of the builtin reload function.
superreload replaces the class dictionary of every top-level
class in the module with the new one automatically,
as well as every function's code object.
"""
module = reload(module)
# iterate over all objects and update them
count = 0
for name, new_obj in module.__dict__.items():
key = (module.__name__, name)
if _old_objects.has_key(key):
for old_obj in _old_objects[key]:
if type(new_obj) == types.ClassType:
old_obj.__dict__.update(new_obj.__dict__)
count += 1
elif type(new_obj) == types.FunctionType:
update_function(old_obj,
new_obj,
"func_code func_defaults func_doc".split())
count += 1
elif type(new_obj) == types.MethodType:
update_function(old_obj.im_func,
new_obj.im_func,
"func_code func_defaults func_doc".split())
count += 1
return module
reloader = ModuleReloader()
#------------------------------------------------------------------------------
# IPython monkey-patching
#------------------------------------------------------------------------------
import IPython.iplib
autoreload_enabled = False
def runcode_hook(self):
if not autoreload_enabled:
raise IPython.ipapi.TryNext
try:
reloader.check()
except:
pass
def enable_autoreload():
global autoreload_enabled
autoreload_enabled = True
def disable_autoreload():
global autoreload_enabled
autoreload_enabled = False
#------------------------------------------------------------------------------
# IPython connectivity
#------------------------------------------------------------------------------
import IPython.ipapi
ip = IPython.ipapi.get()
def autoreload_f(self, parameter_s=''):
r""" %autoreload => Reload modules automatically
%autoreload
Reload all modules (except thoses excluded by %aimport) automatically now.
%autoreload 1
Reload all modules imported with %aimport every time before executing
the Python code typed.
%autoreload 2
Reload all modules (except thoses excluded by %aimport) every time
before executing the Python code typed.
Reloading Python modules in a reliable way is in general
difficult, and unexpected things may occur. Some of the common
caveats relevant for 'autoreload' are:
- Modules are not reloaded in any specific order, and no dependency
analysis is done. For example, modules with 'from xxx import foo'
retain old versions of 'foo' when 'xxx' is autoreloaded.
- Functions or objects imported from the autoreloaded module to
the interactive namespace are not updated.
- C extension modules cannot be reloaded, and so cannot be
autoreloaded.
"""
if parameter_s == '':
reloader.check(True)
elif parameter_s == '0':
disable_autoreload()
elif parameter_s == '1':
reloader.check_all = False
enable_autoreload()
elif parameter_s == '2':
reloader.check_all = True
enable_autoreload()
def aimport_f(self, parameter_s=''):
"""%aimport => Import modules for automatic reloading.
%aimport
List modules to automatically import and not to import.
%aimport foo
Import module 'foo' and mark it to be autoreloaded for %autoreload 1
%aimport -foo
Mark module 'foo' to not be autoreloaded for %autoreload 1
"""
modname = parameter_s
if not modname:
to_reload = reloader.modules.keys()
to_reload.sort()
to_skip = reloader.skip_modules.keys()
to_skip.sort()
if reloader.check_all:
print "Modules to reload:\nall-expect-skipped"
else:
print "Modules to reload:\n%s" % ' '.join(to_reload)
print "\nModules to skip:\n%s" % ' '.join(to_skip)
elif modname.startswith('-'):
modname = modname[1:]
try: del reloader.modules[modname]
except KeyError: pass
reloader.skip_modules[modname] = True
else:
try: del reloader.skip_modules[modname]
except KeyError: pass
reloader.modules[modname] = True
mod = __import__(modname)
ip.to_user_ns({modname: mod})
def init():
ip.expose_magic('autoreload', autoreload_f)
ip.expose_magic('aimport', aimport_f)
ip.set_hook('pre_runcode_hook', runcode_hook)
init()