class Environment(Component, ComponentManager): """The environment loads plugins """ commands = ExtensionPoint(IShellCommandProvider) console_objects = ExtensionPoint(IShellConsoleObjectProvider) env_objects = ExtensionPoint(IEnvObjectProvider) def __init__(self, config=None, entry_point=None, plugins=None, logger=None, locals=None): """Initialize the Dustbowl environment. @param config: the absolute path to a configuration file. @param entry_point: The entry point used to locate plugins. @param plugins: a list of tuples containing paths in which to look for plugins and a boolean indicating whether or not plugins loaded from the specified path should be auto-enabled. @param logger: a Python logger instance. ``sys.path`` will be automatically added to the list of plugin directories. All entries of ``sys.path`` will not be auto-enabled. """ ComponentManager.__init__(self) self.setup_config(config) self.setup_log(logger) # Load plugins self.plugin_data = dict() if not plugins: plugins = list() self.load_modules(plugins, entry_point=entry_point) if locals: self.parent_locals = locals for provider in self.console_objects: for key, value in provider.get_console_objects(): self.add_console_object(key, value, provider.__class__.__name__) continue continue for provider in self.env_objects: for key, value in provider.get_env_objects(): self.add_env_object(key, value, provider.__class__.__name__) continue continue def component_activated(self, component): """Initialize additional member variables for components. Every component activated through the `Environment` object gets three member variables: `env` (the environment object), `config` (the environment configuration) and `log` (a logger object).""" component.env = self component.config = self.config component.log = self.log def is_component_enabled(self, cls): """Implemented to only allow activation of components that are not disabled in the configuration. This is called by the `ComponentManager` base class when a component is about to be activated. If this method returns false, the component does not get activated.""" if not isinstance(cls, basestring): component_name = (cls.__module__ + '.' + cls.__name__).lower() else: component_name = cls.lower() for key, value in self.plugin_data.iteritems(): if component_name == key or component_name.startswith(key + '.'): value['activated'] = True return value['loaded'] return False def setup_config(self, configpath): """Load the configuration file.""" self.config = Configuration(configpath) def setup_log(self, logger): """Initialize the logging sub-system.""" if logger: self.log = logger else: self.log = NullLogger() def is_enabled(self, module_name): """ Return whether a module is enabled in the config. """ for key, value in self.config.options('components'): k = key.lower() mod = module_name.lower() if mod == k or k.endswith('*') and mod.startswith(k[:-1]): return self.config.getbool('components', key) return False def load_modules(self, plugins=None, entry_point='dustbowl.modules'): """ Load plugins """ def _log_error(item, e): ue = format_exception(e) if isinstance(e, DistributionNotFound): self.log.debug('Skipping "%s": ("%s" not found)', item, ue) elif isinstance(e, VersionConflict): self.log.error('Skipping "%s": (version conflict "%s")', item, ue) elif isinstance(e, UnknownExtra): self.log.error('Skipping "%s": (unknown extra "%s")', item, ue) elif isinstance(e, ImportError): self.log.error('Skipping "%s": (can\'t import "%s")', item, ue) else: self.log.error('Skipping "%s": (error "%s")', item, ue) ws = pkg_resources.WorkingSet(sys.path) # Look for core modules ws.add_entry(os.path.dirname( pkg_resources.resource_filename(__name__, ''))) for entry in ws.iter_entry_points(entry_point): if entry.name.startswith('dustbowl.plugins'): entry_data = { 'entry' : entry, 'loaded' : False, 'activated' : False, 'auto_enable' : True, } self.plugin_data.setdefault(entry.name, entry_data) # Look for modules in sys.path for p in sys.path: ws.add_entry(p) for entry in ws.iter_entry_points(entry_point): entry_data = { 'entry' : entry, 'loaded' : False, 'activated' : False, 'auto_enable' : False, } self.plugin_data.setdefault(entry.name, entry_data) # Look for modules from plugin dir specified in the config or on the # command line distributions, errors = ws.find_plugins( pkg_resources.Environment(plugins)) for dist in distributions: self.log.debug('Found plugin %s at %s', dist, dist.location) ws.add(dist) for entry in ws.iter_entry_points(entry_point): entry_data = { 'entry' : entry, 'loaded' : False, 'activated' : False, 'auto_enable' : False, } self.plugin_data.setdefault(entry.name, entry_data) for entry_name, data in self.plugin_data.iteritems(): if data['auto_enable'] or self.is_enabled(entry_name): try: self.log.debug('Loading %s from %s', data['entry'].name, data['entry'].dist) # We need to make sure the distribution is on the path # before we can load it. data['entry'].dist.activate() data['entry'].load(require=True) data['loaded'] = True except (ImportError, DistributionNotFound, VersionConflict, UnknownExtra), e: # Print the last traceback to the debug buffer _log_error(data['entry'], e) continue for dist, e in errors.iteritems(): _log_error(dist, e)
def setup_log(self, logger): """Initialize the logging sub-system.""" if logger: self.log = logger else: self.log = NullLogger()