From 5a1223e94367c4370a94f271ef7e087dbdb02615 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 8 Aug 2008 22:45:09 +0000 Subject: [PATCH] 90: Renamed plugable.Abstract to ProxyTarget, which now subclasses from ReadOnly; updated unit tests --- ipalib/plugable.py | 110 +++++++++++++------------- ipalib/tests/test_plugable.py | 143 ++++++++++++++++++---------------- 2 files changed, 136 insertions(+), 117 deletions(-) diff --git a/ipalib/plugable.py b/ipalib/plugable.py index 092e3bdd7..3f53fd4ad 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -43,7 +43,11 @@ class ReadOnly(object): __locked = False def __lock__(self): - assert self.__locked is False + """ + Puts this instance into a read-only state, after which attempting to + set or delete an attribute will raise AttributeError. + """ + assert self.__locked is False, '__lock__() can only be called once' self.__locked = True def __setattr__(self, name, value): @@ -69,7 +73,7 @@ class ReadOnly(object): return object.__delattr__(self, name) -class Abstract(object): +class ProxyTarget(ReadOnly): __public__ = frozenset() @classmethod @@ -86,6 +90,58 @@ class Abstract(object): ) +class Proxy(ReadOnly): + __slots__ = ( + '__base', + '__target', + '__name_attr', + 'name', + '__public__', + ) + + 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) + self.__base = base + self.__target = target + self.__name_attr = name_attr + self.name = getattr(target, name_attr) + self.__public__ = base.__public__ + assert type(self.__public__) is frozenset + check_identifier(self.name) + self.__lock__() + + def __iter__(self): + for name in sorted(self.__public__): + yield name + + def __getitem__(self, key): + if key in self.__public__: + return getattr(self.__target, key) + raise KeyError('no proxy attribute %r' % key) + + def __getattr__(self, name): + if name in self.__public__: + return getattr(self.__target, name) + raise AttributeError('no proxy attribute %r' % name) + + def __call__(self, *args, **kw): + return self['__call__'](*args, **kw) + + def _clone(self, name_attr): + return self.__class__(self.__base, self.__target, name_attr) + + def __repr__(self): + return '%s(%s, %r, %r)' % ( + self.__class__.__name__, + self.__base.__name__, + self.__target, + self.__name_attr, + ) + + class Plugin(object): """ Base class for all plugins. @@ -132,57 +188,7 @@ class Plugin(object): -class Proxy(ReadOnly): - __slots__ = ( - '__base', - '__target', - '__name_attr', - 'name', - '__public__', - ) - 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) - self.__base = base - self.__target = target - self.__name_attr = name_attr - self.name = getattr(target, name_attr) - self.__public__ = base.__public__ - assert type(self.__public__) is frozenset - check_identifier(self.name) - self.__lock__() - - - def __iter__(self): - for name in sorted(self.__public__): - yield name - - def __getitem__(self, key): - if key in self.__public__: - return getattr(self.__target, key) - raise KeyError('no proxy attribute %r' % key) - - def __getattr__(self, name): - if name in self.__public__: - return getattr(self.__target, name) - raise AttributeError('no proxy attribute %r' % name) - - def __call__(self, *args, **kw): - return self['__call__'](*args, **kw) - - def _clone(self, name_attr): - return self.__class__(self.__base, self.__target, name_attr) - - def __repr__(self): - return '%s(%s, %r, %r)' % ( - self.__class__.__name__, - self.__base.__name__, - self.__target, - self.__name_attr, - ) class NameSpace(ReadOnly): diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py index 1f42aa85c..c3245e775 100644 --- a/ipalib/tests/test_plugable.py +++ b/ipalib/tests/test_plugable.py @@ -26,6 +26,33 @@ from tstutil import ClassChecker from ipalib import plugable, errors +def test_valid_identifier(): + """ + Test the plugable.valid_identifier function. + """ + f = plugable.check_identifier + okay = [ + 'user_add', + 'stuff2junk', + 'sixty9', + ] + nope = [ + '_user_add', + '__user_add', + 'user_add_', + 'user_add__', + '_user_add_', + '__user_add__', + '60nine', + ] + for name in okay: + f(name) + for name in nope: + raises(errors.NameSpaceError, f, name) + for name in okay: + raises(errors.NameSpaceError, f, name.upper()) + + class test_ReadOnly(ClassChecker): """ Test the plugable.ReadOnly class @@ -34,6 +61,7 @@ class test_ReadOnly(ClassChecker): def test_class(self): assert self.cls.__bases__ == (object,) + assert callable(self.cls.__lock__) def test_when_unlocked(self): """ @@ -73,79 +101,64 @@ class test_ReadOnly(ClassChecker): assert read_only(obj, 'an_attribute') == 'Hello world!' +class test_ProxyTarget(ClassChecker): + """ + Test the plugable.ProxyTarget class. + """ + _cls = plugable.ProxyTarget -def test_valid_identifier(): - f = plugable.check_identifier - okay = [ - 'user_add', - 'stuff2junk', - 'sixty9', - ] - nope = [ - '_user_add', - '__user_add', - 'user_add_', - 'user_add__', - '_user_add_', - '__user_add__', - '60nine', - ] - for name in okay: - f(name) - for name in nope: - raises(errors.NameSpaceError, f, name) - for name in okay: - raises(errors.NameSpaceError, f, name.upper()) + def test_class(self): + assert self.cls.__bases__ == (plugable.ReadOnly,) + assert self.cls.implements(frozenset()) + def test_implements(self): + """ + Test the implements() classmethod + """ + class example(self.cls): + __public__ = frozenset(( + 'some_method', + 'some_property', + )) + class superset(self.cls): + __public__ = frozenset(( + 'some_method', + 'some_property', + 'another_property', + )) + class subset(self.cls): + __public__ = frozenset(( + 'some_property', + )) + class any_object(object): + __public__ = frozenset(( + 'some_method', + 'some_property', + )) -def test_Abstract(): - cls = plugable.Abstract + for ex in (example, example()): + # Test using str: + assert ex.implements('some_method') + assert not ex.implements('another_method') - class example(cls): - __public__ = frozenset(( - 'some_method', - 'some_property', - )) + # Test using frozenset: + assert ex.implements(frozenset(['some_method'])) + assert not ex.implements( + frozenset(['some_method', 'another_method']) + ) - # Test using str: - assert example.implements('some_method') - assert not example.implements('another_method') + # Test using another object/class with __public__ frozenset: + assert ex.implements(example) + assert ex.implements(example()) - # Test using frozenset: - assert example.implements(frozenset(['some_method'])) - assert not example.implements( - frozenset(['some_method', 'another_method']) - ) + assert ex.implements(subset) + assert not subset.implements(ex) - # Test using another object/class with __public__ frozenset: - assert example.implements(example) - assert example().implements(example) - assert example.implements(example()) - assert example().implements(example()) + assert not ex.implements(superset) + assert superset.implements(ex) - class subset(cls): - __public__ = frozenset(( - 'some_property', - )) - assert example.implements(subset) - assert not subset.implements(example) - - class superset(cls): - __public__ = frozenset(( - 'some_method', - 'some_property', - 'another_property', - )) - assert not example.implements(superset) - assert superset.implements(example) - - class any_object(object): - __public__ = frozenset(( - 'some_method', - 'some_property', - )) - assert example.implements(any_object) - assert example.implements(any_object()) + assert ex.implements(any_object) + assert ex.implements(any_object()) def test_Plugin():