Created
April 2, 2011 01:11
-
-
Save caryhaynie/899120 to your computer and use it in GitHub Desktop.
Python module to provide plugin support via a simple but effective import hook.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python | |
| import imp | |
| import os | |
| import os.path | |
| import sys | |
| # get the platform-specific list of possible module suffixes. | |
| MODULE_DESC = imp.get_suffixes() | |
| class PluginLoader(object): | |
| """ PluginLoader intercepts python import calls and allows a plugin directory to be mapped | |
| to a specific top-level package. | |
| """ | |
| def __init__(self, plugin_root, plugin_dir): | |
| self._plugin_root = plugin_root | |
| self._plugin_dir = plugin_dir | |
| # add a fake empty module. | |
| sys.modules[self._plugin_root] = imp.new_module(self._plugin_root) | |
| sys.modules[self._plugin_root].__file__ = "<plugin pseudo-package '%s'>" % self._plugin_root | |
| sys.modules[self._plugin_root].__path__ = [] | |
| def _real_name(self, name): | |
| return name[len(self._plugin_root) + 1:] | |
| def _fs_path(self, name): | |
| return os.path.join(self._plugin_dir, self._real_name(name)) | |
| def _real_path(self, name): | |
| path = self._fs_path(name) | |
| # test to see if this is a package or a module. | |
| if self._is_package(path): | |
| return path | |
| else: | |
| return "%s%s" % (path, self._get_suffix(path)) | |
| def _is_package(self, path): | |
| return os.path.isdir(path) | |
| def _get_suffix(self, path): | |
| for suffix, args, type in MODULE_DESC: | |
| test_path = "%s%s" % (path, suffix) | |
| if os.path.exists(test_path): | |
| return suffix | |
| return "" | |
| def _get_desc_for_suffix(self, suffix): | |
| for index, desc in enumerate(MODULE_DESC): | |
| if desc[0] == suffix: | |
| return MODULE_DESC[index] | |
| def find_module(self, fullname, path=None): | |
| # we need to check here to make sure files actually exist, other wise the import mechanism | |
| # breaks badly inside the plugins. | |
| if fullname.startswith(self._plugin_root) and os.path.exists(self._real_path(fullname)): | |
| return self | |
| else: | |
| return None | |
| def load_module(self, fullname): | |
| real_name = self._real_name(fullname) | |
| real_path = self._real_path(fullname) | |
| # don't reimport the module if it's already been imported. That's reload()'s job. | |
| # need to use fullname here instead of real_name, so that reload() works correctly. | |
| if fullname in sys.modules: | |
| return sys.modules[fullname] | |
| try: | |
| if not self._is_package(real_path): | |
| suffix = self._get_suffix(self._fs_path(fullname)) | |
| desc = self._get_desc_for_suffix(suffix) | |
| return imp.load_module(fullname, open(real_path, desc[1]), real_path, desc) | |
| else: | |
| desc = ("", "", imp.PKG_DIRECTORY) | |
| mod = imp.load_module(fullname, None, "", desc) | |
| exec open(os.path.join(real_path, "__init__.py"), "rb").read() in mod.__dict__ | |
| mod.__path__ = [real_path] | |
| return mod | |
| except IOError: | |
| raise ImportError("No module named %s" % real_name) | |
| def map_plugins(plugin_root, plugin_dir): | |
| sys.meta_path.append(PluginLoader(plugin_root, plugin_dir)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment