[Zodb-checkins] CVS: Zope3/src/zope/interface - interface.py:1.23

Jim Fulton jim at zope.com
Sat Mar 6 10:38:47 EST 2004


Update of /cvs-repository/Zope3/src/zope/interface
In directory cvs.zope.org:/tmp/cvs-serv31748/src/zope/interface

Modified Files:
	interface.py 
Log Message:
Added an optional argument to the interface __call__ method.
If the argument is provided, then the value provided will be
returned if adaptation can't be done.  If a second argument isn't 
provided, then a TypeError is raised if an adapter can't be computed.

The main motivation for this change is to provide queryAdapter-like
semantics, while also keeping PEP-246 adapt-like semantics (call 
__conform__ before checking whether the object already provides the
interface).

So now, you can use:

adapter = IFoo(ob, None)

much as you might have used queryAdapter before (but with PEP 246
adapt semantics).

I generally avoid methods like this, that change their behavior
based on the number of arguments provided, however, I decided to
do it in this case to be more consistent with the PEP 246 adapt 
method, and because this is __call__, rather than a "get" method.
I used a trick to avoid using a global marker.  This trick uses nested
scopes to provide hidden "static" data for functions.


=== Zope3/src/zope/interface/interface.py 1.22 => 1.23 ===
--- Zope3/src/zope/interface/interface.py:1.22	Fri Mar  5 19:38:46 2004
+++ Zope3/src/zope/interface/interface.py	Sat Mar  6 10:38:46 2004
@@ -567,92 +567,119 @@
             self._v_repr = r
         return r
 
-    def __call__(self, ob):
-        """Adapt an object to the interface
+    def __call__():
+        # TRICK! Create the call method
+        #
+        # An embedded function is used to allow an optional argument to
+        # __call__ without resorting to a global marker.
+        #
+        # The evility of this trick is a reflection of the underlying
+        # evility of "optional" arguments, arguments whos presense or
+        # absense changes the behavior of the methos.
+        # 
+        # I think the evil is necessary, and perhaps desireable to
+        # provide some consistencey with the PEP 246 adapt method.
 
-           The sematics are those of the PEP 246 adapt function.
-
-           If an object cannot be adapted, then a TypeError is raised::
-
-             >>> import zope.interface
-             >>> class I(zope.interface.Interface):
-             ...     pass
-
-             >>> I(0)
-             Traceback (most recent call last):
-             ...
-             TypeError: ('Could not adapt', 0, """ \
-                  """<InterfaceClass zope.interface.interface.I>)
-
-           If an object already implements the interface, then it will be
-           returned::
-
-             >>> class C:
-             ...     zope.interface.implements(I)
-
-             >>> ob = C()
-             >>> I(ob) is ob
-             True
-
-           If an object implements __conform__, then it will be used::
-
-             >>> class C:
-             ...     zope.interface.implements(I)
-             ...     def __conform__(self, proto):
-             ...          return 0
-
-             >>> I(C())
-             0
+        marker = object()
+        
+        def __call__(self, obj, alternate=marker):
+            """Adapt an object to the interface
 
-           Adapter hools will also be used, if present:
+               The sematics based on those of the PEP 246 adapt function.
 
-             >>> from zope.interface.interface import adapter_hooks
-             >>> def adapt_0_to_42(iface, ob):
-             ...     if ob == 0:
-             ...         return 42
+               If an object cannot be adapted, then a TypeError is raised::
 
