Hi again.

Hanno Schlichting wrote:
I kept my promise and added the simple tests for the first two issues I found while doing testing against Plone.

I have meanwhile fixed the first trivial issue (conflicting argument called 'instance') and added a simple test for the second one.

Now after a good night of sleep here is the condensed version of the problem, for those too lazy to look at the branch and the commits:

All code is in Products.Five.browser. In .tests.aqlegacy.py we define two views:

class LegacyTemplate(BrowserView):

    template = ViewPageTemplateFile('falcon.pt')

    def __call__(self):
        return self.template()

class LegacyTemplateTwo(BrowserView):

    def __init__(self, context, request):
        self.__parent__ = context
        self.context = context
        self.request = request
        self.template = ViewPageTemplateFile('falcon.pt')

    def __call__(self):
        return self.template()

Both are registered in ZCML as:

<browser:page
  for="*"
  name="template"
  class=".aqlegacy.LegacyTemplate"
  permission="zope.Public"
  />

<browser:page
  for="*"
  name="template_two"
  class=".aqlegacy.LegacyTemplateTwo"
  permission="zope.Public"
  />

And in the aqlegacy_ftests.txt we call both of them via:

>>> view = getMultiAdapter((self.folder, request), name='template')
>>> view.template
<BoundPageTemplateFile of <Products.Five.metaclass.LegacyTemplate ...>>
>>> print view()
<p>The falcon has taken flight</p>


>>> view = getMultiAdapter((self.folder, request), name='template_two')
>>> view.template
<Products.Five.browser.pagetemplatefile.ViewPageTemplateFile ...>
>>> print view()
TypeError: __call__() takes at least 2 arguments (1 given)


Now as you can see the only difference is that one of them uses a class variable for assigning the template and the other one is using an instance variable.

From what I understand some magic place in between doesn't find the template instance variable during ZCML processing as it operates on classes only and therefor doesn't turn the template into a BoundPageTemplateFile.

From what I can tell the purpose of the BoundPageTemplateFile is to inject the view class into the method call of __call__, so you don't have to pass it in explicitly for each call. It does so, via some interesting code which boils down to:

class BoundPageTemplate(object):
    def __init__(self, pt, ob):
        object.__setattr__(self, 'im_func', pt)
        object.__setattr__(self, 'im_self', ob)

    def __call__(self, *args, **kw):
        if self.im_self is None:
            im_self, args = args[0], args[1:]
        else:
            im_self = self.im_self
        return self.im_func(im_self, *args, **kw)


Using an instance variable called template and calling it later on without passing in the view as the first argument doesn't work at all in Zope3. In Zope2 it did so far, as the ViewPageTemplateFile would use Acquisition to find its view.

I don't have any good idea on how to handle this problem. We can probably walk up the stack frame to find the view in most cases, as the template is called in almost all cases directly from the view.

But the third test failure, which I haven't written a test for yet, breaks even then. Essentially it puts in an adapter in between the view and the template where the adapter doesn't have any reference to the view anymore, so getting to the view from the template is impossible even by walking up the stack frame. This use-case is highly specialized (the code is in plone.app.form._named) but currently it works in Zope2.

Ideas from people who know more about this side of Zope are still most welcome :)

Hanno

_______________________________________________
Zope-Dev maillist  -  Zope-Dev@zope.org
http://mail.zope.org/mailman/listinfo/zope-dev
**  No cross posts or HTML encoding!  **
(Related lists - http://mail.zope.org/mailman/listinfo/zope-announce
http://mail.zope.org/mailman/listinfo/zope )

Reply via email to