65: Finished simplified Proxy2 class; updated unit tests

This commit is contained in:
Jason Gerard DeRose 2008-08-06 21:54:56 +00:00
parent 0c7769473c
commit f13f1226b4
3 changed files with 37 additions and 75 deletions

View File

@ -53,9 +53,6 @@ class SetError(IPAError):
class RegistrationError(IPAError): class RegistrationError(IPAError):
""" """
Base class for errors that occur during plugin registration. Base class for errors that occur during plugin registration.

View File

@ -25,29 +25,6 @@ import re
import inspect import inspect
import errors 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): def to_cli(name):
""" """
@ -192,53 +169,42 @@ class Proxy(ReadOnly):
class Proxy2(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): if not inspect.isclass(base):
raise TypeError('arg1 must be a class, got %r' % base) raise TypeError('arg1 must be a class, got %r' % base)
if not isinstance(target, base): if not isinstance(target, base):
raise ValueError('arg2 must be instance of arg1, got %r' % target) raise ValueError('arg2 must be instance of arg1, got %r' % target)
object.__setattr__(self, 'base', base) object.__setattr__(self, 'base', base)
object.__setattr__(self, '_Proxy2__target', target) object.__setattr__(self, '_Proxy2__target', target)
object.__setattr__(self, '_Proxy2__props', dict())
names = [] # The names of exported attributes # Check base.public
# This matches implied property fget methods like '_get_user' assert type(self.base.public) is frozenset
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): # Check name
return self.__target(*args, **kw) object.__setattr__(self, 'name', getattr(target, name_attr))
check_identifier(self.name)
def __iter__(self): def __iter__(self):
for name in self.__names: for name in sorted(self.base.public):
yield name 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): def __getattr__(self, name):
if name in self.__props: if name in self.base.public:
return self.__props[name]() return getattr(self.__target, name)
raise AttributeError(name) raise AttributeError('no proxy attribute %r' % name)
def __call__(self, *args, **kw):
return self['__call__'](*args, **kw)
class NameSpace(ReadOnly): class NameSpace(ReadOnly):

View File

@ -170,23 +170,22 @@ def test_Proxy():
def test_Proxy2(): def test_Proxy2():
cls = plugable.Proxy2 cls = plugable.Proxy2
export = plugable.export
assert issubclass(cls, plugable.ReadOnly) assert issubclass(cls, plugable.ReadOnly)
# Setup: # Setup:
class base(object): class base(object):
@export public = frozenset((
'public_0',
'public_1',
'__call__',
))
def public_0(self): def public_0(self):
return 'public_0' return 'public_0'
@export
def public_1(self): def public_1(self):
return 'public_1' return 'public_1'
@export
def _get_some_prop(self):
return 'ya got it'
def __call__(self, caller): def __call__(self, caller):
return 'ya called it, %s.' % caller return 'ya called it, %s.' % caller
@ -197,7 +196,8 @@ def test_Proxy2():
return 'private_1' return 'private_1'
class plugin(base): class plugin(base):
pass name = 'user_add'
attr_name = 'add'
# Test that TypeError is raised when base is not a class: # Test that TypeError is raised when base is not a class:
raises(TypeError, cls, base(), None) raises(TypeError, cls, base(), None)
@ -209,7 +209,8 @@ def test_Proxy2():
i = plugin() i = plugin()
p = cls(base, i) p = cls(base, i)
assert read_only(p, 'base') is base 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: # Test normal methods:
for n in xrange(2): for n in xrange(2):
@ -217,6 +218,7 @@ def test_Proxy2():
priv = 'private_%d' % n priv = 'private_%d' % n
assert getattr(i, pub)() == pub assert getattr(i, pub)() == pub
assert getattr(p, pub)() == pub assert getattr(p, pub)() == pub
assert hasattr(p, pub)
assert getattr(i, priv)() == priv assert getattr(i, priv)() == priv
assert not hasattr(p, priv) assert not hasattr(p, priv)
@ -224,14 +226,11 @@ def test_Proxy2():
value = 'ya called it, dude.' value = 'ya called it, dude.'
assert i('dude') == value assert i('dude') == value
assert p('dude') == value assert p('dude') == value
assert callable(p)
# Test implied property: # Test name_attr='name' kw arg
fget = '_get_some_prop' i = plugin()
name = 'some_prop' p = cls(base, i)
value = 'ya got it'
assert getattr(i, fget)() == value
assert getattr(p, fget)() == value
assert getattr(p, name) == value