plugable: Specify plugins to import in API by module names

This change removes the automatic plugins sub-package magic and allows
specifying modules in addition to packages.

https://fedorahosted.org/freeipa/ticket/3090

Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
This commit is contained in:
Jan Cholasta
2015-06-15 11:02:07 +00:00
parent 7715d5bb04
commit 481f8ddaa3
3 changed files with 46 additions and 46 deletions

View File

@@ -900,15 +900,15 @@ else:
class API(plugable.API): class API(plugable.API):
def __init__(self, allowed): def __init__(self, allowed):
super(API, self).__init__(allowed, ['ipalib']) super(API, self).__init__(allowed, ['ipalib.plugins.*'])
def bootstrap(self, parser=None, **overrides): def bootstrap(self, parser=None, **overrides):
super(API, self).bootstrap(parser, **overrides) super(API, self).bootstrap(parser, **overrides)
if self.env.in_server: if self.env.in_server:
self.packages.append('ipaserver') self.modules.append('ipaserver.plugins.*')
if self.env.context in ('installer', 'updates'): if self.env.context in ('installer', 'updates'):
self.packages.append('ipaserver/install/plugins') self.modules.append('ipaserver.install.plugins.*')
def create_api(mode='dummy'): def create_api(mode='dummy'):

View File

@@ -36,6 +36,7 @@ import optparse
import errors import errors
import textwrap import textwrap
import collections import collections
import importlib
from config import Env from config import Env
import text import text
@@ -79,7 +80,7 @@ def find_modules_in_dir(src_dir):
module = name[:-len(suffix)] module = name[:-len(suffix)]
if module == '__init__': if module == '__init__':
continue continue
yield (module, pyfile) yield module
class Registry(object): class Registry(object):
@@ -429,9 +430,9 @@ class API(DictProxy):
register = Registrar() register = Registrar()
def __init__(self, allowed, packages): def __init__(self, allowed, modules):
self.__plugins = {base: {} for base in allowed} self.__plugins = {base: {} for base in allowed}
self.packages = packages self.modules = modules
self.__d = dict() self.__d = dict()
self.__done = set() self.__done = set()
self.env = Env() self.env = Env()
@@ -609,56 +610,55 @@ class API(DictProxy):
self.__do_if_not_done('bootstrap') self.__do_if_not_done('bootstrap')
if self.env.mode in ('dummy', 'unit_test'): if self.env.mode in ('dummy', 'unit_test'):
return return
for package in self.packages: for module in self.modules:
self.import_plugins(package) self.import_plugins(module)
for klass, kwargs in self.register.iteritems(): for klass, kwargs in self.register.iteritems():
self.add_plugin(klass, **kwargs) self.add_plugin(klass, **kwargs)
# FIXME: This method has no unit test # FIXME: This method has no unit test
def import_plugins(self, package): def import_plugins(self, module):
""" """
Import modules in ``plugins`` sub-package of ``package``. Import plugins from ``module``.
:param module: Name of the module to import. This might be a wildcard
in the form ```package.*``` in which case all modules
from the given package are loaded.
""" """
package = package.replace(os.path.sep, '.') if module.endswith('.*'):
subpackage = '%s.plugins' % package subpackage = module[:-2]
try: try:
parent = __import__(package) plugins = importlib.import_module(subpackage)
parts = package.split('.')[1:]
if parts:
for part in parts:
if part == 'plugins':
plugins = subpackage.plugins
subpackage = plugins.__name__
break
subpackage = parent.__getattribute__(part)
parent = subpackage
else:
plugins = __import__(subpackage).plugins
except ImportError, e: except ImportError, e:
self.log.error( self.log.error("cannot import plugins sub-package %s: %s",
'cannot import plugins sub-package %s: %s', subpackage, e subpackage, e)
) raise
raise e package, dot, part = subpackage.rpartition('.')
parent = sys.modules[package]
parent_dir = path.dirname(path.abspath(parent.__file__)) parent_dir = path.dirname(path.abspath(parent.__file__))
plugins_dir = path.dirname(path.abspath(plugins.__file__)) plugins_dir = path.dirname(path.abspath(plugins.__file__))
if parent_dir == plugins_dir: if parent_dir == plugins_dir:
raise errors.PluginsPackageError( raise errors.PluginsPackageError(
name=subpackage, file=plugins.__file__ name=subpackage, file=plugins.__file__
) )
self.log.debug('importing all plugin modules in %r...', plugins_dir)
for (name, pyfile) in find_modules_in_dir(plugins_dir): self.log.debug("importing all plugin modules in %s...", subpackage)
fullname = '%s.%s' % (subpackage, name) modules = find_modules_in_dir(plugins_dir)
self.log.debug('importing plugin module %r', pyfile) modules = ['.'.join((subpackage, name)) for name in modules]
else:
modules = [module]
for name in modules:
self.log.debug("importing plugin module %s", name)
try: try:
__import__(fullname) importlib.import_module(name)
except errors.SkipPluginModule, e: except errors.SkipPluginModule, e:
self.log.debug( self.log.debug("skipping plugin module %s: %s", name, e.reason)
'skipping plugin module %s: %s', fullname, e.reason
)
except StandardError, e: except StandardError, e:
if self.env.startup_traceback: if self.env.startup_traceback:
import traceback import traceback
self.log.error('could not load plugin module %r\n%s', pyfile, traceback.format_exc()) self.log.error("could not load plugin module %s\n%s", name,
traceback.format_exc())
raise raise
def add_plugin(self, klass, override=False): def add_plugin(self, klass, override=False):

View File

@@ -121,7 +121,7 @@ class Advice(Plugin):
raise NotImplementedError raise NotImplementedError
advise_api = API((Advice,), ('ipaserver/advise/plugins',)) advise_api = API((Advice,), ('ipaserver.advise.plugins.*',))
class IpaAdvise(admintool.AdminTool): class IpaAdvise(admintool.AdminTool):