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
signature.asc
Description: Digital signature
_______________________________________________ Zope3-users mailing list Zope3-users@zope.org http://mail.zope.org/mailman/listinfo/zope3-users