diff options
-rw-r--r-- | ChangeLog | 2 | ||||
-rw-r--r-- | interface.py | 44 | ||||
-rw-r--r-- | test/unittest_interface.py | 54 |
3 files changed, 90 insertions, 10 deletions
@@ -15,6 +15,8 @@ ChangeLog for logilab.common * modutils: new load_module_from_file shortcut function * clcommands: pop_args accept None as value for expected_size_after, meaning remaining args should not be checked + * interface: new extend function to dynamically add an implemented interface + to a new style class 2007-06-25 -- 0.22.2 * new 'typechanged' action for configuration.read_old_config diff --git a/interface.py b/interface.py index e94df1b..78f0afa 100644 --- a/interface.py +++ b/interface.py @@ -1,4 +1,4 @@ -# Copyright (c) 2000-2004 LOGILAB S.A. (Paris, FRANCE). +# Copyright (c) 2002-2007 LOGILAB S.A. (Paris, FRANCE). # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This program is free software; you can redistribute it and/or modify it under @@ -14,7 +14,7 @@ # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ - bases class for interfaces + bases class for interfaces to provide "light" interface handling. TODO: _ implements a check method which check that an object implements the @@ -31,15 +31,39 @@ class Interface: def is_implemented_by(cls, instance): return implements(instance, cls) is_implemented_by = classmethod(is_implemented_by) + -def implements(instance, interface): - """return true if the instance implements the interface +def implements(obj, interface): + """return true if the give object (maybe an instance or class) implements + the interface """ - if hasattr(instance, "__implements__") and \ - (interface is instance.__implements__ or - (type(instance.__implements__) in (ListType, TupleType) and \ - interface in instance.__implements__)): - return 1 - return 0 + kimplements = getattr(obj, '__implements__', ()) + if not isinstance(kimplements, (list, tuple)): + kimplements = (kimplements,) + for implementedinterface in kimplements: + if issubclass(interface, implementedinterface): + return True + return False + +def extend(klass, interface, _recurs=False): + """add interface to klass'__implements__ if not already implemented in. + if klass is subclassed, ensure subclasses __implements__ it as well. + + NOTE: klass should be e new class. + """ + if not implements(klass, interface): + try: + kimplements = klass.__implements__ + kimplementsklass = type(kimplements) + kimplements = list(kimplements) + except AttributeError: + kimplements = [] + kimplements.append(interface) + klass.__implements__ = kimplementsklass(kimplements) + for subklass in klass.__subclasses__(): + extend(subklass, interface, _recurs=True) + elif _recurs: + for subklass in klass.__subclasses__(): + extend(subklass, interface, _recurs=True) diff --git a/test/unittest_interface.py b/test/unittest_interface.py new file mode 100644 index 0000000..22992c3 --- /dev/null +++ b/test/unittest_interface.py @@ -0,0 +1,54 @@ +from logilab.common.testlib import TestCase, unittest_main +from logilab.common.interface import * + +class IFace1(Interface): pass +class IFace2(Interface): pass +class IFace3(Interface): pass + + +class A(object): + __implements__ = (IFace1,) + + +class B(A): pass + + +class C1(B): + __implements__ = list(B.__implements__) + [IFace3] + +class C2(B): + __implements__ = B.__implements__ + (IFace2,) + +class D(C1): + __implements__ = () + + +class ExtendTC(TestCase): + + def setUp(self): + global aimpl, c1impl, c2impl, dimpl + aimpl = A.__implements__ + c1impl = C1.__implements__ + c2impl = C2.__implements__ + dimpl = D.__implements__ + + def test_base(self): + extend(A, IFace1) + self.failUnless(A.__implements__ is aimpl) + extend(A, IFace2) + self.failUnlessEqual(A.__implements__, (IFace1, IFace2)) + self.failUnlessEqual(B.__implements__, (IFace1, IFace2)) + self.failUnless(B.__implements__ is A.__implements__) + self.failUnlessEqual(C1.__implements__, [IFace1, IFace3, IFace2]) + self.failUnlessEqual(C2.__implements__, (IFace1, IFace2)) + self.failUnless(C2.__implements__ is c2impl) + self.failUnlessEqual(D.__implements__, (IFace2,)) + + def test_notimpl_explicit(self): + extend(C1, IFace3) + self.failUnless(C1.__implements__ is c1impl) + self.failUnless(D.__implements__ is dimpl) + + +if __name__ == '__main__': + unittest_main() |