/_unsorted/core/importing/components.py
Python | 256 lines | 239 code | 2 blank | 15 comment | 0 complexity | 98269b92ed65853a1066af10dd7c2ff4 MD5 | raw file
Possible License(s): BSD-3-Clause
- """core.importing.components module
- """
- """
- Module components act as extensions to the module associated with the
- component set. As a kind of plugin system, they allow a simple
- mechanism for extending a module.
- """
- """
- Making a Component Module
- =========================
- First, make a 'component module', which is a normal Python source file
- that contains whatever you want. This likely includes the objects you
- want to add to the core built-ins.
- Second, in the component module set __all__ to an iterable containing
- the names that should be bound in core.builtins.
- Third, put the component module into the _builtins directory of the core
- package.
- Restrictions
- ============
- The .builtins directory is not a package (no __init__.py) so component
- modules cannot be imported directly through the 'core' namespace.
- Rather, their only purpose is to extend core.builtins.
- If you would like to expose part of a component module outside
- core.builtins, put that part elsewhere in a normal module. Then you can
- import it into the component module, but still access it from outside
- core.builtins.
- Since .builtins is not a package, no component module will be loaded
- through the normal import mechanism. However, each will still be
- treated as a normal module.
- As such, component modules must have valid module names (non-keyword
- identifiers).
- Component modules are expected to be Python source files and are
- compiled with that in mind. The filename of each must have a '.py'
- extension.
- Any component module whose name starts with an underscore is ignored.
- Packages are not supported as component modules, so they are likewise
- ignored here.
- If a component module does not have an __all__ attibute, it is ignored.
- If any of the names in __all__ is already bound in core.builtins, the
- import of core.builtins will fail.
- Be careful of existing files when putting files into the .builtins
- directory.
- """
- """
- The core package makes use of a concept called component modules. These
- are sub-modules that contain the result of splitting a module up into
- cohesive parts. However, unlike with the similar approach of simply
- putting the sub-modules into a package, component modules are not
- exposed for direct import.
- Component modules are grouped together into component sets. Each set is
- realized by a directory whose name is a period followed by the name of
- the set (e.g. ".<set name>"). A strict component set directory will
- contain no __init__ module, but this is not enforced.
- A component set directory on its own is nothing more than an import-
- inaccessible directory. Using the set requires a module that
- So the quick way to identify a component set is the presence of a module
- file next to a component set directory with a matching name.
- """
- # XXX need handler support (a la PEP 302) for other manifestations of
- # component sets and module components (e.g. zip files, .pyc, etc.)
- __all__ = ("ComponentSet",)
- import os
- from os.path import join as pjoin, splitext, dirname
- from core import importing, builtins
- INDICATOR = "."
- class ModuleComponent(importing.Module):
- def __init__(self, name, setname, setpath):
- super(type(self), self).__init__(name)
- self.setname = setname
- self.setpath = setpath
-
- def default_names(self):
- return ()
- def load(self, names=None):
- if self.isloaded():
- return
- ns = exec_module(self.setpath, self.name)
- if names is None:
- names = ns.get("__all__", self.default_names)
- if not names:
- return
- try:
- self.namespace.update((k, ns[k]) for k in names)
- except KeyError, e:
- raise ImportError("cannot import name %s" % e[0])
-
- class ComponentNamespace(importing.ModuleNamespace):
- UNLOCKED_CLASS = ModuleComponent
- # XXX circular reference?
- ModuleComponent.NAMESPACE_CLASS = ComponentNamespace
- class ComponentSet(object):
- """A single component set.
- Parameters:
- name - the name of the component set.
- path - the path of the directory where the component set resides.
- """
- MODULE_CLASS = ComponentModule
- def __init__(self, name, path):
- self.name = name
- self.basepath = path
- self.path = pjoin(path, INDICATOR + name)
- self.main_component = "__%s__" % name
- self._names = ()
- self._components = None
- @classmethod
- def from_parent(cls, parent, name=None):
- """Expects parent to be a module object or the __file__ attr."""
- if isinstance(parent, Module):
- if name is None:
- name = parent.__name__.split(".")[-1]
- parent = parent.__file__
- elif name is None:
- name = importing.extract_name(parent)
- parent = importing.find_source(parent)
- return cls(name, dirname(parent))
- def __iter__(self):
- self._pull()
- return iter(self._components.values())
- def __getitem__(self, key):
- return self._components[key]
- @property
- def names(self):
- if hasattr(self, "_names"):
- return self._names
- self._pull()
- names = self._names = tuple(self._components)
- return names
- def _pull(self):
- if self._components is not None:
- return
- filenames = os.listdir(self.path)
- components = {}
- name = "%s.py" % self.main_component
- if name in filenames:
- components[name] = ComponentModule(name, self.name, self.path)
- for filename in filenames:
- if filename.startswith("_"):
- continue
- name, ext = splitext(filename)
- if not builtins.isidentifier(name):
- continue
- if ext != "py":
- continue
- components[name] = ComponentModule(name, self.name, self.path)
- self._components = components
- def load_all(self, bind=None, attach=None, on_collision="fail"):
- self._pull()
- if on_collision is None:
- cls = dict
- elif on_collision == "fail":
- cls = builtins.WriteOnceDict
- else:
- raise TypeError("unrecognized on_collision: %s" % on_collision)
- if bind is None:
- ns = cls()
- else:
- ns = cls(bind)
- moduleclass = type
- names = self.names[:]
- if names.pop(self.main_component, False):
- module = self[self.main_component]
- module.load()
- try:
- ns.update(module.namespace)
- except KeyError, e:
- raise ImportError(e[0])
- for name in names:
- module = self[name]
- module.load()
- try:
- ns.update(module.namespace)
- except KeyError, e:
- raise ImportError(e[0])
- if bind:
- bind.update(ns)
- if attach:
- for name in self.names:
- attach[name] = self[name]
- return ns
- @classmethod
- def load_from_module(module, bind=True):
- if hasattr(module, "__dict__"):
- module = module.__dict__
- components = cls.from_parent(module["__file__"], module["__name__"])
- if bind:
- names = components.load_all(bind=module)
- module["__all__"] += tuple(names)
- else:
- components.load_all()