[Checkins] SVN: five.pt/trunk/ Add somewhat evil method to hook into grok. Unfortunately grok makes it extremely hard to do this more cleanly.

Vincent Fretin vincent.fretin at gmail.com
Wed Apr 14 11:31:19 EDT 2010


On Wed, Apr 14, 2010 at 4:36 PM, Wichert Akkerman <wichert at wiggy.net> wrote:
> On 4/14/10 16:27 , Vincent Fretin wrote:
>>
>> On Wed, Apr 14, 2010 at 12:06 PM, Wichert Akkerman<wichert at wiggy.net>
>>  wrote:
>>>
>>> Log message for revision 110841:
>>>  Add somewhat evil method to hook into grok. Unfortunately grok makes it
>>> extremely hard to do this more cleanly.
>>>
>>> Changed:
>>>  U   five.pt/trunk/CHANGES.txt
>>>  U   five.pt/trunk/src/five/pt/patches.py
>>>
>>> -=-
>>> Modified: five.pt/trunk/CHANGES.txt
>>> ===================================================================
>>> --- five.pt/trunk/CHANGES.txt   2010-04-14 09:48:30 UTC (rev 110840)
>>> +++ five.pt/trunk/CHANGES.txt   2010-04-14 10:06:30 UTC (rev 110841)
>>> @@ -1,6 +1,11 @@
>>>  Changelog
>>>  =========
>>>
>>> +?.? - unreleased
>>> +~~~~~~~~~~~~~~~~
>>> +
>>> +- Basic support for five.grok templates. [wichert]
>>> +
>>>  0.8 - 2010-01-05
>>>  ~~~~~~~~~~~~~~~~
>>>
>>>
>>> Modified: five.pt/trunk/src/five/pt/patches.py
>>> ===================================================================
>>> --- five.pt/trunk/src/five/pt/patches.py        2010-04-14 09:48:30 UTC
>>> (rev 110840)
>>> +++ five.pt/trunk/src/five/pt/patches.py        2010-04-14 10:06:30 UTC
>>> (rev 110841)
>>> @@ -81,3 +81,26 @@
>>>  ZopeViewPageTemplateFile.__get__ = get_bound_template
>>>  PageTemplateFile.__call__ = call_template
>>>  PageTemplateFile.macros = property(get_macros)
>>> +
>>> +try:
>>> +    from five.grok.components import ZopeTwoPageTemplate
>>> +
>>> +    _tpf  = FiveViewPageTemplateFile(__file__)
>>> +    class GrokViewAwarePageTemplateFile(ViewPageTemplateFile):
>>> +        def pt_getContext(self, *args, **kw):
>>> +            global _tpf
>>> +            return _tpf.pt_getContext(*args, **kw)
>>> +        def pt_render(self, namespace, **kw):
>>> +            if "args" in namespace:
>>> +                del namespace["args"]
>>> +            context=namespace.pop("context")
>>> +            request=namespace.pop("request")
>>> +            view=namespace["view"]
>>> +            return self.__call__(_ob=view, context=context,
>>> request=request, **namespace)
>>> +
>>> +    def setFromFilename(self, filename, _prefix=None):
>>> +        self._template = GrokViewAwarePageTemplateFile(filename,
>>> _prefix)
>>> +    ZopeTwoPageTemplate.setFromFilename = setFromFilename
>>> +except ImportError:
>>> +    pass
>>> +
>>
>> Hi Wichert,
>>
>> It's great you are fixing five.grok+chameleon.
>>
>> I think with the change you did, you don't have the "static" variable
>> in the namespace, Am I wrong?
>
> Could be. I've never used 'static', and I have no idea if it makes sense in
> a Zope 2 context. I know this works for a basic grok.View instance to render
> templates, but I'm not making any further guarantees.
I'm using a static directory in one of my five.grok product, so it
make sense in a Zope2 context. :)

