summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/plugable.py73
-rw-r--r--ipalib/tests/test_plugable.py71
2 files changed, 144 insertions, 0 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index ee65c516..43dd50ca 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -25,6 +25,29 @@ 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):
"""
@@ -168,6 +191,56 @@ class Proxy(ReadOnly):
)
+class Proxy2(ReadOnly):
+ def __init__(self, base, target):
+ 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)
+
+ def __iter__(self):
+ for name in self.__names:
+ yield name
+
+ def __getattr__(self, name):
+ if name in self.__props:
+ return self.__props[name]()
+ raise AttributeError(name)
+
+
+
+
+
+
+
class NameSpace(ReadOnly):
"""
A read-only namespace of (key, value) pairs that can be accessed
diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py
index ab9a8665..40e98ed3 100644
--- a/ipalib/tests/test_plugable.py
+++ b/ipalib/tests/test_plugable.py
@@ -168,6 +168,77 @@ def test_Proxy():
assert c.name == 'do_a_thing'
+def test_Proxy2():
+ cls = plugable.Proxy2
+ export = plugable.export
+ assert issubclass(cls, plugable.ReadOnly)
+
+ # Setup:
+ class base(object):
+ @export
+ 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
+
+ def private_0(self):
+ return 'private_0'
+
+ def private_1(self):
+ return 'private_1'
+
+ class plugin(base):
+ pass
+
+ # Test that TypeError is raised when base is not a class:
+ raises(TypeError, cls, base(), None)
+
+ # Test that ValueError is raised when target is not instance of base:
+ raises(ValueError, cls, base, object())
+
+ # Test with correct arguments:
+ i = plugin()
+ p = cls(base, i)
+ assert read_only(p, 'base') is base
+ assert list(p) == ['_get_some_prop', 'public_0', 'public_1']
+
+ # 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 getattr(i, priv)() == priv
+ assert not hasattr(p, priv)
+
+ # Test __call__:
+ value = 'ya called it, dude.'
+ assert i('dude') == value
+ assert p('dude') == value
+
+ # 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
+
+
+
+
+
+
+
def test_Registrar():
class Base1(object):
pass