/_unsorted/_core_new/_importing/module.py
Python | 291 lines | 269 code | 14 blank | 8 comment | 4 complexity | 87411e2b0411b732c672a082e895a0be MD5 | raw file
Possible License(s): BSD-3-Clause
- """core.importing module
- """
- __all__ = (
- "Module",
- "LazyModule", "SmartModule", "ExtensionModule",
- )
- import sys
- import imp
- ModuleType = type(sys)
- #################################################
- # module classes
- ModuleDescription = namedtuple("ModuleDescription", "suffix mode type")
- ModuleFile = namedtuple("ModuleFile", "file pathname description")
- class ModuleMeta(type):
- def from_path(cls, path, name=None, **kwargs):
- """Factory function for cls which pulls from the path."""
- raise NotImplementedError
- def from_module(cls, module, name=None, **kwargs):
- """Factory function for cls which copies the module."""
- if not hasattr(module, "items"):
- module = getattr(module, self.MODULE_NAMESPACE_ATTR)
- return cls.from_namespace(module, name, **kwargs)
- def from_namespace(cls, namespace, name=None, **kwargs):
- """Factory function for cls which copies the namespace."""
- namespace = namespace.copy()
- if "__builtins__" in namespace:
- del namespace["__builtins__"]
- namespace.update(kwargs)
- if name is None:
- name = namespace.get("__name__")
- else:
- namespace["__name__"] = name
- module = cls(name)
- module.__dict__.update(namespace)
- return module
- def find(cls, module):
- """Find this module's ModuleFile."""
- raise NotImplementedError
- def build(cls, module):
- """Execute the module and populate its namespace."""
- raise NotImplementedError
- def attach(cls, module, ...):
- """Bind the module to its package and sys.modules."""
- raise NotImplementedError
- def reload(cls, module):
- """Reload that module object."""
- raise NotImplementedError
- #class Module(ModuleType):
- class Module(object):
- """A straight-up replacement for the built-in module type.
- By default Module objects will always have the following attributes:
-
- __doc__
- __name__
- Where __name__ is set to the passed name and __doc__ to None. Here
- are the other attributes that may be found on Module objects:
- __all__ - a list of the module's "public" attribute names.
- __builtins__ - the builtins used during execution for non-builtin
- modules (None or not set for "built-in" modules).
- For this class __builtins__ is explicitly not set.
- __cached__ - the cached file (see PEP 3147).
- __file__ - the path to the file from which the module was
- loaded (None or not set for "built-in" modules).
- __loader__ - the loader that loaded the module (see PEP 302).
- __package__ - the package to which the module belongs (see
- PEP 366).
- __path__ - for packages, the list of paths to use when
- importing modules in the package (see PEP 302).
- For more on these attributes, see the following resources (all at
- <http://docs.python.org/dev>):
- /reference/simple_stmts.html#the-import-statement
- /library/importlib.html#importlib.abc.Loader.load_module
- /library/runpy.html#runpy.run_module
- /library/imp.html#imp.source_from_cache
- /whatsnew/3.2.html#pep-3147-pyc-repository-directories
- /tutorial/modules.html#packages-in-multiple-directories
- PEPS:
- 302 - http://www.python.org/dev/peps/pep-0302/
- 366 - http://www.python.org/dev/peps/pep-0366/
- 3147 - http://www.python.org/dev/peps/pep-3147/
-
- When a module's code object is executed, it should be done using
- the modules __dict__ as the globals passed in to exec(). Thus, any
- module attributes must be set before module execution if they are to
- be available during execution.
-
- Note: a package is just a module with __path__ set. The package's
- __init__.py is the actual code executed for that module. When
- submodules are imported, they are subsequently bound by their base
- name to the namespace of the package and __package__ of the
- submodule is set to the package. A submodule can be a package
- itself.
- """
- __metaclass__ = ModuleMeta
- MODULE_NAMESPACE_ATTR = "__dict__"
- MODULE_ATTRS = (
- "__name__", "__doc__", "__file__",
- "__loader__", "__all__", # optional
- "__package__", "__loader__", # future
- "__builtins__", # implementation detail
- )
- PACKAGE_ATTRS = ("__path__",)
-
- def __new__(cls, name, *args, **kwargs):
- if not name or not isinstance(name, str):
- raise TypeError("name must be a non-empty string")
- return super(cls, cls).__new__(cls, name, *args, **kwargs)
- def __init__(self, name, engine):
- self.__name__ = name
- self.__doc__ = None
- self.__engine__ = engine
- @property
- def __module__(self):
- return self
- @property
- def __builtins__(self):
- raise TypeError("__builtins__ not available")
- class LazyModule(Module):
- """Effectively a placeholder module.
-
- Any attribute access forces the lazy load and rebinds the original
- name to the wrapped module (before anything else happens).
- """
- def __init__(self, name, wrapped):
- type(self)._setattr(self, "__name__", name)
- type(self)._setattr(self, "__doc__", None)
- type(self)._setattr(self, "__wrapped__", wrapped)
- type(self)._setattr(self, "_done", False)
- # the triggers
- def __getattribute__(self, name):
- if name in ("__name__",):
- return object.__getattribute__(self, name)
- module = type(self).rebind(self)
- return getattr(module, name)
- def __setattr__(self, name, value):
- module = type(self).rebind(self)
- setattr(module, name, value)
- def __delattr__(self, name):
- module = type(self).rebind(self)
- delattr(module, name)
- # access work-around
- def _getattr(self, name):
- return object.__getattribute__(self, name)
- def _setattr(self, name, value):
- object.__setattr__(self, name, value)
- # the meat of the class
- def rebind(self, name=None):
- """Force the wrapped module to be put into place."""
- if type(self)._getattr(self, "_done"):
- raise TypeError("can't rebind a module that has been rebound")
- if name is None:
- name = self.__name__
- wrapped = type(self)._getattr(self, "__wrapped__")
- if isinstance(wrapped, str):
- wrapped = load_module(wrapped)
- type(self)._setattr(self, "__wrapped__", wrapped)
- attach_module(wrapped, name, verify=self)
- rebind_module(wrapped, name, verify=self)
- type(self)._setattr(self, "_done", True)
- return wrappped
- class SmartModule(Module):
- """Some extra features added on to the base module.
- The SmartModule methods are exposed as staticmethods on the
- metaclass so that they do not interfere with the namespace of
- module instances.
- The exception to this rule is the __wrapped__ attribute, which
- is bound to the module this one replaced, or to None.
- """
- class __metaclass__(type):
- def from_module(cls, module, lazy=False, attach=False):
- """Make a new SmartModule out of the passed module."""
- if isinstance(module, str):
- name = module
- else:
- name = module.__name__
- newmodule = cls(name)
- cls.assume(newmodule, module, lazy)
- cls.attach(newmodule, name)
- return newmodule
- @staticmethod
- def assume(module, wrapped, lazy=False):
- if isinstance(module, str):
- module = load_module(module)
- def __init__(self, name):
- super(type(self), self).__init__(name)
- self.__wrapped__ = None
- def assume(self, module):
- """Take over the place of the passed module in sys.modules."""
- self.__wrapped__ = module
- ...
- def find(self):
- ...
- def load(self):
- ...
- def attach(self):
- ...
- class ExtendedModule(ModuleType):
- BORROWED = ("__name__", "__doc__",
- "__file__", "__path__", "__package__",
- )
- def __init__(self, original=None, lazy=False):
- if not lazy:
- original = self.load_module(original)
- else:
- find_module(original)
- self.__wrapped__ = original
- self.lazy = lazy
-
- @classmethod
- def from_module(cls, module, lazy=False, replace=True):
- #def from_module(cls, module, *, lazy=False, replace=True):
- newmodule = cls(module, lazy)
- if replace:
- sys.modules[newmodule.__name__]
- #######################################
- # tests
- #
- # ExtendedModule
- # * <result of ExtendedModule(original)>.__name__ == original.__name__
- # * <result of from_module(module)>.__name__ == module.__name__
- # *
- # *
- # *
- # *
- #
- # *
- #