summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2009-08-04 02:41:11 -0600
committerRob Crittenden <rcritten@redhat.com>2009-08-05 12:18:51 -0400
commitc0f558d98b46df6131b221b746e8dc54787225e7 (patch)
treec4e39d94ed0a913cf618f5d6764f848e890a2fc2
parentb7b9f9b6a66f75e838a3a60b105ac7d0f8197ed2 (diff)
downloadfreeipa-c0f558d98b46df6131b221b746e8dc54787225e7.tar.gz
freeipa-c0f558d98b46df6131b221b746e8dc54787225e7.tar.xz
freeipa-c0f558d98b46df6131b221b746e8dc54787225e7.zip
Removed PluginProxy and all its uses
-rw-r--r--ipalib/__init__.py4
-rw-r--r--ipalib/base.py8
-rw-r--r--ipalib/cli.py15
-rw-r--r--ipalib/frontend.py12
-rw-r--r--ipalib/plugable.py117
-rw-r--r--tests/test_ipalib/test_frontend.py2
-rw-r--r--tests/test_ipalib/test_plugable.py164
7 files changed, 35 insertions, 287 deletions
diff --git a/ipalib/__init__.py b/ipalib/__init__.py
index 4ad58d6e9..b21c30384 100644
--- a/ipalib/__init__.py
+++ b/ipalib/__init__.py
@@ -239,9 +239,7 @@ plugin you implement.
Backend plugins are much more free-form than command plugins. Aside from a
few reserved attribute names, you can define arbitrary public methods on your
-backend plugin (in contrast, frontend plugins get wrapped in a
-`plugable.PluginProxy`, which allow access to only specific attributes on the
-frontend plugin).
+backend plugin.
Here is a simple example:
diff --git a/ipalib/base.py b/ipalib/base.py
index e0951e41e..38b1e8f32 100644
--- a/ipalib/base.py
+++ b/ipalib/base.py
@@ -382,7 +382,7 @@ class NameSpace(ReadOnly):
examples, see the `plugable.API` and the `frontend.Command` classes.
"""
- def __init__(self, members, sort=True):
+ def __init__(self, members, sort=True, name_attr='name'):
"""
:param members: An iterable providing the members.
:param sort: Whether to sort the members by member name.
@@ -394,14 +394,14 @@ class NameSpace(ReadOnly):
self.__sort = sort
if sort:
self.__members = tuple(
- sorted(members, key=lambda m: m.name)
+ sorted(members, key=lambda m: getattr(m, name_attr))
)
else:
self.__members = tuple(members)
- self.__names = tuple(m.name for m in self.__members)
+ self.__names = tuple(getattr(m, name_attr) for m in self.__members)
self.__map = dict()
for member in self.__members:
- name = check_name(member.name)
+ name = check_name(getattr(member, name_attr))
if name in self.__map:
raise AttributeError(OVERRIDE_ERROR %
(self.__class__.__name__, name, self.__map[name], member)
diff --git a/ipalib/cli.py b/ipalib/cli.py
index 3258556b9..5f7822357 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -491,12 +491,15 @@ class help(frontend.Command):
_PLUGIN_BASE_MODULE = 'ipalib.plugins'
- def _get_command_module(self, cmd_plugin_proxy):
+ def _get_command_module(self, module):
"""
- Return bare module name where command represented by PluginProxy
- instance is defined. Return None if command isn't defined in
- a plugin module (we consider these commands to be builtins).
+ Return last part of ``module`` name, or ``None`` if module is this file.
+
+ For example:
"""
+ if module == __name__:
+ return
+ return module.split('.')[-1]
# get representation in the form of 'base_module.bare_module.command()'
r = repr(cmd_plugin_proxy)
# skip base module part and the following dot
@@ -517,7 +520,7 @@ class help(frontend.Command):
# build help topics
for c in self.Command():
- topic = self._get_command_module(c)
+ topic = self._get_command_module(c.module)
if topic:
self._topics.setdefault(topic, [0]).append(c)
# compute maximum length of command in topic
@@ -528,7 +531,7 @@ class help(frontend.Command):
# compute maximum topic length
self._mtl = max(
- len(s) for s in (self._topics.keys() + self._builtins)
+ len(s) for s in (self._topics.keys() + [c.name for c in self._builtins])
)
super(help, self).finalize()
diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index 2a5c5311e..5ee703812 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -352,7 +352,7 @@ class Command(HasParam):
>>> list(api.Command)
['my_command']
>>> api.Command.my_command # doctest:+ELLIPSIS
- PluginProxy(Command, ...my_command())
+ ipalib.frontend.my_command()
"""
__public__ = frozenset((
@@ -752,10 +752,10 @@ class Object(HasParam):
def set_api(self, api):
super(Object, self).set_api(api)
self.methods = NameSpace(
- self.__get_attrs('Method'), sort=False
+ self.__get_attrs('Method'), sort=False, name_attr='attr_name'
)
self.properties = NameSpace(
- self.__get_attrs('Property'), sort=False
+ self.__get_attrs('Property'), sort=False, name_attr='attr_name'
)
self._create_param_namespace('params')
pkeys = filter(lambda p: p.primary_key, self.params())
@@ -798,9 +798,9 @@ class Object(HasParam):
return
namespace = self.api[name]
assert type(namespace) is NameSpace
- for proxy in namespace(): # Equivalent to dict.itervalues()
- if proxy.obj_name == self.name:
- yield proxy.__clone__('attr_name')
+ for plugin in namespace(): # Equivalent to dict.itervalues()
+ if plugin.obj_name == self.name:
+ yield plugin
def get_params(self):
"""
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index ba7ac711b..d94ff7524 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -308,118 +308,6 @@ class Plugin(ReadOnly):
)
-class PluginProxy(SetProxy):
- """
- Allow access to only certain attributes on a `Plugin`.
-
- Think of a proxy as an agreement that "I will have at most these
- attributes". This is different from (although similar to) an interface,
- which can be thought of as an agreement that "I will have at least these
- attributes".
- """
-
- __slots__ = (
- '__base',
- '__target',
- '__name_attr',
- '__public__',
- 'name',
- 'doc',
- )
-
- def __init__(self, base, target, name_attr='name'):
- """
- :param base: A subclass of `Plugin`.
- :param target: An instance ``base`` or a subclass of ``base``.
- :param name_attr: The name of the attribute on ``target`` from which
- to derive ``self.name``.
- """
- if not inspect.isclass(base):
- raise TypeError(
- '`base` must be a class, got %r' % base
- )
- if not isinstance(target, base):
- raise ValueError(
- '`target` must be an instance of `base`, got %r' % target
- )
- self.__base = base
- self.__target = target
- self.__name_attr = name_attr
- if hasattr(type(target), '__public__'):
- self.__public__ = type(target).__public__
- else:
- self.__public__ = base.__public__
- self.name = getattr(target, name_attr)
- self.doc = target.doc
- assert type(self.__public__) is frozenset
- super(PluginProxy, self).__init__(self.__public__)
-
- def implements(self, arg):
- """
- Return True if plugin being proxied implements ``arg``.
-
- This method simply calls the corresponding `Plugin.implements`
- classmethod.
-
- Unlike `Plugin.implements`, this is not a classmethod as a
- `PluginProxy` can only implement anything as an instance.
- """
- return self.__base.implements(arg)
-
- def __clone__(self, name_attr):
- """
- Return a `PluginProxy` instance similar to this one.
-
- The new `PluginProxy` returned will be identical to this one except
- the proxy name might be derived from a different attribute on the
- target `Plugin`. The same base and target will be used.
- """
- return self.__class__(self.__base, self.__target, name_attr)
-
- def __getitem__(self, key):
- """
- Return attribute named ``key`` on target `Plugin`.
-
- If this proxy allows access to an attribute named ``key``, that
- attribute will be returned. If access is not allowed,
- KeyError will be raised.
- """
- if key in self.__public__:
- return getattr(self.__target, key)
- raise KeyError('no public attribute %s.%s' % (self.name, key))
-
- def __getattr__(self, name):
- """
- Return attribute named ``name`` on target `Plugin`.
-
- If this proxy allows access to an attribute named ``name``, that
- attribute will be returned. If access is not allowed,
- AttributeError will be raised.
- """
- if name in self.__public__:
- return getattr(self.__target, name)
- raise AttributeError('no public attribute %s.%s' % (self.name, name))
-
- def __call__(self, *args, **kw):
- """
- Call target `Plugin` and return its return value.
-
- If `__call__` is not an attribute this proxy allows access to,
- KeyError is raised.
- """
- return self['__call__'](*args, **kw)
-
- def __repr__(self):
- """
- Return a Python expression that could create this instance.
- """
- return '%s(%s, %r)' % (
- self.__class__.__name__,
- self.__base.__name__,
- self.__target,
- )
-
-
class Registrar(DictProxy):
"""
Collects plugin classes as they are registered.
@@ -734,10 +622,7 @@ class API(DictProxy):
p = plugins[klass]
assert base not in p.bases
p.bases.append(base)
- if base.__proxy__:
- yield PluginProxy(base, p.instance)
- else:
- yield p.instance
+ yield p.instance
for name in self.register:
base = self.register[name]
diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py
index 170891eea..385219d04 100644
--- a/tests/test_ipalib/test_frontend.py
+++ b/tests/test_ipalib/test_frontend.py
@@ -666,7 +666,7 @@ class test_Object(ClassChecker):
assert attr is getattr(namespace, attr_name)
assert attr.obj_name == 'user'
assert attr.attr_name == attr_name
- assert attr.name == attr_name
+ assert attr.name == '%s_%s' % ('user', attr_name)
# Test params instance attribute
o = self.cls()
diff --git a/tests/test_ipalib/test_plugable.py b/tests/test_ipalib/test_plugable.py
index 6f0cc297d..ec1ef3e00 100644
--- a/tests/test_ipalib/test_plugable.py
+++ b/tests/test_ipalib/test_plugable.py
@@ -377,128 +377,6 @@ class test_Plugin(ClassChecker):
assert e.argv == ('/bin/false',)
-class test_PluginProxy(ClassChecker):
- """
- Test the `ipalib.plugable.PluginProxy` class.
- """
- _cls = plugable.PluginProxy
-
- def test_class(self):
- """
- Test the `ipalib.plugable.PluginProxy` class.
- """
- assert self.cls.__bases__ == (plugable.SetProxy,)
-
- def test_proxy(self):
- """
- Test proxy behaviour of `ipalib.plugable.PluginProxy` instance.
- """
- # Setup:
- class base(object):
- __public__ = frozenset((
- 'public_0',
- 'public_1',
- '__call__',
- ))
-
- def public_0(self):
- return 'public_0'
-
- def public_1(self):
- return 'public_1'
-
- def __call__(self, caller):
- return 'ya called it, %s.' % caller
-
- def private_0(self):
- return 'private_0'
-
- def private_1(self):
- return 'private_1'
-
- class plugin(base):
- name = 'user_add'
- attr_name = 'add'
- doc = 'add a new user'
-
- # Test that TypeError is raised when base is not a class:
- raises(TypeError, self.cls, base(), None)
-
- # Test that ValueError is raised when target is not instance of base:
- raises(ValueError, self.cls, base, object())
-
- # Test with correct arguments:
- i = plugin()
- p = self.cls(base, i)
- assert read_only(p, 'name') is plugin.name
- assert read_only(p, 'doc') == plugin.doc
- assert list(p) == sorted(base.__public__)
-
- # Test normal methods:
- for n in xrange(2):
- pub = 'public_%d' % n
- priv = 'private_%d' % n
- assert getattr(i, pub)() == pub
- assert getattr(p, pub)() == pub
- assert hasattr(p, pub)
- assert getattr(i, priv)() == priv
- assert not hasattr(p, priv)
-
- # Test __call__:
- value = 'ya called it, dude.'
- assert i('dude') == value
- assert p('dude') == value
- assert callable(p)
-
- # Test name_attr='name' kw arg
- i = plugin()
- p = self.cls(base, i, 'attr_name')
- assert read_only(p, 'name') == 'add'
-
- def test_implements(self):
- """
- Test the `ipalib.plugable.PluginProxy.implements` method.
- """
- class base(object):
- __public__ = frozenset()
- name = 'base'
- doc = 'doc'
- @classmethod
- def implements(cls, arg):
- return arg + 7
-
- class sub(base):
- @classmethod
- def implements(cls, arg):
- """
- Defined to make sure base.implements() is called, not
- target.implements()
- """
- return arg
-
- o = sub()
- p = self.cls(base, o)
- assert p.implements(3) == 10
-
- def test_clone(self):
- """
- Test the `ipalib.plugable.PluginProxy.__clone__` method.
- """
- class base(object):
- __public__ = frozenset()
- class sub(base):
- name = 'some_name'
- doc = 'doc'
- label = 'another_name'
-
- p = self.cls(base, sub())
- assert read_only(p, 'name') == 'some_name'
- c = p.__clone__('label')
- assert isinstance(c, self.cls)
- assert c is not p
- assert read_only(c, 'name') == 'another_name'
-
-
def test_Registrar():
"""
Test the `ipalib.plugable.Registrar` class
@@ -682,50 +560,34 @@ class test_API(ClassChecker):
assert api.isdone('bootstrap') is True
assert api.isdone('finalize') is True
- def get_base(b):
+ def get_base_name(b):
return 'base%d' % b
- def get_plugin(b, p):
+
+ def get_plugin_name(b, p):
return 'base%d_plugin%d' % (b, p)
for b in xrange(2):
- base_name = get_base(b)
+ base_name = get_base_name(b)
+ base = locals()[base_name]
ns = getattr(api, base_name)
assert isinstance(ns, plugable.NameSpace)
assert read_only(api, base_name) is ns
assert len(ns) == 3
for p in xrange(3):
- plugin_name = get_plugin(b, p)
- proxy = ns[plugin_name]
- assert isinstance(proxy, plugable.PluginProxy)
- assert proxy.name == plugin_name
- assert read_only(ns, plugin_name) is proxy
- assert read_only(proxy, 'method')(7) == 7 + b
+ plugin_name = get_plugin_name(b, p)
+ plugin = locals()[plugin_name]
+ inst = ns[plugin_name]
+ assert isinstance(inst, base)
+ assert isinstance(inst, plugin)
+ assert inst.name == plugin_name
+ assert read_only(ns, plugin_name) is inst
+ assert inst.method(7) == 7 + b
# Test that calling finilize again raises AssertionError:
e = raises(StandardError, api.finalize)
assert str(e) == 'API.finalize() already called', str(e)
- # Test with base class that doesn't request a proxy
- class NoProxy(plugable.Plugin):
- __proxy__ = False
- api = plugable.API(NoProxy)
- api.env.mode = 'unit_test'
- class plugin0(NoProxy):
- pass
- api.register(plugin0)
- class plugin1(NoProxy):
- pass
- api.register(plugin1)
- api.finalize()
- names = ['plugin0', 'plugin1']
- assert list(api.NoProxy) == names
- for name in names:
- plugin = api.NoProxy[name]
- assert getattr(api.NoProxy, name) is plugin
- assert isinstance(plugin, plugable.Plugin)
- assert not isinstance(plugin, plugable.PluginProxy)
-
def test_bootstrap(self):
"""
Test the `ipalib.plugable.API.bootstrap` method.