Source code for openalea.vpltk.catalog.catalog

# -*- python -*-
#
#       Plugin System for vpltk
#
#       OpenAlea.VPLTk: Virtual Plants Lab Toolkit
#
#       Copyright 2013 INRIA - CIRAD - INRA
#
#       File author(s): Guillaume Baty <guillaume.baty@inria.fr>
#
#       File contributor(s):
#
#       Distributed under the Cecill-C License.
#       See accompanying file LICENSE.txt or copy at
#           http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
#       OpenAlea WebSite : http://openalea.gforge.inria.fr
#
###############################################################################

import abc
import inspect
import pkg_resources
import site
import sys

from openalea.core.pkgmanager import UnknownPackageError
from openalea.vpltk.catalog.pluginmanager import PluginManager
from openalea.core.singleton import Singleton
from openalea.core.interface import IInterface

# TODO
# Write a short description of what do methods

[docs]class Catalog(object): __metaclass__ = Singleton def __init__(self, verbose=True): self.plugin_types = ('wralea', 'plugin', 'adapters', 'interfaces') self.groups = set() self.managers = {} self._services = {} self._interfaces = {} self._lowername = {} # list all path supporting python modules paths = site.getsitepackages() usersite = site.getusersitepackages() if isinstance(usersite, basestring): paths.append(usersite) elif isinstance(usersite, (tuple, list)): paths += list(usersite) paths += sys.path # scan all entry_point and list different groups for path in set(paths): distribs = pkg_resources.find_distributions(path) for distrib in distribs : for group in distrib.get_entry_map(): self.groups.add(group) self.groups = [group for group in self.groups] self.tags = self._clean_lst(self.groups) self._load_interfaces() def _clean_lst(self, lst): lst = [item for item in lst] for plugin_type in self.plugin_types : if plugin_type in lst : lst.remove(plugin_type) return lst def _load_interfaces(self): # load all interfaces defined in __plugin__.py files. for pl in self.factories(tags=['plugin', 'interfaces']): if inspect.isclass(pl) and issubclass(pl, IInterface): interface = pl for parent in inspect.getmro(interface): if hasattr(parent, 'name'): self._interfaces[parent.name] = parent self._lowername[parent.name.lower()[1:]] = parent.name
[docs] def interface(self, name): interface_id = self.interface_id(name) return self._interfaces.get(interface_id)
[docs] def interface_id(self, interface): if isinstance(interface, basestring): return interface elif inspect.isclass(interface) and issubclass(interface, IInterface): if hasattr(interface, 'name'): return interface.name else : return 'builtin:%s.%s' % (interface.__module__, interface.__name__) elif inspect.isclass(interface) : return interface.__name__ else: raise NotImplementedError
[docs] def interfaces(self, obj=None): if obj is None : return self._interfaces all_interfaces = set() # Check interfaces defined in openalea factories if hasattr(obj, '__interfaces__'): for interface in obj.__interfaces__: # Currently, interfaces can be defined as "string identifier" or directly using interface class # If interface class is used, get its "string identifier". interface_id = self.interface_id(interface) if interface_id in self._interfaces: # check if interface_id is yet known by Catalog # Search parent interfaces interface = self._interfaces[interface_id] for parent in inspect.getmro(interface): parent_id = self.interface_id(parent) all_interfaces.add(parent_id) else : # Cannot reach real interface class, so add it directly all_interfaces.add(self.interface_id(interface)) if not inspect.isclass(obj): obj = obj.__class__ # Check interfaces defined using traits.provides if hasattr(obj, '__implements__'): for interface in obj.__implements__.getBases(): all_interfaces.add(self.interface_id(interface)) return all_interfaces
[docs] def is_implementation(self, obj, interface): if interface is None : return True if isinstance(interface, abc.ABCMeta): return isinstance(obj, interface) else : return self.interface_id(interface) in self.interfaces(obj)
def _getplugin(self, plugin_type, interfaces, name, tags): lst = [] if plugin_type not in self.managers : manager = PluginManager(plugin_type=plugin_type) manager.init() self.managers[plugin_type] = manager else: manager = self.managers[plugin_type] for pkgname in manager.iterkeys() : try: factories = manager[pkgname] except UnknownPackageError : pass else: for factory in factories.itervalues(): if name and factory.name != name : continue if self.is_implementation(factory, interfaces): lst.append(factory) return lst
[docs] def factories(self, interfaces=None, name=None, tags=None, exclude_tags=None): """ :param exclude_tags: if tags is not specified, scan all tags except one defined in exclude_tags :param interfaces: by default do not check interfaces so return all factories TODO: tags and exclude_tags need to be clarified """ lst = [] if tags and exclude_tags: print 'tags and exclude_tags are mutually exclusive' if exclude_tags is None: exclude_tags = ['wralea'] if tags is None : tags = self.groups for tag in exclude_tags : if tag in tags : tags.remove(tag) # Scan standard entry_points : 1 entry_point = 1 object for tag in self._clean_lst(tags) : for ep in pkg_resources.iter_entry_points(tag, name) : if self.is_implementation(ep, interfaces): lst.append(ep) # Scan openalea entry_points : 1 entry_point = n factories (scan) for tag in self.plugin_types: if tag in tags : lst += self._getplugin(tag, interfaces, name, tags) return lst
[docs] def factory(self, interfaces=None, name=None, tags=None, exclude_tags=None): """ get factories matching given criteria (interfaces, name, tags). criterion=None means criterion is not used. If all criteria are None (default) it returns all factories. """ lst = self.factories(interfaces, name, tags, exclude_tags) if lst : return lst[0]
[docs] def create_service(self, object_factory, *args, **kargs): """ Create a service from object_factory. If object_factory is None, returns None. If this factory is called for the first time, instantiate it with args and kargs. Else, use previous instance. """ if object_factory is None : return None if object_factory in self._services : service = self._services[object_factory] else : service = object_factory.instantiate(*args, **kargs) self._services[object_factory] = service return service
[docs] def service(self, interfaces=None, name=None, tags=None, exclude_tags=None, args=None, kargs=None): """ args and kargs are not currently used but defined for future additional filters. For example "author='John Doe'". TODO: tags and exclude_tags need to be clarified """ if args is None : args = [] if kargs is None : kargs = {} if exclude_tags is None : exclude_tags = ['wralea'] object_factory = self.factory(interfaces, name, tags, exclude_tags) return self.create_service(object_factory)
[docs] def services(self, interfaces=None, name=None, tags=None, exclude_tags=None, args=None, kargs=None): """ See :meth:`Catalog.service <openalea.vpltk.catalog.Catalog.service>`. """ if args is None : args = [] if kargs is None : kargs = {} if exclude_tags is None : exclude_tags = ['wralea'] object_factories = self.factories(interfaces, name, tags, exclude_tags) services = [] for object_factory in object_factories : services.append(self.create_service(object_factory, *args, **kargs)) return services