>
>> because you don't call pt_grokContext which call actually getNamespace
>> which call view.default_namespace (where static is defined), and
>> view.namespace (where you can give special variable to the template)
>
> I have no idea what pt_grokContext is or where it comes from. grok
> obfuscates the standard code paths by inserting its own PageTemplate class
> (see grokcore.view.components.PageTemplate) in the middle of the
> standard call chain for views. In other words: on a grok.View instance the
> template instance variable is not the template at all, but some
> man-in-the-middle thing grok invented. I am not sure why that extra class is
> needed; it looks like everything it does could be done in grok.View.render
> itself. It did make figuring this out and debugging it quite hard - there
> are half a dozen *PageTemplate* classes involved, each of which is a little
> bit different.
Yes, you're right about the man-in-the-middle. :)

>
> The special grok PageTemplate thing has a render method which looks like
> this:
>
>    def render(self, view):
>        namespace = self.getNamespace(view)
>        template = self._template
>        namespace.update(template.pt_getContext())
>        return template.pt_render(namespace)
>
> Note that there is no mention of pt_grokContext there, or anywhere else in
> grokcore.view.

But the executed render method is not the one from
grokcore.view.component.PageTemplate, but in the one in
five.grok.components.ZopeTwoPageTemplate right?

The template file factory from five.grok.templatereg overwrite the one
from grokcore.view.templatereg right?

And so we have a ZopeTwoPageTemplate instance in the template
attribute of a five.grok.View instance, not a
grokcore.view.component.PageTemplate instance, right?

The render method from five.grok.components.ZopeTwoPageTemplate looks like this:
class ViewPageTemplateFile(BaseViewPageTemplateFile):

    def pt_getContext(self):
        c = super(ViewPageTemplateFile, self).pt_getContext()
        if hasattr(self, 'pt_grokContext'):
            c.update(self.pt_grokContext)

        return c

class ZopeTwoPageTemplate(PageTemplate):

    def setFromString(self, string):
        self._template = ViewAwareZopePageTemplate(id=None, text=string)

    def setFromFilename(self, filename, _prefix=None):
        self._template = ViewPageTemplateFile(filename, _prefix)

    def render(self, view):
        namespace = self.getNamespace(view)
        template = self._template.__of__(view)
        template.pt_grokContext = namespace
        return template()

so template.__call__ is called, not template_pt_render here. and with
your patch, template is a five.pt.pagetemplate.ViewPageTemplateFile
instance which doesn't know anything about pt_getContext and pt_render
if I'm not wrong here.

Can you put a pdb in pt_getContext of your patch ? If it doesn't
block, then I'm right. :)


>
> Now I know that in the five.grok case the namespace parameter for pt_render
> is essentially ignored; somewhere else in the five.pt stack it does its own
> pt_getContext and uses that.
it's normal the first parameter is ignored because
Products.PageTemplates.PageTemplate.PageTemplate.pt_render have a
different signature that
zope.pagetemplate.pagetemplate.PageTemplate.

Products.Five.browser.pagetemplatefile.ZopeTwoPageTemplateFile
inherits from Products.PageTemplates.PageTemplateFile.PageTemplateFile
which inherits from
Products.PageTemplates.PageTemplate.PageTemplate and it defines
pt_render(self, source=False, extra_context={})   (here the first
parameter is not the namespace. To create the namespace, indeed
pt_getContext is called and the result is updated with extra_context)
Then the pt_render method of
zope.pagetemplate.pagetemplate.PageTemplate is called with the created
namespace as the first parameter.


>
>> But more I read the code, more I'm lost. Is the pt_render and
>> pt_getContext really executed here ? because the
>> ViewPageTemplateFile.__call__  doesn't use these methods AFAICS
>> I'm not sure. Maybe the code should only be:
>
> You missed the magic PageTemplate man-in-the-middle attack from grok :)

Does the patched FiveViewPageTemplateFile still use the original
pt_getContext method? I don't think so. For me it uses the namespace
returned by the _pt_get_context method.
Well I should launch a Plone4 with five.grok+five.pt to test it to be
sure. I should try the second implementation I proposed in the
previous mail to be sure.

Vincent


More information about the checkins mailing list