[Zope3-checkins] SVN: Zope3/trunk/src/zope/interface/ Fixed a bug in the inheritence of interface attribute definitions

Jim Fulton jim at zope.com
Sat Sep 11 12:27:14 EDT 2004


Log message for revision 27496:
  Fixed a bug in the inheritence of interface attribute definitions
  
  Previously, attributes were looked up using a dpeth-first serach of
  ancestor interfaces.  This is inconsistent with the way that
  components are looked up for interfaces (and with the way attributes
  are looked up for new-style classes.)
  
  


Changed:
  U   Zope3/trunk/src/zope/interface/README.txt
  U   Zope3/trunk/src/zope/interface/declarations.py
  U   Zope3/trunk/src/zope/interface/interface.py
  U   Zope3/trunk/src/zope/interface/interfaces.py
  U   Zope3/trunk/src/zope/interface/tests/test_declarations.py


-=-
Modified: Zope3/trunk/src/zope/interface/README.txt
===================================================================
--- Zope3/trunk/src/zope/interface/README.txt	2004-09-10 17:08:49 UTC (rev 27495)
+++ Zope3/trunk/src/zope/interface/README.txt	2004-09-11 16:27:14 UTC (rev 27496)
@@ -90,6 +90,11 @@
   >>> x.__doc__
   'X blah blah'
 
+  >>> IFoo.get('x').__name__
+  'x'
+
+  >>> IFoo.get('y')
+
 You can use `in` to determine if an interface defines a name::
 
   >>> 'x' in IFoo
@@ -116,7 +121,7 @@
   >>> bar.getSignatureString()
   '(q, r=None)'
 
-XXX
+TODO
   Methods really should have a better API.  This is something that
   needs to be improved.
 
@@ -459,7 +464,46 @@
   >>> list(IBaz.names())
   ['eek']
 
+Inheritance if attribute specifications
+---------------------------------------
 
+An interface may override attribute definitions frob base interfaces.
+If two base interfaces define the same attribute, the attribute is
+inherited from the most specific interface. For example, with:
+
+  >>> class IBase(zope.interface.Interface):
+  ...    
+  ...     def foo():
+  ...         "base foo doc"
+
+  >>> class IBase1(IBase):
+  ...     pass
+
+  >>> class IBase2(IBase):
+  ...    
+  ...     def foo():
+  ...         "base2 foo doc"
+
+  >>> class ISub(IBase1, IBase2):
+  ...     pass
+
+ISub's definition of foo is the one from IBase2, since IBase2 is more
+specific that IBase:
+
+  >>> ISub['foo'].__doc__
+  'base2 foo doc'
+
+Note that this differs from a depth-first search.
+
+Sometimes, it's useful to ask whether an interface defines an
+attribute directly.  You can use the direct method to get a directly
+defined definitions:
+
+  >>> IBase.direct('foo').__doc__
+  'base foo doc'
+
+  >>> ISub.direct('foo')
+
 Specifications
 --------------
 

Modified: Zope3/trunk/src/zope/interface/declarations.py
===================================================================
--- Zope3/trunk/src/zope/interface/declarations.py	2004-09-10 17:08:49 UTC (rev 27495)
+++ Zope3/trunk/src/zope/interface/declarations.py	2004-09-11 16:27:14 UTC (rev 27496)
@@ -52,72 +52,6 @@
         except AttributeError:
             pass
 