-             >>> adapter_hooks.append(adapt_0_to_42)
-             >>> I(0)
-             42
-
-             >>> adapter_hooks.remove(adapt_0_to_42)
-             >>> I(0)
-             Traceback (most recent call last):
-             ...
-             TypeError: ('Could not adapt', 0, """ \
-                  """<InterfaceClass zope.interface.interface.I>)
-        
-        """
-        conform = getattr(ob, '__conform__', None)
-        if conform is not None:
-            try:
-                adapter = conform(self)
-            except TypeError:
-                # We got a TypeError. It might be an error raised by
-                # the __conform__ implementation, or *we* may have
-                # made the TypeError by calling an unbound method
-                # (object is a class).  In the later case, we behave
-                # as though there is no __conform__ method. We can
-                # detect this case by checking whether there is more
-                # than one traceback object in the traceback chain:
-                if sys.exc_info()[2].tb_next is not None:
-                    # There is more than one entry in the chain, so
-                    # reraise the error:
-                    raise
-                # This clever trick is from Phillip Eby
-            else:
-                if adapter is not None:
-                    return adapter
-        
-        adapter = self.__adapt__(ob)
+                 >>> import zope.interface
+                 >>> class I(zope.interface.Interface):
+                 ...     pass
+
+                 >>> I(0)
+                 Traceback (most recent call last):
+                 ...
+                 TypeError: ('Could not adapt', 0, """ \
+                      """<InterfaceClass zope.interface.interface.I>)
+
+               unless an alternate value is provided as a second
+               positional argument::
+
+                 >>> I(0, 'bob')
+                 'bob'
+
+               If an object already implements the interface, then it will be
+               returned::
+
+                 >>> class C:
+                 ...     zope.interface.implements(I)
+
+                 >>> obj = C()
+                 >>> I(obj) is obj
+                 True
+
+               If an object implements __conform__, then it will be used::
+
+                 >>> class C:
+                 ...     zope.interface.implements(I)
+                 ...     def __conform__(self, proto):
+                 ...          return 0
+
+                 >>> I(C())
+                 0
+
+               Adapter hooks (see __adapt__) will also be used, if present:
+
+                 >>> from zope.interface.interface import adapter_hooks
+                 >>> def adapt_0_to_42(iface, obj):
+                 ...     if obj == 0:
+                 ...         return 42
+
+                 >>> adapter_hooks.append(adapt_0_to_42)
+                 >>> I(0)
+                 42
+
+                 >>> adapter_hooks.remove(adapt_0_to_42)
+                 >>> I(0)
+                 Traceback (most recent call last):
+                 ...
+                 TypeError: ('Could not adapt', 0, """ \
+                      """<InterfaceClass zope.interface.interface.I>)
+
+            """
+            conform = getattr(obj, '__conform__', None)
+            if conform is not None:
+                try:
+                    adapter = conform(self)
+                except TypeError:
+                    # We got a TypeError. It might be an error raised by
+                    # the __conform__ implementation, or *we* may have
+                    # made the TypeError by calling an unbound method
+                    # (object is a class).  In the later case, we behave
+                    # as though there is no __conform__ method. We can
+                    # detect this case by checking whether there is more
+                    # than one traceback object in the traceback chain:
+                    if sys.exc_info()[2].tb_next is not None:
+                        # There is more than one entry in the chain, so
+                        # reraise the error:
+                        raise
+                    # This clever trick is from Phillip Eby
+                else:
+                    if adapter is not None:
+                        return adapter
+
+            adapter = self.__adapt__(obj)
+
+            if adapter is None:
+                if alternate is not marker:
+                    return alternate
+                
+                raise TypeError("Could not adapt", obj, self)
 
-        if adapter is None:
-            raise TypeError("Could not adapt", ob, self)
+            return adapter
 
-        return adapter
+        return __call__
 
+    __call__ = __call__() # TRICK! Make the *real* __call__ method
 
-    def __adapt__(self, ob):
+    def __adapt__(self, obj):
         """Adapt an object to the reciever
 
            This method is normally not called directly. It is called by
@@ -675,8 +702,8 @@
              >>> class C:
              ...     zope.interface.implements(I)
 
-             >>> ob = C()
-             >>> I.__adapt__(ob) is ob
+             >>> obj = C()
+             >>> I.__adapt__(obj) is obj
              True
 
            Adapter hooks can be provided (or removed) to provide custom
@@ -685,8 +712,8 @@
            list::
 
              >>> from zope.interface.interface import adapter_hooks
-             >>> def adapt_0_to_42(iface, ob):
-             ...     if ob == 0:
+             >>> def adapt_0_to_42(iface, obj):
+             ...     if obj == 0:
              ...         return 42
 
              >>> adapter_hooks.append(adapt_0_to_42)
@@ -702,11 +729,11 @@
              >>> I.__adapt__(0)
 
            """
-        if self.providedBy(ob):
-            return ob
+        if self.providedBy(obj):
+            return obj
 
         for hook in adapter_hooks:
-            adapter = hook(self, ob)
+            adapter = hook(self, obj)
             if adapter is not None:
                 return adapter
 




More information about the Zodb-checkins mailing list