[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