[Zope3-dev] calling objects when evaluating path expressions
Steve Alexander
steve@cat-box.net
Fri, 20 Jun 2003 13:56:41 +0300
Consider the following class:
class Foo:
def __init__(self, baz):
self.baz = baz
def bar(self):
return self.baz
class Baz:
spoo = 23
Let's say that in a page template, the name 'foo' is bound to Foo(Baz()).
Before yesterday, the path expression 'foo/bar' returned a Baz instance.
So:
<span tal:define="mybaz foo/bar">
<span tal:replace="mybaz/spoo" />
</span>
would give:
<span>
23
</span>
However, the following gives an unexpected result.
<span tal:replace="foo/bar/spoo" />
result:
<bound method baz of <Foo object at 0x92c668c>>
Conclusion 1: We should be calling some things, such as bound methods
that have no unbound positional arguments.
The TALES machinery will automatically call the results of a path
expression, unless you explicitly tell it not to with the magic word
'nocall'. That's why the first example above works.
However, if the method required any arguments, there would have been an
error. In any case, the behaviour should be consistent whether a
potentially-callable thing comes at the end of a path expression, or
somewhere in the middle.
Conclusion 2: TALES should not try to call things at all. This should be
left up to the Zope traversal components, so that the behaviour may be
customised.
In Zope 3, a Component is defined as anything that provides an
interface. So in the example that follows, Fish instances are
components, Tree instances aren't. Note that BoringComponent instances
are components because they do actually provide some interface.
class Fish:
implements(ISwimmer)
class Tree:
pass
class BoringComponent:
implements(Interface)
When an object provides an interface, it kind of says "I'm taking
responsibility for explicitly saying how I should be used". If an object
is a component, we should base a decision whether to call it only on the
interfaces it provides. We should not inspect what methods a component
has, because that is going "behind its back", and not respecting its
wish to be explicit about its responsibilities.
Conclusion 3: Any implicit decision to call objects while evaluating a
path expression should apply only to objects that are not Components.
Proposal:
I propose the following:
1: Remove from Zope 3 the TALES behaviour of calling the result of a
path expression if it has a __call__ attribute.
2: Deprecating the 'nocall' magic word, as it will no longer be
needed.
3: Making the zope 3 default traversal machinery call an object on
traversal if and only if the following conditions hold:
a: the object provides no interfaces
b: the object has a __call__ attribute
c: on inspection, it appears that the __call__ attribute can be
called with no arguments
This will most usefully apply to instance methods, static methods,
class methods and method-wrappers (which are how builtin methods
appear to Python).
--
Steve Alexander