From 38a86adcdb78c1944c26a5ab8deddff619b33bcf Mon Sep 17 00:00:00 2001 From: J08nY Date: Wed, 31 May 2017 02:09:09 +0200 Subject: Add pluggable components. - Adds the notion of a 'plugin'. - A plugin has a package path and a flag specifying whether it's enabled or not. - Adds a find_pluggable_components function similar to the find_components one. This one dynamically searches not only the mailman package but all of plugins. - e.g. find_pluggable_components('rules', IRule) finds all IRule components in mailman.rules but also in example_plugin.rules for plugin names example_plugin. - Uses the find_pluggable_components function in place of find_components when searching for Rules, Handlers, Chains, EmailCommands, and Styles. --- src/mailman/utilities/plugins.py | 76 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/mailman/utilities/plugins.py (limited to 'src/mailman/utilities/plugins.py') diff --git a/src/mailman/utilities/plugins.py b/src/mailman/utilities/plugins.py new file mode 100644 index 000000000..a8449af31 --- /dev/null +++ b/src/mailman/utilities/plugins.py @@ -0,0 +1,76 @@ +# 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 . + +"""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 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 package: The subpackage path to search. + :type package: 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 in find_pluggable_components(subpackage, interface): + if component.name in mapping: + raise RuntimeError( + 'Duplicate key "{}" found in {}; previously {}'.format( + component.name, component, mapping[component.name])) + mapping[component.name] = component -- cgit v1.2.3-70-g09d2