Skip to content

Instantly share code, notes, and snippets.

@jdanbrown
Created November 26, 2013 05:21
Show Gist options
  • Save jdanbrown/7653835 to your computer and use it in GitHub Desktop.
Save jdanbrown/7653835 to your computer and use it in GitHub Desktop.
import sys, types
def module(cls):
'''
Compose modules via class inheritance.
Construct a module from a singleton object from the given class `cls`, and
then replace cls's enclosing, top-level, file-based module with the newly
constructed class-based module. Retain a reference `_cls` to the original
class to allow extension via class inheritance.
'''
# Build a module out of singleton cls()
m = types.ModuleType(cls.__module__)
m._cls = cls # Retain for composability via subclassing
m._obj = cls() # Fix for determinism
for x in [
sys.modules[m.__name__], # Retain names from m, e.g. submodules, imports, globals
m._obj, # After m names, for shadowing
]:
m.__dict__.update({ k: getattr(x,k) for k in dir(x) })
# WAT. Keep a reference to the old module to prevent it from getting gc'd,
# else its __del__ method will manually nullify each value in its __dict__ by
# setting it to None, and since the module's __dict__ is pointed to by each of
# its functions' func_globals attr, this will make all globally scoped vars in
# all functions mysteriously become None.
# [https://mail.python.org/pipermail/python-dev/2007-January/070690.html]
m._mod = sys.modules[m.__name__]
# Swap m in for the existing top-level module
old = dir(sys.modules[m.__name__])
sys.modules[m.__name__] = m
new = dir(sys.modules[m.__name__])
assert set(old).issubset(new), 'Module replacement would lose names: %s -> %s' % (old, new)
return m
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment