From f767543fe71db6fb840ad8f328158fe0c6d65ad4 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Tue, 12 Aug 2008 22:40:06 +0000 Subject: [PATCH] 120: Moved ProxyTarget below Proxy to emphasize relationship with Plugin; added docstrings for ProxyTarget.implements() and implemented_by() classmethods; fixed typo in Plugin.finalize() docstring --- ipalib/plugable.py | 118 +++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 37 deletions(-) diff --git a/ipalib/plugable.py b/ipalib/plugable.py index a91063e7c..230e8ee26 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -112,42 +112,6 @@ class ReadOnly(object): return object.__delattr__(self, name) -class ProxyTarget(ReadOnly): - __public__ = frozenset() - - def __get_name(self): - """ - Convenience property to return the class name. - """ - return self.__class__.__name__ - name = property(__get_name) - - @classmethod - def implements(cls, arg): - 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): - 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 Proxy(ReadOnly): """ Allows access to only certain attributes on its target object (a @@ -251,6 +215,86 @@ 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) + + @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 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 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 + , 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. @@ -270,7 +314,7 @@ class Plugin(ProxyTarget): """ 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 be + should check that other plugins they depend upon have actually been loaded. """ assert self.__api is None, 'finalize() can only be called once'