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

Darryl Cousins darryl at darrylcousins.net.nz
Sun Oct 28 18:10:50 EDT 2007


On Sun, 2007-10-28 at 01:22 -0400, 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.
> 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.  This works wonderfully for
>     classes: it makes martian ignore classes you've brought into your
>     module through "import", since their __module__ names the one they
>     were imported from, not the one martian's currently scanning.  But
>     it has the side-effect of making martian completely ignore class
>     instances defined in another module from the class itself, because
>     Python instances don't have their own __module__ attribute - they
>     inherit the value from their class instead.  (For details, grep
>     through martian for the "locally_defined" function.)
>     And, again, all of this causes problems only because of the
>     asymmetry between how you create, say, a view:
>         class MyView(grok.View):
>             ...
>     and how you create a template:
>         myTemplate = grok.PageTemplate("...")
>     which produces a mere instance.  This all brings us back to that
>     ugly:
>         self.__grok_module__ = martian.util.caller_module()
>     call which, we now see, is an attempt to fool martain into looking
>     at instances by stating which module they "belong to".
>     I will suggest four alternative approaches, any of which I think
>     would make things far simpler, and it would be really nice if the
>     "regebro-guido-templates" branch was converted to one of them
>     before being merged.  I'm willing to write code. :-)
>     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 = '...'
>         This might have other advantages involving the ability to mark
>         up templates with the same sorts of directives we use for
>         other things in Grok.  For example, a Template could accept a
>         grok.context(...) (or grok.view(...)?) statement explicitly
>         stating the View it should work with, just like Views can
>         state the objects that they adapt.  Or maybe that would never
>         be useful? :-)

This approach is pretty much what I use for mars.template. Please see:

The ftests in the package use the old grok method of doctests but should
provide additional documentation on how it all works.

Disclaimer: I haven't been back to the mars packages for 3 months as
I've been busy with other projects. Obviously therefore they will be
broken against recent grok but the code may be worth a look still.

Best regards,

>     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.
>         This would involve the difficult decision as to whether
>         grok.instances() should be inherited by subclasses of a class,
>         or whether each of them should have to repeat the directive in
>         order to make its own instances discoverable.  The latter
>         seems the safer approach but I'm too tired at this point to
>         ponder all the issues.
>     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.
>     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().
>     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. :-)
>  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))
> Okay, I'm too tired to keep typing now.  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.

More information about the Grok-dev mailing list