autoreload.py
623 lines
| 18.9 KiB
| text/x-python
|
PythonLexer
Fernando Perez
|
r6937 | """IPython extension to reload modules before executing user code. | ||
``autoreload`` reloads modules automatically before entering the execution of | ||||
code typed at the IPython prompt. | ||||
vivainio2
|
r1035 | |||
Pauli Virtanen
|
r4843 | This makes for example the following workflow possible: | ||
vivainio2
|
r1037 | |||
Pauli Virtanen
|
r4843 | .. sourcecode:: ipython | ||
In [1]: %load_ext autoreload | ||||
In [2]: %autoreload 2 | ||||
In [3]: from foo import some_function | ||||
In [4]: some_function() | ||||
Out[4]: 42 | ||||
In [5]: # open foo.py in an editor and change some_function to return 43 | ||||
In [6]: some_function() | ||||
Out[6]: 43 | ||||
Fernando Perez
|
r6937 | The module was reloaded without reloading it explicitly, and the object | ||
imported with ``from foo import ...`` was also updated. | ||||
Pauli Virtanen
|
r4843 | |||
Usage | ||||
===== | ||||
The following magic commands are provided: | ||||
``%autoreload`` | ||||
Reload all modules (except those excluded by ``%aimport``) | ||||
automatically now. | ||||
``%autoreload 0`` | ||||
Disable automatic reloading. | ||||
``%autoreload 1`` | ||||
Reload all modules imported with ``%aimport`` every time before | ||||
executing the Python code typed. | ||||
``%autoreload 2`` | ||||
Reload all modules (except those excluded by ``%aimport``) every | ||||
time before executing the Python code typed. | ||||
Spas Kalaydzhisyki
|
r26238 | ``%autoreload 3`` | ||
Reload all modules AND autoload newly added objects | ||||
every time before executing the Python code typed. | ||||
Pauli Virtanen
|
r4843 | ``%aimport`` | ||
List modules which are to be automatically imported or not to be imported. | ||||
``%aimport foo`` | ||||
Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1`` | ||||
Matthias Bussonnier
|
r23000 | ``%aimport foo, bar`` | ||
Srinivas Reddy Thatiparthy
|
r22988 | |||
Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1`` | ||||
Pauli Virtanen
|
r4843 | ``%aimport -foo`` | ||
Mark module 'foo' to not be autoreloaded. | ||||
Caveats | ||||
======= | ||||
Reloading Python modules in a reliable way is in general difficult, | ||||
and unexpected things may occur. ``%autoreload`` tries to work around | ||||
common pitfalls by replacing function code objects and parts of | ||||
classes previously in the module with new versions. This makes the | ||||
following things to work: | ||||
- Functions and classes imported via 'from xxx import foo' are upgraded | ||||
to new versions when 'xxx' is reloaded. | ||||
- Methods and properties of classes are upgraded on reload, so that | ||||
calling 'c.foo()' on an object 'c' created before the reload causes | ||||
the new code for 'foo' to be executed. | ||||
Some of the known remaining caveats are: | ||||
- Replacing code objects does not always succeed: changing a @property | ||||
in a class to an ordinary method or a method to a member variable | ||||
can cause problems (but in old objects only). | ||||
- Functions that are removed (eg. via monkey-patching) from a module | ||||
before it is reloaded are not upgraded. | ||||
Fernando Perez
|
r6937 | - C extension modules cannot be reloaded, and so cannot be autoreloaded. | ||
vivainio2
|
r1035 | """ | ||
Nikita Kniazev
|
r26873 | __skip_doctest__ = True | ||
Fernando Perez
|
r4866 | |||
Spas Kalaydzhisyki
|
r26246 | # ----------------------------------------------------------------------------- | ||
Fernando Perez
|
r6937 | # Copyright (C) 2000 Thomas Heller | ||
# Copyright (C) 2008 Pauli Virtanen <pav@iki.fi> | ||||
# Copyright (C) 2012 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
Spas Kalaydzhisyki
|
r26246 | # ----------------------------------------------------------------------------- | ||
vivainio2
|
r1035 | # | ||
# This IPython module is written by Pauli Virtanen, based on the autoreload | ||||
# code by Thomas Heller. | ||||
Spas Kalaydzhisyki
|
r26246 | # ----------------------------------------------------------------------------- | ||
Fernando Perez
|
r6937 | # Imports | ||
Spas Kalaydzhisyki
|
r26246 | # ----------------------------------------------------------------------------- | ||
Brian Granger
|
r8197 | |||
Fernando Perez
|
r6937 | import os | ||
import sys | ||||
import traceback | ||||
import types | ||||
Ville M. Vainio
|
r1245 | import weakref | ||
Niclas
|
r25185 | import gc | ||
Erik
|
r26962 | from importlib import import_module, reload | ||
Matthias Bussonnier
|
r24337 | from importlib.util import source_from_cache | ||
vivainio2
|
r1035 | |||
Spas Kalaydzhisyki
|
r26246 | # ------------------------------------------------------------------------------ | ||
Fernando Perez
|
r6937 | # Autoreload functionality | ||
Spas Kalaydzhisyki
|
r26246 | # ------------------------------------------------------------------------------ | ||
Fernando Perez
|
r6937 | |||
Spas Kalaydzhisyki
|
r26244 | class ModuleReloader: | ||
Pauli Virtanen
|
r4840 | enabled = False | ||
"""Whether this reloader is enabled""" | ||||
vivainio2
|
r1035 | check_all = True | ||
"""Autoreload all modules, not just those listed in 'modules'""" | ||||
Ville M. Vainio
|
r1245 | |||
Spas Kalaydzhisyki
|
r26238 | autoload_obj = False | ||
"""Autoreload all modules AND autoload all new objects""" | ||||
def __init__(self, shell=None): | ||||
Thomas Kluyver
|
r15682 | # Modules that failed to reload: {module: mtime-on-failed-reload, ...} | ||
self.failed = {} | ||||
# Modules specially marked as autoreloadable. | ||||
self.modules = {} | ||||
# Modules specially marked as not autoreloadable. | ||||
self.skip_modules = {} | ||||
# (module-name, name) -> weakref, for replacing old code objects | ||||
self.old_objects = {} | ||||
# Module modification timestamps | ||||
self.modules_mtimes = {} | ||||
Spas Kalaydzhisyki
|
r26238 | self.shell = shell | ||
Thomas Kluyver
|
r15682 | |||
# Cache module modification times | ||||
self.check(check_all=True, do_reload=False) | ||||
Pauli Virtanen
|
r2004 | |||
Pauli Virtanen
|
r4840 | def mark_module_skipped(self, module_name): | ||
"""Skip reloading the named module in the future""" | ||||
try: | ||||
del self.modules[module_name] | ||||
except KeyError: | ||||
pass | ||||
self.skip_modules[module_name] = True | ||||
def mark_module_reloadable(self, module_name): | ||||
"""Reload the named module in the future (if it is imported)""" | ||||
try: | ||||
del self.skip_modules[module_name] | ||||
except KeyError: | ||||
pass | ||||
self.modules[module_name] = True | ||||
def aimport_module(self, module_name): | ||||
"""Import a module, and mark it reloadable | ||||
Returns | ||||
------- | ||||
top_module : module | ||||
The imported module if it is top-level, or the top-level | ||||
top_name : module | ||||
Name of top_module | ||||
""" | ||||
self.mark_module_reloadable(module_name) | ||||
Diego Garcia
|
r22954 | import_module(module_name) | ||
Spas Kalaydzhisyki
|
r26246 | top_name = module_name.split(".")[0] | ||
Pauli Virtanen
|
r4840 | top_module = sys.modules[top_name] | ||
return top_module, top_name | ||||
Thomas Kluyver
|
r15682 | def filename_and_mtime(self, module): | ||
Spas Kalaydzhisyki
|
r26246 | if not hasattr(module, "__file__") or module.__file__ is None: | ||
Thomas Kluyver
|
r15682 | return None, None | ||
Spas Kalaydzhisyki
|
r26246 | if getattr(module, "__name__", None) in [None, "__mp_main__", "__main__"]: | ||
Luke Pfister
|
r23600 | # we cannot reload(__main__) or reload(__mp_main__) | ||
Thomas Kluyver
|
r15682 | return None, None | ||
filename = module.__file__ | ||||
path, ext = os.path.splitext(filename) | ||||
Spas Kalaydzhisyki
|
r26246 | if ext.lower() == ".py": | ||
Thomas Kluyver
|
r15682 | py_filename = filename | ||
else: | ||||
try: | ||||
Matthias Bussonnier
|
r24337 | py_filename = source_from_cache(filename) | ||
Thomas Kluyver
|
r15682 | except ValueError: | ||
return None, None | ||||
try: | ||||
pymtime = os.stat(py_filename).st_mtime | ||||
except OSError: | ||||
return None, None | ||||
return py_filename, pymtime | ||||
def check(self, check_all=False, do_reload=True): | ||||
vivainio2
|
r1035 | """Check whether some modules need to be reloaded.""" | ||
Pauli Virtanen
|
r2004 | |||
Pauli Virtanen
|
r4840 | if not self.enabled and not check_all: | ||
return | ||||
vivainio2
|
r1035 | if check_all or self.check_all: | ||
Thomas Kluyver
|
r13384 | modules = list(sys.modules.keys()) | ||
vivainio2
|
r1035 | else: | ||
Thomas Kluyver
|
r13384 | modules = list(self.modules.keys()) | ||
Pauli Virtanen
|
r2004 | |||
vivainio2
|
r1035 | for modname in modules: | ||
m = sys.modules.get(modname, None) | ||||
if modname in self.skip_modules: | ||||
continue | ||||
Pauli Virtanen
|
r2004 | |||
Thomas Kluyver
|
r15682 | py_filename, pymtime = self.filename_and_mtime(m) | ||
if py_filename is None: | ||||
vivainio2
|
r1035 | continue | ||
Pauli Virtanen
|
r2004 | |||
vivainio2
|
r1035 | try: | ||
Thomas Kluyver
|
r15682 | if pymtime <= self.modules_mtimes[modname]: | ||
vivainio2
|
r1035 | continue | ||
Thomas Kluyver
|
r15682 | except KeyError: | ||
self.modules_mtimes[modname] = pymtime | ||||
continue | ||||
else: | ||||
Pauli Virtanen
|
r4840 | if self.failed.get(py_filename, None) == pymtime: | ||
vivainio2
|
r1035 | continue | ||
Pauli Virtanen
|
r2004 | |||
Thomas Kluyver
|
r15682 | self.modules_mtimes[modname] = pymtime | ||
# If we've reached this point, we should try to reload the module | ||||
if do_reload: | ||||
try: | ||||
Spas Kalaydzhisyki
|
r26238 | if self.autoload_obj: | ||
superreload(m, reload, self.old_objects, self.shell) | ||||
else: | ||||
superreload(m, reload, self.old_objects) | ||||
Thomas Kluyver
|
r15682 | if py_filename in self.failed: | ||
del self.failed[py_filename] | ||||
except: | ||||
Spas Kalaydzhisyki
|
r26246 | print( | ||
"[autoreload of {} failed: {}]".format( | ||||
modname, traceback.format_exc(10) | ||||
), | ||||
file=sys.stderr, | ||||
) | ||||
Thomas Kluyver
|
r15682 | self.failed[py_filename] = pymtime | ||
vivainio2
|
r1035 | |||
Spas Kalaydzhisyki
|
r26246 | |||
# ------------------------------------------------------------------------------ | ||||
Ville M. Vainio
|
r1245 | # superreload | ||
Spas Kalaydzhisyki
|
r26246 | # ------------------------------------------------------------------------------ | ||
vivainio2
|
r1035 | |||
Srinivas Reddy Thatiparthy
|
r23088 | |||
Spas Kalaydzhisyki
|
r26246 | func_attrs = [ | ||
"__code__", | ||||
"__defaults__", | ||||
"__doc__", | ||||
"__closure__", | ||||
"__globals__", | ||||
"__dict__", | ||||
] | ||||
Thomas Kluyver
|
r5911 | |||
Fernando Perez
|
r6937 | |||
Ville M. Vainio
|
r1245 | def update_function(old, new): | ||
"""Upgrade the code object of a function""" | ||||
Thomas Kluyver
|
r5911 | for name in func_attrs: | ||
Ville M. Vainio
|
r1245 | try: | ||
setattr(old, name, getattr(new, name)) | ||||
except (AttributeError, TypeError): | ||||
pass | ||||
Fernando Perez
|
r6937 | |||
Niclas
|
r25185 | def update_instances(old, new): | ||
"""Use garbage collector to find all instances that refer to the old | ||||
class definition and update their __class__ to point to the new class | ||||
definition""" | ||||
Spas Kalaydzhisyki
|
r26246 | |||
Niclas
|
r25185 | refs = gc.get_referrers(old) | ||
for ref in refs: | ||||
if type(ref) is old: | ||||
ref.__class__ = new | ||||
Niclas
|
r24965 | |||
Niclas
|
r24992 | |||
Ville M. Vainio
|
r1245 | def update_class(old, new): | ||
"""Replace stuff in the __dict__ of a class, and upgrade | ||||
oscar6echo
|
r24514 | method code objects, and add new methods, if any""" | ||
Thomas Kluyver
|
r13384 | for key in list(old.__dict__.keys()): | ||
Ville M. Vainio
|
r1245 | old_obj = getattr(old, key) | ||
try: | ||||
new_obj = getattr(new, key) | ||||
Sanyam Agarwal
|
r24910 | # explicitly checking that comparison returns True to handle | ||
# cases where `==` doesn't return a boolean. | ||||
if (old_obj == new_obj) is True: | ||||
Matthias Bussonnier
|
r23397 | continue | ||
Ville M. Vainio
|
r1245 | except AttributeError: | ||
# obsolete attribute: remove it | ||||
Pauli Virtanen
|
r2004 | try: | ||
Ville M. Vainio
|
r1245 | delattr(old, key) | ||
except (AttributeError, TypeError): | ||||
pass | ||||
continue | ||||
Pauli Virtanen
|
r2004 | |||
Spas Kalaydzhisyki
|
r26246 | if update_generic(old_obj, new_obj): | ||
continue | ||||
Ville M. Vainio
|
r1245 | |||
try: | ||||
setattr(old, key, getattr(new, key)) | ||||
except (AttributeError, TypeError): | ||||
Spas Kalaydzhisyki
|
r26246 | pass # skip non-writable attributes | ||
Ville M. Vainio
|
r1245 | |||
oscar6echo
|
r24514 | for key in list(new.__dict__.keys()): | ||
if key not in list(old.__dict__.keys()): | ||||
try: | ||||
setattr(old, key, getattr(new, key)) | ||||
except (AttributeError, TypeError): | ||||
Spas Kalaydzhisyki
|
r26246 | pass # skip non-writable attributes | ||
oscar6echo
|
r24514 | |||
Niclas
|
r24965 | # update all instances of class | ||
Niclas
|
r24992 | update_instances(old, new) | ||
Niclas
|
r24965 | |||
Fernando Perez
|
r6937 | |||
Ville M. Vainio
|
r1245 | def update_property(old, new): | ||
"""Replace get/set/del functions of a property""" | ||||
update_generic(old.fdel, new.fdel) | ||||
update_generic(old.fget, new.fget) | ||||
update_generic(old.fset, new.fset) | ||||
Fernando Perez
|
r6937 | |||
Ville M. Vainio
|
r1245 | def isinstance2(a, b, typ): | ||
return isinstance(a, typ) and isinstance(b, typ) | ||||
Fernando Perez
|
r6937 | |||
Ville M. Vainio
|
r1245 | UPDATE_RULES = [ | ||
Spas Kalaydzhisyki
|
r26246 | (lambda a, b: isinstance2(a, b, type), update_class), | ||
(lambda a, b: isinstance2(a, b, types.FunctionType), update_function), | ||||
(lambda a, b: isinstance2(a, b, property), update_property), | ||||
Ville M. Vainio
|
r1245 | ] | ||
Spas Kalaydzhisyki
|
r26246 | UPDATE_RULES.extend( | ||
[ | ||||
( | ||||
lambda a, b: isinstance2(a, b, types.MethodType), | ||||
lambda a, b: update_function(a.__func__, b.__func__), | ||||
), | ||||
] | ||||
) | ||||
Mikhail Korobov
|
r9108 | |||
Thomas Kluyver
|
r5911 | |||
Ville M. Vainio
|
r1245 | def update_generic(a, b): | ||
for type_check, update in UPDATE_RULES: | ||||
if type_check(a, b): | ||||
update(a, b) | ||||
return True | ||||
return False | ||||
Fernando Perez
|
r6937 | |||
Spas Kalaydzhisyki
|
r26244 | class StrongRef: | ||
Ville M. Vainio
|
r1245 | def __init__(self, obj): | ||
self.obj = obj | ||||
Spas Kalaydzhisyki
|
r26246 | |||
Ville M. Vainio
|
r1245 | def __call__(self): | ||
return self.obj | ||||
Fernando Perez
|
r6937 | |||
Spas Kalaydzhisyki
|
r26249 | mod_attrs = [ | ||
"__name__", | ||||
"__doc__", | ||||
"__package__", | ||||
"__loader__", | ||||
"__spec__", | ||||
"__file__", | ||||
"__cached__", | ||||
Spas Kalaydzhisyki
|
r26251 | "__builtins__", | ||
Spas Kalaydzhisyki
|
r26249 | ] | ||
Spas Kalaydzhisyki
|
r26238 | def append_obj(module, d, name, obj, autoload=False): | ||
Spas Kalaydzhisyki
|
r26253 | in_module = hasattr(obj, "__module__") and obj.__module__ == module.__name__ | ||
Spas Kalaydzhisyki
|
r26238 | if autoload: | ||
Spas Kalaydzhisyki
|
r26249 | # check needed for module global built-ins | ||
Spas Kalaydzhisyki
|
r26253 | if not in_module and name in mod_attrs: | ||
Spas Kalaydzhisyki
|
r26238 | return False | ||
else: | ||||
Spas Kalaydzhisyki
|
r26253 | if not in_module: | ||
Spas Kalaydzhisyki
|
r26238 | return False | ||
key = (module.__name__, name) | ||||
try: | ||||
d.setdefault(key, []).append(weakref.ref(obj)) | ||||
except TypeError: | ||||
pass | ||||
return True | ||||
def superreload(module, reload=reload, old_objects=None, shell=None): | ||||
vivainio2
|
r1035 | """Enhanced version of the builtin reload function. | ||
Pauli Virtanen
|
r2004 | |||
Ville M. Vainio
|
r1245 | superreload remembers objects previously in the module, and | ||
- upgrades the class dictionary of every old class in the module | ||||
- upgrades the code object of every old function and method | ||||
- clears the module's namespace before reloading | ||||
Pauli Virtanen
|
r2004 | |||
vivainio2
|
r1035 | """ | ||
Emil Hessman
|
r24618 | if old_objects is None: | ||
old_objects = {} | ||||
Pauli Virtanen
|
r2004 | |||
Ville M. Vainio
|
r1245 | # collect old objects in the module | ||
Thomas Kluyver
|
r13384 | for name, obj in list(module.__dict__.items()): | ||
Spas Kalaydzhisyki
|
r26238 | if not append_obj(module, old_objects, name, obj): | ||
Ville M. Vainio
|
r1245 | continue | ||
key = (module.__name__, name) | ||||
try: | ||||
old_objects.setdefault(key, []).append(weakref.ref(obj)) | ||||
except TypeError: | ||||
Srinivas Reddy Thatiparthy
|
r23103 | pass | ||
Ville M. Vainio
|
r1245 | |||
# reload module | ||||
try: | ||||
# clear namespace first from old cruft | ||||
Pauli Virtanen
|
r4841 | old_dict = module.__dict__.copy() | ||
Ville M. Vainio
|
r1245 | old_name = module.__name__ | ||
module.__dict__.clear() | ||||
Spas Kalaydzhisyki
|
r26246 | module.__dict__["__name__"] = old_name | ||
module.__dict__["__loader__"] = old_dict["__loader__"] | ||||
Ville M. Vainio
|
r1245 | except (TypeError, AttributeError, KeyError): | ||
pass | ||||
Pauli Virtanen
|
r4841 | |||
try: | ||||
module = reload(module) | ||||
except: | ||||
# restore module dictionary on failed reload | ||||
module.__dict__.update(old_dict) | ||||
raise | ||||
Pauli Virtanen
|
r2004 | |||
Ville M. Vainio
|
r1245 | # iterate over all objects and update functions & classes | ||
Thomas Kluyver
|
r13384 | for name, new_obj in list(module.__dict__.items()): | ||
vivainio2
|
r1035 | key = (module.__name__, name) | ||
Spas Kalaydzhisyki
|
r26238 | if key not in old_objects: | ||
# here 'shell' acts both as a flag and as an output var | ||||
if ( | ||||
Spas Kalaydzhisyki
|
r26246 | shell is None | ||
or name == "Enum" | ||||
or not append_obj(module, old_objects, name, new_obj, True) | ||||
Spas Kalaydzhisyki
|
r26238 | ): | ||
continue | ||||
shell.user_ns[name] = new_obj | ||||
Ville M. Vainio
|
r1245 | |||
new_refs = [] | ||||
for old_ref in old_objects[key]: | ||||
old_obj = old_ref() | ||||
Spas Kalaydzhisyki
|
r26246 | if old_obj is None: | ||
continue | ||||
Ville M. Vainio
|
r1245 | new_refs.append(old_ref) | ||
update_generic(old_obj, new_obj) | ||||
if new_refs: | ||||
old_objects[key] = new_refs | ||||
else: | ||||
del old_objects[key] | ||||
vivainio2
|
r1035 | return module | ||
Spas Kalaydzhisyki
|
r26246 | |||
# ------------------------------------------------------------------------------ | ||||
Ville M. Vainio
|
r1245 | # IPython connectivity | ||
Spas Kalaydzhisyki
|
r26246 | # ------------------------------------------------------------------------------ | ||
Ville M. Vainio
|
r1245 | |||
Fernando Perez
|
r6973 | from IPython.core.magic import Magics, magics_class, line_magic | ||
Pauli Virtanen
|
r2004 | |||
Spas Kalaydzhisyki
|
r26246 | |||
Fernando Perez
|
r6973 | @magics_class | ||
Fernando Perez
|
r6937 | class AutoreloadMagics(Magics): | ||
Pauli Virtanen
|
r4840 | def __init__(self, *a, **kw): | ||
Spas Kalaydzhisyki
|
r26244 | super().__init__(*a, **kw) | ||
Spas Kalaydzhisyki
|
r26238 | self._reloader = ModuleReloader(self.shell) | ||
Pauli Virtanen
|
r4702 | self._reloader.check_all = False | ||
Spas Kalaydzhisyki
|
r26238 | self._reloader.autoload_obj = False | ||
Thomas Kluyver
|
r15682 | self.loaded_modules = set(sys.modules) | ||
Pauli Virtanen
|
r2004 | |||
Fernando Perez
|
r6937 | @line_magic | ||
Spas Kalaydzhisyki
|
r26246 | def autoreload(self, parameter_s=""): | ||
Pauli Virtanen
|
r4702 | r"""%autoreload => Reload modules automatically | ||
%autoreload | ||||
Reload all modules (except those excluded by %aimport) automatically | ||||
now. | ||||
%autoreload 0 | ||||
Disable automatic reloading. | ||||
%autoreload 1 | ||||
Reload all modules imported with %aimport every time before executing | ||||
the Python code typed. | ||||
%autoreload 2 | ||||
Reload all modules (except those 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. %autoreload tries to | ||||
work around common pitfalls by replacing function code objects and | ||||
parts of classes previously in the module with new versions. This | ||||
makes the following things to work: | ||||
- Functions and classes imported via 'from xxx import foo' are upgraded | ||||
to new versions when 'xxx' is reloaded. | ||||
- Methods and properties of classes are upgraded on reload, so that | ||||
calling 'c.foo()' on an object 'c' created before the reload causes | ||||
the new code for 'foo' to be executed. | ||||
Some of the known remaining caveats are: | ||||
- Replacing code objects does not always succeed: changing a @property | ||||
in a class to an ordinary method or a method to a member variable | ||||
can cause problems (but in old objects only). | ||||
- Functions that are removed (eg. via monkey-patching) from a module | ||||
before it is reloaded are not upgraded. | ||||
- C extension modules cannot be reloaded, and so cannot be | ||||
autoreloaded. | ||||
""" | ||||
Spas Kalaydzhisyki
|
r26246 | if parameter_s == "": | ||
Pauli Virtanen
|
r4702 | self._reloader.check(True) | ||
Spas Kalaydzhisyki
|
r26246 | elif parameter_s == "0": | ||
Pauli Virtanen
|
r4840 | self._reloader.enabled = False | ||
Spas Kalaydzhisyki
|
r26246 | elif parameter_s == "1": | ||
Pauli Virtanen
|
r4702 | self._reloader.check_all = False | ||
Pauli Virtanen
|
r4840 | self._reloader.enabled = True | ||
Spas Kalaydzhisyki
|
r26246 | elif parameter_s == "2": | ||
Pauli Virtanen
|
r4702 | self._reloader.check_all = True | ||
Pauli Virtanen
|
r4840 | self._reloader.enabled = True | ||
Spas Kalaydzhisyki
|
r26238 | self._reloader.enabled = True | ||
Spas Kalaydzhisyki
|
r26246 | elif parameter_s == "3": | ||
Spas Kalaydzhisyki
|
r26238 | self._reloader.check_all = True | ||
self._reloader.enabled = True | ||||
self._reloader.autoload_obj = True | ||||
Pauli Virtanen
|
r4702 | |||
Fernando Perez
|
r6937 | @line_magic | ||
Spas Kalaydzhisyki
|
r26246 | def aimport(self, parameter_s="", stream=None): | ||
Pauli Virtanen
|
r4702 | """%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 | ||||
Matthias Bussonnier
|
r23000 | %aimport foo, bar | ||
Srinivas Reddy Thatiparthy
|
r22988 | Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1 | ||
Pauli Virtanen
|
r4702 | %aimport -foo | ||
Mark module 'foo' to not be autoreloaded for %autoreload 1 | ||||
""" | ||||
modname = parameter_s | ||||
if not modname: | ||||
Thomas Kluyver
|
r13384 | to_reload = sorted(self._reloader.modules.keys()) | ||
to_skip = sorted(self._reloader.skip_modules.keys()) | ||||
Pauli Virtanen
|
r4840 | if stream is None: | ||
stream = sys.stdout | ||||
Pauli Virtanen
|
r4702 | if self._reloader.check_all: | ||
Pauli Virtanen
|
r4840 | stream.write("Modules to reload:\nall-except-skipped\n") | ||
Pauli Virtanen
|
r4702 | else: | ||
Spas Kalaydzhisyki
|
r26246 | stream.write("Modules to reload:\n%s\n" % " ".join(to_reload)) | ||
stream.write("\nModules to skip:\n%s\n" % " ".join(to_skip)) | ||||
elif modname.startswith("-"): | ||||
Pauli Virtanen
|
r4702 | modname = modname[1:] | ||
Pauli Virtanen
|
r4840 | self._reloader.mark_module_skipped(modname) | ||
Pauli Virtanen
|
r4702 | else: | ||
Spas Kalaydzhisyki
|
r26246 | for _module in [_.strip() for _ in modname.split(",")]: | ||
Srinivas Reddy Thatiparthy
|
r22988 | top_module, top_name = self._reloader.aimport_module(_module) | ||
vivainio2
|
r1035 | |||
Srinivas Reddy Thatiparthy
|
r22988 | # Inject module to user namespace | ||
self.shell.push({top_name: top_module}) | ||||
vivainio2
|
r1035 | |||
Thomas Kluyver
|
r15607 | def pre_run_cell(self): | ||
Thomas Kluyver
|
r15603 | if self._reloader.enabled: | ||
try: | ||||
self._reloader.check() | ||||
except: | ||||
pass | ||||
Pauli Virtanen
|
r4840 | |||
Thomas Kluyver
|
r15682 | def post_execute_hook(self): | ||
Spas Kalaydzhisyki
|
r26246 | """Cache the modification times of any modules imported in this execution""" | ||
Thomas Kluyver
|
r15682 | newly_loaded_modules = set(sys.modules) - self.loaded_modules | ||
for modname in newly_loaded_modules: | ||||
_, pymtime = self._reloader.filename_and_mtime(sys.modules[modname]) | ||||
if pymtime is not None: | ||||
self._reloader.modules_mtimes[modname] = pymtime | ||||
self.loaded_modules.update(newly_loaded_modules) | ||||
Fernando Perez
|
r6937 | |||
Pauli Virtanen
|
r4702 | def load_ipython_extension(ip): | ||
"""Load the extension in IPython.""" | ||||
Thomas Kluyver
|
r8552 | auto_reload = AutoreloadMagics(ip) | ||
ip.register_magics(auto_reload) | ||||
Spas Kalaydzhisyki
|
r26246 | ip.events.register("pre_run_cell", auto_reload.pre_run_cell) | ||
ip.events.register("post_execute", auto_reload.post_execute_hook) | ||||