[Grok-dev] Re: first thoughts on "regebro-guido-templates"

Martijn Faassen faassen at startifact.com
Mon Oct 29 15:55:25 EDT 2007


Brandon Craig Rhodes wrote:
> I have many things to say about the "regebro-guido-templates" branch,
> but since it's now after midnight, this email will only include two
> thoughts - which, if they get addressed in the branch, might start
> clearing up smaller issues that I won't mention for now.

Thanks for putting so much thought into this!

Good thing it was after midnight so your mail is only this long? :)

> The two subjects are:
> 
>  1) Why self.__grok_module__ keeps getting involved.
>  2) Putting template logic together into one class.
> 
> My ideas:
> 
>  1) The question was raised: Why does the new template code require
>     template plugin classes to do this in their __init__() functions:
> 
>         self.__grok_module__ = martian.util.caller_module()
> 
>     This should go away.  What is it trying to do?
> 
>     I have looked around, and noticed that the old-fashioned Grok
>     template logic does exactly the same thing.  And the reason why
>     hinges on an interesting asymmetry: a Grok programmer creates
>     Models and Views through subclassing, but he creates Templates
>     through instantiation!
> 
>     Since martian is built around the idea of grokking classes, it
>     only pays attention to objects whose __module__ is the same as
>     that of the module it's searching.

Well, Martian works just fine for grokking instances too. When you talk 
about I think some stuff is coming back to me. We need to set this bit 
as otherwise Grok will ignore the instance. Grok ignores any object 
found in a module that has a module set that isn't the module it's 
currently in. This is to prevent imported objects (typically classes, 
but could be grokked) from being grokked. We only want locally *defined* 
classes and instances being Grokked.

[snip]
>     Approach A: Make templates classes.
> 
>         This would abandon the asymmetry above, and require users to
>         create a class for each inline template they wanted to create.
>         Instead of having class-instance pairs like:
> 
>             class AView(grok.View):
>                 ...
>             aTemplate = grok.PageTemplate("...")
> 
>         they would have pairs of classes, something like:
> 
>             class AView(grok.View):
>                 ...
>             class ATemplate(grok.PageTemplate):
>                 content = '...'

While somewhat attractive, this would break other places where we rely 
on grokking instances. Oddly enough, a grok.Indexes subclass is in fact 
an *instance*, due to some Python voodoo magic I don't pretend to fully 
understand. So this strategy won't make this go away completely yet.

Actually this is somewhat attractive for inline templates for another 
reason - inline templates tend to grow a larger and larger list of 
arguments that might go away this way.

[snip]
>     Approach B: Teach martian about instances explicitly.
> 
>         This would maintain the asymmetry between View classes and
>         inline Template instances, but rather than requiring the
>         implementor to practice witchcraft, would introduce a new
>         martian directive "instances()" that tells martian to pay
>         attention to instances of a class, not just the class itself.
>         So template classes would look like:
> 
>             class GenshiTemplate(...):
>                 grok.instances()
>                 ....
> 
>         instead of having to set __grok_module__ on every single
>         dratted instance that it wants martian to pay attention to.

I'm not sure I understand this approach.

As far as I know, this wouldn't stop instances being imported from 
another module being grokked as well. Basically the fundamental issue is 
that if we want to prevent imported things from being grokked, we need 
to know where things are defined first. Unless there's another way to 
figure out where an instance is defined that we haven't discovered the 
only thing we can do is set it in definition time, which in case of 
isntances is instantiation time, so in __init__().

[snip]
>     Approach C: make Views link to their inline templates explicitly
> 
>         Magically associating the View my.Foo with the contents of the
>         file "my_templates/foo.pt" is great.  But how important is it,
>         really, to magically associate a variable named "foo" with the
>         View?
> 
>             class Foo(grok.View):
>                 ...
>             foo = grok.PageTemplate(...)
> 
>         Couldn't we instead have the View state that it wanted to use
>         an inline template like this?
> 
>             foo = grok.PageTemplate(...)
>             class Foo(grok.View):
>                 grok.template(foo)
>                 ...
> 
>         Then we would not need magic to grok instances at all.

Yes, this is my favorite approach for now. Looks clean enough, as it's 
right: the auto-association for inline templates isn't that common. The 
only drawback is that it would make some sample code look a bit more 
verbose.

>     Approach D: search for templates without grokking them
> 
>         Why grok instances of templates at all?  Template files like
>         "foo.pt", after all, aren't grokked!  They're discovered by a
>         little routine that scans the _templates directory and looks
>         for appropriate files.  Instead of grokking template
>         instances, why not just explicitly scan each module for
>         template instances in a search that exactly parallels the
>         search for template files?  The scans could be run and compare
>         their findings for conflicts first ("template 'foo' is defined
>         both in 'foo.pt' and in-line!"), then offer the resulting
>         collection of templates to the association routine.  This
>         could, I think, be done straightforwardly by adding a
>         findModule() routine to templatereg.py that parallels the
>         structure of findFilesystem().

I don't think this is a good idea. We have a whole system that has 
extensive tests called Martian to do this kind of scanning. We shouldn't 
implement ad-hoc systems. In addition, I don't see how this would 
actually solve the underlying problem, which is to recognize that an 
instance is defined in the same module, not somewhere else.

>     Those wiser, more experienced, and more awake ought to weigh in on
>     these approaches.  I think I will have to read through them again
>     tomorrow before even starting to decide which one I myself like
>     best. :-)

I like approach C best. If other people like this, I'd suggest we don't 
wait for this to get implemented though before we merge the branch. It's 
only a small blemish we can live with for a while to have to add in that 
silly line.

>  2) I think the reason that "regebro-guido-templates" offers a clumsy
>     and verbose mechanism for plugging in new templates is that they
>     copied the two-class way that Grok already does it, which is
>     clumsy and verbose because Grok itself inherited it from
>     zope.pagetemplate. :-)
> 
>     By "two-class", I mean that you have to create one class for
>     inline templates, and another class for templates that are stored
>     in files.  I think these two situations should be collapsed into
>     one class, so that the person plugging in a new template language
>     just writes one class like:
> 
>     class MyPageTemplateLanguage(grok.PageTemplateLanguage):
>         grok.name('mtl')
> 
>         def setup(self, filename, content):
>             self._template = genshi.Template(content)
> 
>         def render(self, view):
>             return self._template.render(**self.namespace(view))

This sounds attractive! Just one class would definitely be better than 
having to do two. How to go about collapsing this? Lennart, does this 
look doable?

> Okay, I'm too tired to keep typing now.

Good, as I can only absorb so much at any reading. :)

> I'll outline more about a
> single-class approach tomorrow, maybe.  And then I can get started on
> how the namespace() situation could perhaps be simplified and
> streamlined a bit.  But, tomorrow.

In order to get this merged, I'd suggest we prioritize as follows:

* single-class approach - would be nice to do this before merge if this 
is doable with a moderate amount of work. This would make the extension 
story that much cleaner and clearer.

* make grok.template() understand instances so we can get rid of 
__grok_module__ requirement: would be nice but could be done at a later 
stage. We need to examine the consequences of this.

* namespace() situation - I think the design is flexible enough to work 
with now. If these are minor changes, by all means. But let's not go too 
deeply into the philosophy of this stuff before we gain a bit more usage 
experience and integrate a few more template languages and examine the 
use cases then. Interesting candidate languages that might exercise our 
pluggability a bit more: Mako, Templess, XSLT

Regards,

Martijn



More information about the Grok-dev mailing list