[Zope-dev] External Method Missery

Shane Hathaway shane@digicool.com
Wed, 12 Jul 2000 08:43:00 -0400


Steve Alexander wrote:
> 
> Chris Withers wrote:
> > I have an external method called navTree (dtml-tree was too broken to
> > fix in the time frame :( ) with a spec as follows:
> >
> > def navTree(self,start):
> >
> > It's called in some DTML as:
> >
> > <dtml-var "nav_tree(PARENTS[-2])">
> >
> > which is fine, unless I call it with the following:
> >
> > <dtml-var "nav_tree(start=PARENTS[-2])">
> >
> > in which case I get:
> > TypeError: not enough arguments; expected 2, got 0
> >
> So, in the first case, you're not getting the current context passed in,
> but you are getting it in the second case.
> 
> ...
> Looks like it is assumed that the first non-keyword argument should be
> passed as the client (ie "self").
> 
> So, you can fix your exception by giving "start" a default value:
> 
>   def navTree(self,start=''):
> 
> However, you'll have to always use the keyword form of calling it:
> 
>   <dtml-var "nav_tree(start=PARENTS[-2])">
> 
> Or otherwise, provide a client for it:
> 
>   <dtml-var "nav_tree(this(), PARENTS[-2])">
> 
> As for why this is the case... I have other things to do this morning,
> so I won't go rooting around in the DTML source just now. [ Although, it
> sure is tempting :-) ]

The problem is near the bottom of ExternalMethod.py.  The PythonMethod
product is based partly on ExternalMethod, so I'm now familiar with the
invocation mechanism.

Here's the logic: ExternalMethod sets up func_* attributes so it can
masquerade as a function.  The trick works well enough to convince
ZPublisher's mapply() to pass in a "self" argument as the first
argument when needed.

However, if you invoke the method directly (as you're doing here),
mapply is not part of the mechanism.  So you're supposed to provide the
"self" argument manually.  This is nonintuitive IMHO.

BUT if you don't provide the "self" argument, and ExternalMethod can
see that the method signature includes "self" as the first argument,
ExternalMethod will try to call the method again with the self argument
prepended.

So if you have arguments with defaults or use variable argument lists,
that last algorithm falls to pieces.  The solution is to always provide
the "self" argument.

Shane