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

Brandon Craig Rhodes brandon at rhodesmill.org
Sun Oct 28 01:22:18 EDT 2007


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? :-)

    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.

-- 
Brandon Craig Rhodes   brandon at rhodesmill.org   http://rhodesmill.org/brandon


More information about the Grok-dev mailing list