summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/plugins/base.py')
-rw-r--r--sleekxmpp/plugins/base.py360
1 files changed, 0 insertions, 360 deletions
diff --git a/sleekxmpp/plugins/base.py b/sleekxmpp/plugins/base.py
deleted file mode 100644
index 67675908..00000000
--- a/sleekxmpp/plugins/base.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# -*- encoding: utf-8 -*-
-
-"""
- sleekxmpp.plugins.base
- ~~~~~~~~~~~~~~~~~~~~~~
-
- This module provides XMPP functionality that
- is specific to client connections.
-
- Part of SleekXMPP: The Sleek XMPP Library
-
- :copyright: (c) 2012 Nathanael C. Fritz
- :license: MIT, see LICENSE for more details
-"""
-
-import sys
-import copy
-import logging
-import threading
-
-
-if sys.version_info >= (3, 0):
- unicode = str
-
-
-log = logging.getLogger(__name__)
-
-
-#: Associate short string names of plugins with implementations. The
-#: plugin names are based on the spec used by the plugin, such as
-#: `'xep_0030'` for a plugin that implements XEP-0030.
-PLUGIN_REGISTRY = {}
-
-#: In order to do cascading plugin disabling, reverse dependencies
-#: must be tracked.
-PLUGIN_DEPENDENTS = {}
-
-#: Only allow one thread to manipulate the plugin registry at a time.
-REGISTRY_LOCK = threading.RLock()
-
-
-class PluginNotFound(Exception):
- """Raised if an unknown plugin is accessed."""
-
-
-def register_plugin(impl, name=None):
- """Add a new plugin implementation to the registry.
-
- :param class impl: The plugin class.
-
- The implementation class must provide a :attr:`~BasePlugin.name`
- value that will be used as a short name for enabling and disabling
- the plugin. The name should be based on the specification used by
- the plugin. For example, a plugin implementing XEP-0030 would be
- named `'xep_0030'`.
- """
- if name is None:
- name = impl.name
- with REGISTRY_LOCK:
- PLUGIN_REGISTRY[name] = impl
- if name not in PLUGIN_DEPENDENTS:
- PLUGIN_DEPENDENTS[name] = set()
- for dep in impl.dependencies:
- if dep not in PLUGIN_DEPENDENTS:
- PLUGIN_DEPENDENTS[dep] = set()
- PLUGIN_DEPENDENTS[dep].add(name)
-
-
-def load_plugin(name, module=None):
- """Find and import a plugin module so that it can be registered.
-
- This function is called to import plugins that have selected for
- enabling, but no matching registered plugin has been found.
-
- :param str name: The name of the plugin. It is expected that
- plugins are in packages matching their name,
- even though the plugin class name does not
- have to match.
- :param str module: The name of the base module to search
- for the plugin.
- """
- try:
- if not module:
- try:
- module = 'sleekxmpp.plugins.%s' % name
- __import__(module)
- mod = sys.modules[module]
- except ImportError:
- module = 'sleekxmpp.features.%s' % name
- __import__(module)
- mod = sys.modules[module]
- elif isinstance(module, (str, unicode)):
- __import__(module)
- mod = sys.modules[module]
- else:
- mod = module
-
- # Add older style plugins to the registry.
- if hasattr(mod, name):
- plugin = getattr(mod, name)
- if hasattr(plugin, 'xep') or hasattr(plugin, 'rfc'):
- plugin.name = name
- # Mark the plugin as an older style plugin so
- # we can work around dependency issues.
- plugin.old_style = True
- register_plugin(plugin, name)
- except ImportError:
- log.exception("Unable to load plugin: %s", name)
-
-
-class PluginManager(object):
- def __init__(self, xmpp, config=None):
- #: We will track all enabled plugins in a set so that we
- #: can enable plugins in batches and pull in dependencies
- #: without problems.
- self._enabled = set()
-
- #: Maintain references to active plugins.
- self._plugins = {}
-
- self._plugin_lock = threading.RLock()
-
- #: Globally set default plugin configuration. This will
- #: be used for plugins that are auto-enabled through
- #: dependency loading.
- self.config = config if config else {}
-
- self.xmpp = xmpp
-
- def register(self, plugin, enable=True):
- """Register a new plugin, and optionally enable it.
-
- :param class plugin: The implementation class of the plugin
- to register.
- :param bool enable: If ``True``, immediately enable the
- plugin after registration.
- """
- register_plugin(plugin)
- if enable:
- self.enable(plugin.name)
-
- def enable(self, name, config=None, enabled=None):
- """Enable a plugin, including any dependencies.
-
- :param string name: The short name of the plugin.
- :param dict config: Optional settings dictionary for
- configuring plugin behaviour.
- """
- top_level = False
- if enabled is None:
- enabled = set()
-
- with self._plugin_lock:
- if name not in self._enabled:
- enabled.add(name)
- self._enabled.add(name)
- if not self.registered(name):
- load_plugin(name)
-
- plugin_class = PLUGIN_REGISTRY.get(name, None)
- if not plugin_class:
- raise PluginNotFound(name)
-
- if config is None:
- config = self.config.get(name, None)
-
- plugin = plugin_class(self.xmpp, config)
- self._plugins[name] = plugin
- for dep in plugin.dependencies:
- self.enable(dep, enabled=enabled)
- plugin._init()
-
- if top_level:
- for name in enabled:
- if hasattr(self.plugins[name], 'old_style'):
- # Older style plugins require post_init()
- # to run just before stream processing begins,
- # so we don't call it here.
- pass
- self.plugins[name].post_init()
-
- def enable_all(self, names=None, config=None):
- """Enable all registered plugins.
-
- :param list names: A list of plugin names to enable. If
- none are provided, all registered plugins
- will be enabled.
- :param dict config: A dictionary mapping plugin names to
- configuration dictionaries, as used by
- :meth:`~PluginManager.enable`.
- """
- names = names if names else PLUGIN_REGISTRY.keys()
- if config is None:
- config = {}
- for name in names:
- self.enable(name, config.get(name, {}))
-
- def enabled(self, name):
- """Check if a plugin has been enabled.
-
- :param string name: The name of the plugin to check.
- :return: boolean
- """
- return name in self._enabled
-
- def registered(self, name):
- """Check if a plugin has been registered.
-
- :param string name: The name of the plugin to check.
- :return: boolean
- """
- return name in PLUGIN_REGISTRY
-
- def disable(self, name, _disabled=None):
- """Disable a plugin, including any dependent upon it.
-
- :param string name: The name of the plugin to disable.
- :param set _disabled: Private set used to track the
- disabled status of plugins during
- the cascading process.
- """
- if _disabled is None:
- _disabled = set()
- with self._plugin_lock:
- if name not in _disabled and name in self._enabled:
- _disabled.add(name)
- plugin = self._plugins.get(name, None)
- if plugin is None:
- raise PluginNotFound(name)
- for dep in PLUGIN_DEPENDENTS[name]:
- self.disable(dep, _disabled)
- plugin._end()
- if name in self._enabled:
- self._enabled.remove(name)
- del self._plugins[name]
-
- def __keys__(self):
- """Return the set of enabled plugins."""
- return self._plugins.keys()
-
- def __getitem__(self, name):
- """
- Allow plugins to be accessed through the manager as if
- it were a dictionary.
- """
- plugin = self._plugins.get(name, None)
- if plugin is None:
- raise PluginNotFound(name)
- return plugin
-
- def __iter__(self):
- """Return an iterator over the set of enabled plugins."""
- return self._plugins.__iter__()
-
- def __len__(self):
- """Return the number of enabled plugins."""
- return len(self._plugins)
-
-
-class BasePlugin(object):
-
- #: A short name for the plugin based on the implemented specification.
- #: For example, a plugin for XEP-0030 would use `'xep_0030'`.
- name = ''
-
- #: A longer name for the plugin, describing its purpose. For example,
- #: a plugin for XEP-0030 would use `'Service Discovery'` as its
- #: description value.
- description = ''
-
- #: Some plugins may depend on others in order to function properly.
- #: Any plugin names included in :attr:`~BasePlugin.dependencies` will
- #: be initialized as needed if this plugin is enabled.
- dependencies = set()
-
- #: The basic, standard configuration for the plugin, which may
- #: be overridden when initializing the plugin. The configuration
- #: fields included here may be accessed directly as attributes of
- #: the plugin. For example, including the configuration field 'foo'
- #: would mean accessing `plugin.foo` returns the current value of
- #: `plugin.config['foo']`.
- default_config = {}
-
- def __init__(self, xmpp, config=None):
- self.xmpp = xmpp
- if self.xmpp:
- self.api = self.xmpp.api.wrap(self.name)
-
- #: A plugin's behaviour may be configurable, in which case those
- #: configuration settings will be provided as a dictionary.
- self.config = copy.copy(self.default_config)
- if config:
- self.config.update(config)
-
- def __getattr__(self, key):
- """Provide direct access to configuration fields.
-
- If the standard configuration includes the option `'foo'`, then
- accessing `self.foo` should be the same as `self.config['foo']`.
- """
- if key in self.default_config:
- return self.config.get(key, None)
- else:
- return object.__getattribute__(self, key)
-
- def __setattr__(self, key, value):
- """Provide direct assignment to configuration fields.
-
- If the standard configuration includes the option `'foo'`, then
- assigning to `self.foo` should be the same as assigning to
- `self.config['foo']`.
- """
- if key in self.default_config:
- self.config[key] = value
- else:
- super(BasePlugin, self).__setattr__(key, value)
-
- def _init(self):
- """Initialize plugin state, such as registering event handlers.
-
- Also sets up required event handlers.
- """
- if self.xmpp is not None:
- self.xmpp.add_event_handler('session_bind', self.session_bind)
- if self.xmpp.session_bind_event.is_set():
- self.session_bind(self.xmpp.boundjid.full)
- self.plugin_init()
- log.debug('Loaded Plugin: %s', self.description)
-
- def _end(self):
- """Cleanup plugin state, and prepare for plugin removal.
-
- Also removes required event handlers.
- """
- if self.xmpp is not None:
- self.xmpp.del_event_handler('session_bind', self.session_bind)
- self.plugin_end()
- log.debug('Disabled Plugin: %s' % self.description)
-
- def plugin_init(self):
- """Initialize plugin state, such as registering event handlers."""
- pass
-
- def plugin_end(self):
- """Cleanup plugin state, and prepare for plugin removal."""
- pass
-
- def session_bind(self, jid):
- """Initialize plugin state based on the bound JID."""
- pass
-
- def post_init(self):
- """Initialize any cross-plugin state.
-
- Only needed if the plugin has circular dependencies.
- """
- pass
-
-
-base_plugin = BasePlugin