Source code for taurus.core.util.plugin

#!/usr/bin/env python

# ###########################################################################
#
# This file is part of Taurus
#
# http://taurus-scada.org
#
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
#
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taurus 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
#
# ###########################################################################

"""
Utilities for plugin loading and selection
"""

__all__ = ["selectEntryPoints", "EntryPointAlike"]

import re
import pkg_resources
from re import fullmatch


[docs]class EntryPointAlike(object): """ A dummy EntryPoint substitute to be used with :func:`selectEntryPoints` for wrapping arbitrary objects and imitate the `name` and `load()` API of a :class:`pkg_resources.EntryPoint` instance. Pass an object and optionally a name to the constructor to get the wrapped `EntryPointAlike` instance. The `repr` of the object is used as the name if `name` arg is not provided. """ def __init__(self, obj, name=None): self._obj = obj if name is None: name = repr(obj) self.name = name
[docs] def load(self): """Returns the wrapped object""" return self._obj
[docs]def selectEntryPoints(group=None, include=(".*",), exclude=()): """ Selects and prioritizes entry points from an entry point group. The entry points are selected using their name. The selection is done by regex matching on the names: first the entry points whose name matches any of the patterns in `excluded` are discarded; then each pattern in `included` is used to select from the names of the remaining entry points (from highest to lowest priority). In this way, the entry points are selected in the order dictated by the `included` pattern list. If a pattern matches several names, these will be sorted alphabetically. If a name is matched by several patterns, it is only selected by the first pattern. For example, if there are the following registered entry point names: ['foo1', 'foo2', 'baz1', 'baz2', 'bar1', 'bar2'] And we use `exclude=("foo2",)` and `include=("bar2", "b.*1", "f.*")` , then the selection will be: ["bar2","bar1","baz1","foo1"] Note: apart from regex patterns (strings or compiled) the `include` list can also contain :class:`pkg_resources`EntryPoint`-like instances (more specifically, an object having `.name` and `.load()` members), in which case they are added directly to the selected list. If a member is something other than a pattern or an EntryPoint-like object, it will be wrapped in an :class:`EntryPointAlike` instance and also included in the selection. :param group: entry point group name from which the entry points are obtained. :type group: str :param include: The members of the tuple can either be patterns (both in the form of strings or of :class:`re.Pattern` objects), which will be matched against registered names in group; or EntryPoint-like objects which will be included as they are; or an arbitrary object which will be wrapped as an EntryPoint-like object before being included. Default is `(".*",)`, which matches all registered names in group and the sort is purely alphabetical. :type include: `tuple` :param exclude: Regexp patterns (either `str` or :class:`re.Pattern` objects) matching names to be excluded. Default is `()`, so no entry point is excluded. :type exclude: `tuple` :return: the selected entry points. :rtype: list of :class:`pkg_resources.EntryPoint` """ ret = [] remaining = list(pkg_resources.iter_entry_points(group)) # filter out the entry points whose name matches a exclude pattern for p in exclude: remaining = [e for e in remaining if not fullmatch(p, e.name)] # sort the remaining entry points alphabetically remaining.sort(key=lambda e: e.name) # fill the ret list with the entry points whose name matches a pattern in # the `include` tuple. The inclusion order follows the order of the # patterns in `include` (and alphabetically for a pattern that produces # multiple matches) for p in include: try: # check if it is a pattern string or pattern object p = re.compile(p) except TypeError: # if p is not an entry point -like object, create one if not hasattr(p, "name") or not hasattr(p, "load"): p = EntryPointAlike(p) # and add it directly ret.append(p) continue # if it is a pattern, match it against remaining entry points tmp = remaining remaining = [] for e in tmp: if fullmatch(p, e.name): ret.append(e) else: remaining.append(e) return ret