[Zope3-Users] Weird behaviour of ViewPageTemplateFile()

Marius Gedminas mgedmin at b4net.lt
Thu Apr 10 10:30:44 EDT 2008


Hi!

On Thu, Apr 10, 2008 at 03:22:33PM +0200, Martin J. Laubach wrote:
>   I just stumbled over something that utterly baffles me and hope
> someone can point out the absolutely obvious to me.

Sure.

>   I've got a view defined in a configure.zcml that points to this class:
>
>     class MyView(BrowserView):
>         def __call__(self):
>             self.pt = ViewPageTemplateFile('empty.pt')
>             data = self.pt()
>             return data
>
>   That works fine.

I'm actually surprised that this works.

The usual way in Zope 3 is

      class MyView(BrowserView):
          pt = ViewPageTemplateFile('empty.pt')
          def __call__(self):
              data = self.pt()
              return data

which can also be shortened to

      class MyView(BrowserView):
          __call__ = ViewPageTemplateFile('empty.pt')

> However, I don't really need pt as instance variable,
> so I turned it into a local variable:
>
>     class MyView(BrowserView):
>         def __call__(self):
>             pt = ViewPageTemplateFile('empty.pt')
>             data = pt()
>             return data
>
>   And that version throws an exception:
>
>     Traceback (innermost last):
>       Module ZPublisher.Publish, line 119, in publish
>       Module ZPublisher.mapply, line 88, in mapply
>       Module ZPublisher.Publish, line 42, in call_object
>       Module mjl.example.browser.myform, line 31, in __call__
>       Module Shared.DC.Scripts.Bindings, line 313, in __call__
>       Module Shared.DC.Scripts.Bindings, line 348, in _bindAndExec
>       Module Shared.DC.Scripts.Bindings, line 1, in ?
>       Module Shared.DC.Scripts.Bindings, line 293, in _getTraverseSubpath

This does not look like Zope 3 to me.

>     AttributeError: 'str' object has no attribute 'other'

Okay, I know why the code doesn't work, but I don't know why you get
this strange Zope 2 error.  I'll answer just the first part.

>
>   Same if I just do the simpler "return ViewPageTemplateFile('empty.pt')()"
> of course.
>
>   I simply do not understand why, what or who does care how I name my
> variables or where I put them? Please hit me hard with a cluebat. Twice.

You have encountered the magic of Python descriptors.  Feel free to skip
the detailed explanation if you're in a hurry:

    ViewPageTemplateFile is a descriptor, which is a fancy way of saying
    "object that has a method named __get__".  When you access a descriptor
    from an instance, Python silently calls __get__ for you, so 

      class MyView(BrowserView):
          pt = ViewPageTemplateFile('empty.pt')
          def __call__(self):
              pt = self.pt()

    is more or less equivalent to

      class MyView(BrowserView):
          def __call__(self):
              pt = ViewPageTemplateFile('empty.pt')
              pt = pt.__get__(self, ViewPageTemplateFile)

    ViewPageTemplateFile.__get__ returns a BoundPageTemplate object
    (it's a bit like the difference between unbound and bound methods)
    that knows which view it is bound to and can pass it via the "view"
    name to your TALES expressions.

    Now when you invoke ViewPageTemplateFile's __call__, you have to
    pass the view as the first argument:

      class MyView(BrowserView):
          def __call__(self):
              pt = ViewPageTemplateFile('empty.pt')
              return pt(self)

    but when you invoke BoundPageTemplate.__call__, it knows and
    provides the view argument itself:

      class MyView(BrowserView):
          def __call__(self):
              pt = ViewPageTemplateFile('empty.pt')
              bound_pt = pt.__get__(self, ViewPageTemplateFile)
              return bound_pt()

    which is more or less the same as

      class MyView(BrowserView):
          pt = ViewPageTemplateFile('empty.pt')
          def __call__(self):
              bound_pt = self.pt
              return bound_pt()

    [End of explanation]

So, if you really want to use a local variable instead of a class
attribute, pass the view itself as the first argument to __call__

      class MyView(BrowserView):
          def __call__(self):
              pt = ViewPageTemplateFile('empty.pt')
              data = pt(self)
              return data

HTH,
Marius Gedminas
-- 
Anyone can do any amount of work provided it isn't the work he is supposed
to be doing at the moment.
                -- Robert Benchley
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
Url : http://mail.zope.org/pipermail/zope3-users/attachments/20080410/8b2a4cc5/attachment.bin


More information about the Zope3-users mailing list