[Zope3-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 Zope3-Checkins
mailing list