diff options
Diffstat (limited to 'src/mailman/utilities/plugins.py')
| -rw-r--r-- | src/mailman/utilities/plugins.py | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/mailman/utilities/plugins.py b/src/mailman/utilities/plugins.py new file mode 100644 index 000000000..1a8c6d461 --- /dev/null +++ b/src/mailman/utilities/plugins.py @@ -0,0 +1,77 @@ +# Copyright (C) 2009-2017 by the Free Software Foundation, Inc. +# +# This file is part of GNU Mailman. +# +# GNU Mailman is free software: you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along with +# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. + +"""Plugin utilities.""" + +from lazr.config import as_boolean +from mailman.config import config +from mailman.utilities.modules import find_components +from pkg_resources import resource_isdir +from public import public + + +@public +def find_pluggable_components(subpackage, interface): + """Find components which conform to a given interface, in subpackage. + + :param subpackage: Mailman subpackage to search. The search is also + done inside the corresponding plugins subpackages. + :type subpackage: string + :param interface: The interface that returned objects must conform to. + :type interface: `Interface` + :return: The sequence of matching components. + :rtype: objects implementing `interface` + """ + yield from find_components('mailman.' + subpackage, interface) + package_roots = [plugin.path + for name, plugin in config.plugin_configs + if as_boolean(plugin.enable) and plugin.path] + for package_root in package_roots: + package = package_root + '.' + subpackage + if resource_isdir(package_root, subpackage): + yield from find_components(package, interface) + + +@public +def add_pluggable_components(subpackage, interface, mapping): + """Add components to a given mapping. + + Similarly to `find_pluggable_components()` this inspects all modules in a + given mailman and plugin's subpackage looking for objects that conform to + a given interface. All such found objects (unless decorated with + `@abstract_component`) are added to the given mapping, keyed by the + object's `.name` attribute, which is required. It is a fatal error if + that key already exists in the mapping. + + :param subpackage: The subpackage path to search. + :type subpackage: string + :param interface: The interface that returned objects must conform to. + Objects found must have a `.name` attribute containing a unique + string. + :type interface: `Interface` + :param mapping: The mapping to add the found components to. + :type mapping: A dict-like mapping. This only needs to support + containment tests (e.g. `in` and `not in`) and `__setitem__()`. + :raises RuntimeError: when a duplicate key is found. + """ + for component_class in find_pluggable_components(subpackage, interface): + component = component_class() + if component.name in mapping: + raise RuntimeError( # pragma: nocover + 'Duplicate key "{}" found in {}; previously {}'.format( + component.name, component, mapping[component.name])) + mapping[component.name] = component |
