summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipalib/errors.py3
-rw-r--r--ipalib/plugable.py80
-rw-r--r--ipalib/tests/test_plugable.py31
3 files changed, 38 insertions, 76 deletions
diff --git a/ipalib/errors.py b/ipalib/errors.py
index e9f78477..b86ffcdb 100644
--- a/ipalib/errors.py
+++ b/ipalib/errors.py
@@ -53,9 +53,6 @@ class SetError(IPAError):
-
-
-
class RegistrationError(IPAError):
"""
Base class for errors that occur during plugin registration.
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index 43dd50ca..bf0f52b4 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -25,29 +25,6 @@ import re
import inspect
import errors
-EXPORT_FLAG = 'exported'
-
-def export(obj):
- """
- Decorator function to set the 'exported' flag to True.
-
- For example:
-
- >>> @export
- >>> def my_func():
- >>> pass
- >>> assert my_func.exported is True
- """
- assert not hasattr(obj, EXPORT_FLAG)
- setattr(obj, EXPORT_FLAG, True)
- return obj
-
-def is_exported(obj):
- """
- Returns True if `obj` as an 'exported' attribute that is True.
- """
- return getattr(obj, EXPORT_FLAG, False) is True
-
def to_cli(name):
"""
@@ -192,53 +169,42 @@ class Proxy(ReadOnly):
class Proxy2(ReadOnly):
- def __init__(self, base, target):
+ __slots__ = (
+ 'base',
+ 'name',
+ '__target',
+ )
+ def __init__(self, base, target, name_attr='name'):
if not inspect.isclass(base):
raise TypeError('arg1 must be a class, got %r' % base)
if not isinstance(target, base):
raise ValueError('arg2 must be instance of arg1, got %r' % target)
object.__setattr__(self, 'base', base)
object.__setattr__(self, '_Proxy2__target', target)
- object.__setattr__(self, '_Proxy2__props', dict())
-
- names = [] # The names of exported attributes
- # This matches implied property fget methods like '_get_user'
- r = re.compile(r'^_get_([a-z][_a-z0-9]*[a-z0-9])$')
- for name in dir(base):
- match = r.match(name)
- if name != '__call__' and name.startswith('_') and not match:
- continue # Skip '_SomeClass__private', etc.
- base_attr = getattr(base, name)
- if is_exported(base_attr):
- target_attr = getattr(target, name)
- assert not hasattr(self, name), 'Cannot override %r' % name
- object.__setattr__(self, name, target_attr)
- names.append(name)
- if match:
- assert callable(target_attr), '%s must be callable' % name
- key = match.group(1)
- assert not hasattr(self, key), (
- '%r cannot override %r' % (name, key)
- )
- self.__props[key] = target_attr
- object.__setattr__(self, '_Proxy2__names', tuple(names))
- def __call__(self, *args, **kw):
- return self.__target(*args, **kw)
+ # Check base.public
+ assert type(self.base.public) is frozenset
+
+ # Check name
+ object.__setattr__(self, 'name', getattr(target, name_attr))
+ check_identifier(self.name)
def __iter__(self):
- for name in self.__names:
+ for name in sorted(self.base.public):
yield name
- def __getattr__(self, name):
- if name in self.__props:
- return self.__props[name]()
- raise AttributeError(name)
-
-
-
+ def __getitem__(self, key):
+ if key in self.base.public:
+ return getattr(self.__target, key)
+ raise KeyError('no proxy attribute %r' % key)
+ def __getattr__(self, name):
+ if name in self.base.public:
+ return getattr(self.__target, name)
+ raise AttributeError('no proxy attribute %r' % name)
+ def __call__(self, *args, **kw):
+ return self['__call__'](*args, **kw)
class NameSpace(ReadOnly):
diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py
index 40e98ed3..383e068e 100644
--- a/ipalib/tests/test_plugable.py
+++ b/ipalib/tests/test_plugable.py
@@ -170,23 +170,22 @@ def test_Proxy():
def test_Proxy2():
cls = plugable.Proxy2
- export = plugable.export
assert issubclass(cls, plugable.ReadOnly)
# Setup:
class base(object):
- @export
+ public = frozenset((
+ 'public_0',
+ 'public_1',
+ '__call__',
+ ))
+
def public_0(self):
return 'public_0'
- @export
def public_1(self):
return 'public_1'
- @export
- def _get_some_prop(self):
- return 'ya got it'
-
def __call__(self, caller):
return 'ya called it, %s.' % caller
@@ -197,7 +196,8 @@ def test_Proxy2():
return 'private_1'
class plugin(base):
- pass
+ name = 'user_add'
+ attr_name = 'add'
# Test that TypeError is raised when base is not a class:
raises(TypeError, cls, base(), None)
@@ -209,7 +209,8 @@ def test_Proxy2():
i = plugin()
p = cls(base, i)
assert read_only(p, 'base') is base
- assert list(p) == ['_get_some_prop', 'public_0', 'public_1']
+ assert read_only(p, 'name') is 'user_add'
+ assert list(p) == sorted(base.public)
# Test normal methods:
for n in xrange(2):
@@ -217,6 +218,7 @@ def test_Proxy2():
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)
@@ -224,14 +226,11 @@ def test_Proxy2():
value = 'ya called it, dude.'
assert i('dude') == value
assert p('dude') == value
+ assert callable(p)
- # Test implied property:
- fget = '_get_some_prop'
- name = 'some_prop'
- value = 'ya got it'
- assert getattr(i, fget)() == value
- assert getattr(p, fget)() == value
- assert getattr(p, name) == value
+ # Test name_attr='name' kw arg
+ i = plugin()
+ p = cls(base, i)