summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog2
-rw-r--r--interface.py44
-rw-r--r--test/unittest_interface.py54
3 files changed, 90 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index 260f2ba..cd5e8db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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()