154: Merged ProxyTarget functionality into Plugin to make things a bit clearer

This commit is contained in:
Jason Gerard DeRose
2008-08-14 07:10:07 +00:00
parent a59d6698d2
commit 00f4272662
2 changed files with 271 additions and 281 deletions

View File

@@ -102,9 +102,130 @@ class ReadOnly(object):
return object.__delattr__(self, name)
class Plugin(ReadOnly):
"""
Base class for all plugins.
"""
__public__ = frozenset()
__api = None
def __get_name(self):
"""
Convenience property to return the class name.
"""
return self.__class__.__name__
name = property(__get_name)
def __get_doc(self):
"""
Convenience property to return the class docstring.
"""
return self.__class__.__doc__
doc = property(__get_doc)
def __get_api(self):
"""
Returns the `API` instance passed to `finalize`, or
or returns None if `finalize` has not yet been called.
"""
return self.__api
api = property(__get_api)
@classmethod
def implements(cls, arg):
"""
Returns True if this cls.__public__ frozenset contains `arg`;
returns False otherwise.
There are three different ways this can be called:
1. With a <type 'str'> argument, e.g.:
>>> class base(ProxyTarget):
>>> __public__ = frozenset(['some_attr', 'another_attr'])
>>> base.implements('some_attr')
True
>>> base.implements('an_unknown_attribute')
False
2. With a <type 'frozenset'> argument, e.g.:
>>> base.implements(frozenset(['some_attr']))
True
>>> base.implements(frozenset(['some_attr', 'an_unknown_attribute']))
False
3. With any object that has a `__public__` attribute that is
<type 'frozenset'>, e.g.:
>>> class whatever(object):
>>> __public__ = frozenset(['another_attr'])
>>> base.implements(whatever)
True
Unlike ProxyTarget.implemented_by(), this returns an abstract answer
because only the __public__ frozenset is checked... a ProxyTarget
need not itself have attributes for all names in __public__
(subclasses might provide them).
"""
assert type(cls.__public__) is frozenset
if isinstance(arg, str):
return arg in cls.__public__
if type(getattr(arg, '__public__', None)) is frozenset:
return cls.__public__.issuperset(arg.__public__)
if type(arg) is frozenset:
return cls.__public__.issuperset(arg)
raise TypeError(
"must be str, frozenset, or have frozenset '__public__' attribute"
)
@classmethod
def implemented_by(cls, arg):
"""
Returns True if (1) `arg` is an instance of or subclass of this class,
and (2) `arg` (or `arg.__class__` if instance) has an attribute for
each name in this class's __public__ frozenset; returns False
otherwise.
Unlike ProxyTarget.implements(), this returns a concrete answer
because the attributes of the subclass are checked.
"""
if inspect.isclass(arg):
subclass = arg
else:
subclass = arg.__class__
assert issubclass(subclass, cls), 'must be subclass of %r' % cls
for name in cls.__public__:
if not hasattr(subclass, name):
return False
return True
def finalize(self, api):
"""
After all the plugins are instantiated, `API` calls this method,
passing itself as the only argument. This is where plugins should
check that other plugins they depend upon have actually been loaded.
:param api: An `API` instance.
"""
assert self.__api is None, 'finalize() can only be called once'
assert api is not None, 'finalize() argument cannot be None'
self.__api = api
def __repr__(self):
"""
Returns a fully qualified module_name.class_name() representation that
could be used to construct this Plugin instance.
"""
return '%s.%s()' % (
self.__class__.__module__,
self.__class__.__name__
)
class Proxy(ReadOnly):
"""
Allows access to only certain attributes on its target object.
Allows access to only certain attributes on a `Plugin`.
Think of a proxy as an agreement that "I will have at most these
attributes". This is different from (although similar to) an interface,
@@ -208,128 +329,6 @@ class Proxy(ReadOnly):
)
class ProxyTarget(ReadOnly):
__public__ = frozenset()
def __get_name(self):
"""
Convenience property to return the class name.
"""
return self.__class__.__name__
name = property(__get_name)
def __get_doc(self):
"""
Convenience property to return the class docstring.
"""
return self.__class__.__doc__
doc = property(__get_doc)
@classmethod
def implements(cls, arg):
"""
Returns True if this cls.__public__ frozenset contains `arg`;
returns False otherwise.
There are three different ways this can be called:
1. With a <type 'str'> argument, e.g.:
>>> class base(ProxyTarget):
>>> __public__ = frozenset(['some_attr', 'another_attr'])
>>> base.implements('some_attr')
True
>>> base.implements('an_unknown_attribute')
False
2. With a <type 'frozenset'> argument, e.g.:
>>> base.implements(frozenset(['some_attr']))
True
>>> base.implements(frozenset(['some_attr', 'an_unknown_attribute']))
False
3. With any object that has a `__public__` attribute that is
<type 'frozenset'>, e.g.:
>>> class whatever(object):
>>> __public__ = frozenset(['another_attr'])
>>> base.implements(whatever)
True
Unlike ProxyTarget.implemented_by(), this returns an abstract answer
because only the __public__ frozenset is checked... a ProxyTarget
need not itself have attributes for all names in __public__
(subclasses might provide them).
"""
assert type(cls.__public__) is frozenset
if isinstance(arg, str):
return arg in cls.__public__
if type(getattr(arg, '__public__', None)) is frozenset:
return cls.__public__.issuperset(arg.__public__)
if type(arg) is frozenset:
return cls.__public__.issuperset(arg)
raise TypeError(
"must be str, frozenset, or have frozenset '__public__' attribute"
)
@classmethod
def implemented_by(cls, arg):
"""
Returns True if (1) `arg` is an instance of or subclass of this class,
and (2) `arg` (or `arg.__class__` if instance) has an attribute for
each name in this class's __public__ frozenset; returns False
otherwise.
Unlike ProxyTarget.implements(), this returns a concrete answer
because the attributes of the subclass are checked.
"""
if inspect.isclass(arg):
subclass = arg
else:
subclass = arg.__class__
assert issubclass(subclass, cls), 'must be subclass of %r' % cls
for name in cls.__public__:
if not hasattr(subclass, name):
return False
return True
class Plugin(ProxyTarget):
"""
Base class for all plugins.
"""
__api = None
def __get_api(self):
"""
Returns the plugable.API instance passed to Plugin.finalize(), or
or returns None if finalize() has not yet been called.
"""
return self.__api
api = property(__get_api)
def finalize(self, api):
"""
After all the plugins are instantiated, the plugable.API calls this
method, passing itself as the only argument. This is where plugins
should check that other plugins they depend upon have actually been
loaded.
"""
assert self.__api is None, 'finalize() can only be called once'
assert api is not None, 'finalize() argument cannot be None'
self.__api = api
def __repr__(self):
"""
Returns a fully qualified module_name.class_name() representation that
could be used to construct this Plugin instance.
"""
return '%s.%s()' % (
self.__class__.__module__,
self.__class__.__name__
)
def check_name(name):

View File

@@ -86,6 +86,155 @@ class test_ReadOnly(ClassChecker):
assert read_only(obj, 'an_attribute') == 'Hello world!'
class test_Plugin(ClassChecker):
"""
Tests the `Plugin` class.
"""
_cls = plugable.Plugin
def test_class(self):
assert self.cls.__bases__ == (plugable.ReadOnly,)
assert self.cls.__public__ == frozenset()
assert type(self.cls.name) is property
assert type(self.cls.doc) is property
assert type(self.cls.api) is property
def test_name(self):
"""
Tests the `name` property.
"""
assert read_only(self.cls(), 'name') == 'Plugin'
class some_subclass(self.cls):
pass
assert read_only(some_subclass(), 'name') == 'some_subclass'
def test_doc(self):
"""
Tests the `doc` property.
"""
class some_subclass(self.cls):
'here is the doc string'
assert read_only(some_subclass(), 'doc') == 'here is the doc string'
def test_implements(self):
"""
Tests 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',
))
for ex in (example, example()):
# Test using str:
assert ex.implements('some_method')
assert not ex.implements('another_method')
# Test using frozenset:
assert ex.implements(frozenset(['some_method']))
assert not ex.implements(
frozenset(['some_method', 'another_method'])
)
# Test using another object/class with __public__ frozenset:
assert ex.implements(example)
assert ex.implements(example())
assert ex.implements(subset)
assert not subset.implements(ex)
assert not ex.implements(superset)
assert superset.implements(ex)
assert ex.implements(any_object)
assert ex.implements(any_object())
def test_implemented_by(self):
"""
Tests the `implemented_by` classmethod.
"""
class base(self.cls):
__public__ = frozenset((
'attr0',
'attr1',
'attr2',
))
class okay(base):
def attr0(self):
pass
def __get_attr1(self):
assert False # Make sure property isn't accesed on instance
attr1 = property(__get_attr1)
attr2 = 'hello world'
another_attr = 'whatever'
class fail(base):
def __init__(self):
# Check that class, not instance is inspected:
self.attr2 = 'hello world'
def attr0(self):
pass
def __get_attr1(self):
assert False # Make sure property isn't accesed on instance
attr1 = property(__get_attr1)
another_attr = 'whatever'
# Test that AssertionError is raised trying to pass something not
# subclass nor instance of base:
raises(AssertionError, base.implemented_by, object)
# Test on subclass with needed attributes:
assert base.implemented_by(okay) is True
assert base.implemented_by(okay()) is True
# Test on subclass *without* needed attributes:
assert base.implemented_by(fail) is False
assert base.implemented_by(fail()) is False
def test_finalize(self):
"""
Tests the `finalize` method.
"""
api = 'the api instance'
o = self.cls()
assert read_only(o, 'name') == 'Plugin'
assert repr(o) == '%s.Plugin()' % plugable.__name__
assert read_only(o, 'api') is None
raises(AssertionError, o.finalize, None)
o.finalize(api)
assert read_only(o, 'api') is api
raises(AssertionError, o.finalize, api)
class some_plugin(self.cls):
pass
sub = some_plugin()
assert read_only(sub, 'name') == 'some_plugin'
assert repr(sub) == '%s.some_plugin()' % __name__
assert read_only(sub, 'api') is None
raises(AssertionError, sub.finalize, None)
sub.finalize(api)
assert read_only(sub, 'api') is api
raises(AssertionError, sub.finalize, api)
class test_Proxy(ClassChecker):
"""
Tests the `Proxy` class.
@@ -202,164 +351,6 @@ class test_Proxy(ClassChecker):
assert read_only(c, 'name') == 'another_name'
class test_ProxyTarget(ClassChecker):
"""
Test the `ProxyTarget` class.
"""
_cls = plugable.ProxyTarget
def test_class(self):
assert self.cls.__bases__ == (plugable.ReadOnly,)
assert type(self.cls.name) is property
assert self.cls.implements(frozenset())
def test_name(self):
"""
Tests the `name` property.
"""
assert read_only(self.cls(), 'name') == 'ProxyTarget'
class some_subclass(self.cls):
pass
assert read_only(some_subclass(), 'name') == 'some_subclass'
def test_doc(self):
"""
Tests the `doc` property.
"""
class some_subclass(self.cls):
'here is the doc string'
assert read_only(some_subclass(), 'doc') == 'here is the doc string'
def test_implements(self):
"""
Tests 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',
))
for ex in (example, example()):
# Test using str:
assert ex.implements('some_method')
assert not ex.implements('another_method')
# Test using frozenset:
assert ex.implements(frozenset(['some_method']))
assert not ex.implements(
frozenset(['some_method', 'another_method'])
)
# Test using another object/class with __public__ frozenset:
assert ex.implements(example)
assert ex.implements(example())
assert ex.implements(subset)
assert not subset.implements(ex)
assert not ex.implements(superset)
assert superset.implements(ex)
assert ex.implements(any_object)
assert ex.implements(any_object())
def test_implemented_by(self):
"""
Tests the `implemented_by` classmethod.
"""
class base(self.cls):
__public__ = frozenset((
'attr0',
'attr1',
'attr2',
))
class okay(base):
def attr0(self):
pass
def __get_attr1(self):
assert False # Make sure property isn't accesed on instance
attr1 = property(__get_attr1)
attr2 = 'hello world'
another_attr = 'whatever'
class fail(base):
def __init__(self):
# Check that class, not instance is inspected:
self.attr2 = 'hello world'
def attr0(self):
pass
def __get_attr1(self):
assert False # Make sure property isn't accesed on instance
attr1 = property(__get_attr1)
another_attr = 'whatever'
# Test that AssertionError is raised trying to pass something not
# subclass nor instance of base:
raises(AssertionError, base.implemented_by, object)
# Test on subclass with needed attributes:
assert base.implemented_by(okay) is True
assert base.implemented_by(okay()) is True
# Test on subclass *without* needed attributes:
assert base.implemented_by(fail) is False
assert base.implemented_by(fail()) is False
class test_Plugin(ClassChecker):
"""
Tests the `Plugin` class.
"""
_cls = plugable.Plugin
def test_class(self):
assert self.cls.__bases__ == (plugable.ProxyTarget,)
assert type(self.cls.api) is property
def test_finalize(self):
"""
Tests the `finalize` method.
"""
api = 'the api instance'
o = self.cls()
assert read_only(o, 'name') == 'Plugin'
assert repr(o) == '%s.Plugin()' % plugable.__name__
assert read_only(o, 'api') is None
raises(AssertionError, o.finalize, None)
o.finalize(api)
assert read_only(o, 'api') is api
raises(AssertionError, o.finalize, api)
class some_plugin(self.cls):
pass
sub = some_plugin()
assert read_only(sub, 'name') == 'some_plugin'
assert repr(sub) == '%s.some_plugin()' % __name__
assert read_only(sub, 'api') is None
raises(AssertionError, sub.finalize, None)
sub.finalize(api)
assert read_only(sub, 'api') is api
raises(AssertionError, sub.finalize, api)
def test_check_name():
"""
Tests the `check_name` function.