+_livepatch module
+livepatch/xreload: Alternative to reload().
+xreload performs a “live patch” of the modules/classes/functions/etc that have
+already been loaded in memory. It does so by executing the module in a
+scratch namespace, and then patching classes, methods and functions in-place.
+New objects are copied into the target namespace.
+This addresses cases where one module imported functions from another
+module.
+For example, suppose m1.py contains:
+from m2 import foo
+def print_foo():
+ return foo()
+
+
+and m2.py contains:
+
+If you edit m2.py and modify foo
, then reload(m2) on its own would not do
+what you want. You would also need to reload(m1) after reload(m2). This is
+because the built-in reload affects the module being reloaded, but references
+to the old module remain. On the other hand, xreload() patches the existing
+m2.foo, so that live references to it are updated.
+In table form:
+Undesired effect: reload(m2)
+Undesired effect: reload(m1); reload(m2)
+Desired effect: reload(m2); reload(m1)
+
+Desired effect: xreload(m2)
+Desired effect: xreload(m1); xreload(m2)
+Desired effect: xreload(m2); xreload(m1)
+
+
+Even with just two modules, we can see that xreload() is an improvement. When
+working with a large set of interdependent modules, it becomes infeasible to
+know the precise sequence of reload() calls that would be necessary.
+xreload() really shines in that case.
+This implementation of xreload() was originally based the following
+mailing-list post by Guido van Rossum:
+
+
+
+Customizing behavior
+If a class/function/module/etc has an attribute __livepatch__, then this
+function is called instead of performing the regular livepatch mechanism.
+The __livepatch__() function is called with the following arguments:
+
+
+These arguments are matched by name and are passed only if the
+__livepatch__
function is declared to take such named arguments or it takes
+**kwargs. If the __livepatch__
function takes **kwargs, it should ignore
+unknown arguments, in case new parameters are added in the future.
+If the object being updated is an object instance, and __livepatch__
is a
+method, then the function is bound to the new object, i.e. the self
+parameter is the same as new
.
+If the __livepatch__
function successfully patched the old
object, then
+it should return old
. If it is unable to patch, it should return new
.
+Examples
+By default, any attributes on an existing function are updated with ones from
+the new function. If you want a memoized function to keep its cache across
+xreload, you could implement that like this:
+def memoize(function):
+ cache = {}
+ def wrapped_fn(*args):
+ try:
+ return cache[args]
+ except KeyError:
+ result = function(*args)
+ cache[args] = result
+ return result
+ wrapped_fn.cache = cache
+ def my_livepatch(old, new, do_livepatch):
+ keep_cache = dict(old.cache)
+ result = do_livepatch()
+ result.cache.update(keep_cache)
+ return result
+ wrapped_fn.__livepatch__ = my_livepatch
+ return wrapped_fn
+
+
+XXX change example b/c cache is already cleared by default
+XXX maybe global cache
+
+
+- class MyObj(…):
+- def __livepatch__(self, old):
self.__dict__.update(old.__dict__)
+return self
+
+
+
+- class MyObj(…):
+- def __init__(self):
self._my_cache = {}
+
+- def __livepatch__(self, old, do_livepatch):
keep_cache = dict(old._my_cache)
+result = do_livepatch()
+result._my_cache.update(keep_cache)
+return result
+
+
+
+
+
+XXX test
+
+
+-
+exception pyflyby._livepatch.UnknownModuleError
+
+
+
+-
+pyflyby._livepatch._format_age(t)
+
+
+
+-
+pyflyby._livepatch._get_definition_module(obj)
+Get the name of the module that an object is defined in, or None
if
+unknown.
+For classes and functions, this returns the __module__
attribute.
+For object instances, this returns None
, ignoring the __module__
+attribute. The reason is that the __module__
attribute on an instance
+just gives the module that the class was defined in, which is not
+necessarily the module where the instance was constructed.
+
+- Return type:
+str
+
+
+
+
+
+-
+pyflyby._livepatch._get_module_py_file(module)
+
+
+
+-
+pyflyby._livepatch._interpret_module(arg)
+
+
+
+-
+pyflyby._livepatch._livepatch__class(oldclass, newclass, modname, cache, visit_stack)
+Livepatch a class.
+This is similar to _livepatch__dict(oldclass.__dict__, newclass.__dict__).
+However, we can’t just operate on the dict, because class dictionaries are
+special objects that don’t allow setitem, even though we can setattr on
+the class.
+
+
+
+-
+pyflyby._livepatch._livepatch__dict(old_dict, new_dict, modname, cache, visit_stack)
+Livepatch a dict.
+
+
+
+-
+pyflyby._livepatch._livepatch__function(old_func, new_func, modname, cache, visit_stack)
+Livepatch a function.
+
+
+
+-
+pyflyby._livepatch._livepatch__method(old_method, new_method, modname, cache, visit_stack)
+Livepatch a method.
+
+
+
+-
+pyflyby._livepatch._livepatch__module(old_mod, new_mod, modname, cache, visit_stack)
+Livepatch a module.
+
+
+
+-
+pyflyby._livepatch._livepatch__object(oldobj, newobj, modname, cache, visit_stack)
+Livepatch a general object.
+
+
+
+-
+pyflyby._livepatch._livepatch__setattr(oldobj, newobj, name, modname, cache, visit_stack)
+Livepatch something via setattr, i.e.:
+oldobj.{name} = livepatch(oldobj.{name}, newobj.{name}, ...)
+
+
+
+
+
+-
+pyflyby._livepatch._xreload_module(module, filename, force=False)
+Reload a module in place, using livepatch.
+
+- Parameters:
+
+module (ModuleType
) – Module to reload.
+force – Whether to reload even if the module has not been modified since the
+previous load. If False
, then do nothing. If True
, then reload.
+
+
+
+
+
+