From f13f1226b4b798fd901ece6b9a37c06ca25c3c2e Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 6 Aug 2008 21:54:56 +0000 Subject: [PATCH] 65: Finished simplified Proxy2 class; updated unit tests --- ipalib/errors.py | 3 -- ipalib/plugable.py | 78 ++++++++++------------------------- ipalib/tests/test_plugable.py | 31 +++++++------- 3 files changed, 37 insertions(+), 75 deletions(-) diff --git a/ipalib/errors.py b/ipalib/errors.py index e9f784773..b86ffcdbc 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 43dd50ca8..bf0f52b46 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)) + # Check base.public + assert type(self.base.public) is frozenset - def __call__(self, *args, **kw): - return self.__target(*args, **kw) + # 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 __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.__props: - return self.__props[name]() - raise AttributeError(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 40e98ed31..383e068e2 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)