diff options
author | Ryan Petrello <lists@ryanpetrello.com> | 2012-03-26 18:10:10 -0400 |
---|---|---|
committer | Ryan Petrello <lists@ryanpetrello.com> | 2012-03-28 13:24:15 -0400 |
commit | b18670228ffc303faae27b25584bc01388e7f145 (patch) | |
tree | 60d0a458ca68764926b959185dce5a23aac225e8 | |
parent | a7d4684ac5dc704b3fe8320618e71d462ee1209e (diff) | |
download | pecan-b18670228ffc303faae27b25584bc01388e7f145.tar.gz |
Working on extension support.
-rw-r--r-- | pecan/ext/__init__.py | 6 | ||||
-rw-r--r-- | pecan/extensions.py | 82 |
2 files changed, 88 insertions, 0 deletions
diff --git a/pecan/ext/__init__.py b/pecan/ext/__init__.py new file mode 100644 index 0000000..c768f9c --- /dev/null +++ b/pecan/ext/__init__.py @@ -0,0 +1,6 @@ +def install(): + from pecan.extensions import PecanExtensionImporter + PecanExtensionImporter().install() + +install() +del install diff --git a/pecan/extensions.py b/pecan/extensions.py new file mode 100644 index 0000000..b9b5e5a --- /dev/null +++ b/pecan/extensions.py @@ -0,0 +1,82 @@ +import sys +import pkg_resources +import inspect +import logging + +log = logging.getLogger(__name__) + + +class PecanExtensionMissing(Exception): + pass + + +class PecanExtensionImporter(object): + """ + Short circuits imports for extensions. + + This is used in combination with ``pecan.ext`` so that when a user does + ``from pecan.ext import foo``, it will attempt to map ``foo`` to a + registered setuptools entry point in some other (Pecan extension) project. + + Conversely, an extension developer may define an entry point in his + ``setup.py``, e.g., + + setup( + ... + entry_points=''' + [pecan.extension] + celery = pecancelery.lib.core + ''' + ) + + This is mostly for convenience and consistency. In this way, Pecan can + maintain an ecosystem of extensions that share a common namespace, + ``pecan.ext``, while still maintaining backwards compatability for simple + package names (e.g., ``pecancelery``). + """ + + extension_module = 'pecan.ext' + prefix = extension_module + '.' + + def install(self): + if self not in sys.meta_path: + sys.meta_path.append(self) + + def __eq__(self, b): + return self.__class__.__module__ == b.__class__.__module__ and \ + self.__class__.__name__ == b.__class__.__name__ + + def __ne__(self, b): + return not self.__eq__(b) + + def find_module(self, fullname, path=None): + if fullname.startswith(self.prefix): + return self + + def load_module(self, fullname): + if fullname in sys.modules: + return self + extname = fullname.split(self.prefix)[1] + module = self.find_module_for_extension(extname) + realname = module.__name__ + try: + __import__(realname) + except ImportError: + raise sys.exc_info() + module = sys.modules[fullname] = sys.modules[realname] + if '.' not in extname: + setattr(sys.modules[self.extension_module], extname, module) + return module + + def find_module_for_extension(self, name): + for ep in pkg_resources.iter_entry_points('pecan.extension'): + if ep.name != name: + continue + log.debug('%s loading extension %s', self.__class__.__name__, ep) + module = ep.load() + if not inspect.ismodule(module): + continue + return module + raise PecanExtensionMissing( + 'The `pecan.ext.%s` extension is not installed.' % name + ) |