-    def get():
-        marker1 = object()
-        marker2 = object()
-        def get(self, name, default=None):
-            """Query for an attribute description
-
-                >>> import zope.interface
-                >>> class I1(zope.interface.Interface):
-                ...    a11 = zope.interface.Attribute('a11')
-                ...    a12 = zope.interface.Attribute('a12')
-                >>> class I2(zope.interface.Interface):
-                ...    a21 = zope.interface.Attribute('a21')
-                ...    a22 = zope.interface.Attribute('a22')
-                ...    a12 = zope.interface.Attribute('a212')
-                >>> class I11(I1):
-                ...    a11 = zope.interface.Attribute('a111')
-
-                >>> decl = Declaration(I11, I2)
-                >>> decl.get('a11') is I11.get('a11')
-                True
-                >>> decl.get('a12') is I1.get('a12')
-                True
-                >>> decl.get('a21') is I2.get('a21')
-                True
-                >>> decl.get('a22') is I2.get('a22')
-                True
-                >>> decl.get('a')
-                >>> decl.get('a', 42)
-                42
-
-            We get None even with no interfaces:
-
-                >>> decl = Declaration()
-                >>> decl.get('a11')
-                >>> decl.get('a11', 42)
-                42
-
-            We get new data if e change interface bases:
-
-                >>> decl.__bases__ = I11, I2
-                >>> decl.get('a11') is I11.get('a11')
-                True
-            """
-            try:
-                attrs = self._v_attrs
-            except AttributeError:
-                attrs = self._v_attrs = {}
-            attr = attrs.get(name, marker1)
-            if attr is marker1:
-                for iface in self:
-                    attr = iface.get(name, marker2)
-                    if attr is not marker2:
-                        break
-                else:
-                    attr = marker2
-                    
-                attrs[name] = attr
-            
-            if attr is marker2:
-                return default
-            else:
-                return attr
-        
-        return get
-    get = get()
-
     def __contains__(self, interface):
         """Test whether an interface is in the specification
 

Modified: Zope3/trunk/src/zope/interface/interface.py
===================================================================
--- Zope3/trunk/src/zope/interface/interface.py	2004-09-10 17:08:49 UTC (rev 27495)
+++ Zope3/trunk/src/zope/interface/interface.py	2004-09-11 16:27:14 UTC (rev 27496)
@@ -357,8 +357,28 @@
             return weakref.ref(self)
         else:
             return weakref.ref(self, callback)
-        
 
+
+    def get(self, name, default=None):
+        """Query for an attribute description
+        """
+        try:
+            attrs = self._v_attrs
+        except AttributeError:
+            attrs = self._v_attrs = {}
+        attr = attrs.get(name)
+        if attr is None:
+            for iface in self.__iro__:
+                attr = iface.direct(name)
+                if attr is not None:
+                    attrs[name] = attr
+                    break
+            
+        if attr is None:
+            return default
+        else:
+            return attr
+
 class InterfaceClass(Element, Specification):
     """Prototype (scarecrow) Interfaces Implementation."""
 
@@ -495,7 +515,7 @@
 
     def getDescriptionFor(self, name):
         """Return the attribute description for the given name."""
-        r = self.queryDescriptionFor(name)
+        r = self.get(name)
         if r is not None:
             return r
 
@@ -504,22 +524,14 @@
     __getitem__ = getDescriptionFor
 
     def __contains__(self, name):
-        return self.queryDescriptionFor(name) is not None
+        return self.get(name) is not None
 
+    def direct(self, name):
+        return self.__attrs.get(name)
+
     def queryDescriptionFor(self, name, default=None):
-        """Return the attribute description for the given name."""
-        r = self.__attrs.get(name, self)
-        if r is not self:
-            return r
-        for base in self.__bases__:
-            r = base.queryDescriptionFor(name, self)
-            if r is not self:
-                return r
+        return self.get(name, default)
 
-        return default
-
-    get = queryDescriptionFor
-
     def deferred(self):
         """Return a defered class corresponding to the interface."""
         if hasattr(self, "_deferred"): return self._deferred

Modified: Zope3/trunk/src/zope/interface/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/interface/interfaces.py	2004-09-10 17:08:49 UTC (rev 27495)
+++ Zope3/trunk/src/zope/interface/interfaces.py	2004-09-11 16:27:14 UTC (rev 27496)
@@ -264,6 +264,12 @@
 
         If the named attribute is not defined, a KeyError is raised.
         """
+
+    def direct(name):
+        """Get the description for the name if it was defined by the interface
+
+        If the interface doesn't define the name, returns None.
+        """
     
     def validateInvariants(obj, errors=None):
         """Validate invariants

Modified: Zope3/trunk/src/zope/interface/tests/test_declarations.py
===================================================================
--- Zope3/trunk/src/zope/interface/tests/test_declarations.py	2004-09-10 17:08:49 UTC (rev 27495)
+++ Zope3/trunk/src/zope/interface/tests/test_declarations.py	2004-09-11 16:27:14 UTC (rev 27496)
@@ -311,6 +311,48 @@
     
     """
 
+def test_declaration_get():
+    """
+    We can get definitions from a declaration:
+
+        >>> import zope.interface
+        >>> class I1(zope.interface.Interface):
+        ...    a11 = zope.interface.Attribute('a11')
+        ...    a12 = zope.interface.Attribute('a12')
+        >>> class I2(zope.interface.Interface):
+        ...    a21 = zope.interface.Attribute('a21')
+        ...    a22 = zope.interface.Attribute('a22')
+        ...    a12 = zope.interface.Attribute('a212')
+        >>> class I11(I1):
+        ...    a11 = zope.interface.Attribute('a111')
+
+        >>> decl = Declaration(I11, I2)
+        >>> decl.get('a11') is I11.get('a11')
+        True
+        >>> decl.get('a12') is I1.get('a12')
+        True
+        >>> decl.get('a21') is I2.get('a21')
+        True
+        >>> decl.get('a22') is I2.get('a22')
+        True
+        >>> decl.get('a')
+        >>> decl.get('a', 42)
+        42
+
+    We get None even with no interfaces:
+
+        >>> decl = Declaration()
+        >>> decl.get('a11')
+        >>> decl.get('a11', 42)
+        42
+
+    We get new data if e change interface bases:
+
+        >>> decl.__bases__ = I11, I2
+        >>> decl.get('a11') is I11.get('a11')
+        True
+    """
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(Test))



More information about the Zope3-Checkins mailing list