On Dec 17, 2007 11:44 PM, Ian Bicking <[EMAIL PROTECTED]> wrote:
>
> Mike Orr wrote:
> > There are two ways to go with the loader:
> >     t = loader("mytemplate.html")
> >     output = renderer.render(t, variables)
> > Or:
> >     output = renderer.render("mytemplate.html", variables)  # Uses
> > loader in Renderer's constructor.
> >
> Possibly you could declare to the loader what the renderer for the
> template is when you load it?  Then it would be a bit more streamlined:
>
>    t = loader("mytemplate.html", renderer)
>    # using the string.Template method name:
>    t.substitute(variables)
>
> I was trying to keep the template object "light", in that it didn't *do*
> much but just contained some different things the loader really
> provided, but should provide on-demand (like opening a file or reading
> into a string).  But maybe that was unnecessary.
>
> > In applications, users choose the renderer, then the template.  Or
> > they leverage the default renderer, which is the same thing.  Nobody
> > has written code to choose a renderer based on the template.  So
> > having the renderer manage the loader makes sense.
> >
> > This brings us to another issue.  In a multi-renderer deployment,
> > should we recommend a common loader or let each renderer have a
> > separate loader instance?  This affects Pylons' environment.py as well
> > as the internal pylons.wsgiapp and pylons.templating.  Some users will
> > want a different templates directory per engine, which necessitates a
> > renderer-specific loader.  Others will pull from the preconfigured
> > 'templates' directory.  But even in that case, the templates
> > themselves are renderer-specific, so the templates are implicitly
> > grouped by renderer even with a common loader.  And if the user
> > instructs two renderers with private loaders to both load the same
> > template file, what harm is done?
>
> I would probably expect a common loader, but I don't know.  If there's a
> common loader, what determines the renderer?  I expect both loader and
> renderer (and renderer options) to be configured once per application,
> then all exposed in one render() function that loads and calls the
> renderer.  If you were using two renderers I'm not sure how that would
> work.  It also seems generally unlikely; those few times when I want to
> dynamically load a renderer (e.g., maybe as an option for paster
> create), it's still just one renderer for any one context.

I'm following your API because you thought it was a good idea.  If
you're having second thoughts about how the Loader-Template-Renderer
should interrelate, we need to decide both their relationship and why
that's a good design.

Currently we have:

    loader = FileLoader("/templates")
    renderer =  get_renderer("genshi")(loader)
    t = loader("mytemplate.html")
    print renderer.render(t, variables)   #  Sets t.native.
    # Or:
    print renderer.render("mytemplate.html", variables)  # Uses
implicit loader.  Sets t.native.

Your suggestion would change it to:

    loader = FileLoader("/templates")
    renderer = get_renderer("genshi")
    t = loader("mytemplate.html", render)
    print t.substitute(variables)    # Comples template and sets t.native.

I don't like the way the renderer sets t.native on an autonomous
foreign object.  But if the template asks for the native format and
stores it in self.native, maybe that would be more natural.

The user views everything from a global render function:

    render("genshi", "mytemplate.html", variables)

This can be a Buffet object or an application object, which holds all
the renderers in a dict.  Given that the app will probably have to do
app-specific things like read specs from a config file and inject
system variables into 'variables', I'm not sure whether a Buffet
organizer object would be useful or not.

This render function translates naturally into:
1) Find the appropriate renderer.
2) Call renderer.render(template_name, variables)   # Uses implicit loader.

So I'm not sure we'd gain anything by putting the renderer inside the template.
+ The template would know how to render itself.
- But it wouldn't know the first time, because the loader can't deduce
the renderer from the template
  source, so we'd have to tell it which renderer to use.
- You'd have to forcibly update t.renderer to share a template between
renderers, or call renderer.render.
  Calling render.render brings us back to where we started, which
makes me doubt that the template
  should contain a renderer.  It probably makes more sense for a
renderer to contain templates,
  which it can either cache itself or let its (private?) loader cache
them.  Having the renderer cache
  templates is closest to what Mako does natively.

> > I still don't see how you think we can pass a Loader to an underlying
> > engine, given that none of the existing engines have hooks for it.
> > You can pass the appropriate attributes to Mako's TemplateLookup and
> > Template constructors, but that's it.  Which then brings us to: should
> > MakoRenderer create a Mako TemplateLookup at instantiation or for each
> > render, or ignore TemplateLookup completely?
>
> I was looking at Jinja just a tiny bit, and it looked like it did have a
> hook for it (with a little futzing and subclassing).  I think it's fair
> for us to push back on the template engines some (really this is nothing
> major we are proposing).  If some don't respond then we just recommend
> people don't use those template languages.

Well, we should work with them to design a Loader object then, rather
than assuming we know better than them what it should do.

> > Mako also has its own template caching, but for that we need to pass
> > in a data directory.  Currently the only way to do that is with an
> > engine-specific option.  But a Loader with caching could do the
> > equivalent.
>
> Hmm... it might be.  The loader cache would probably be entirely
> in-memory.  Caching to disk (.pyc files, I'm guessing?) removes some of
> the warmup cost when starting a new process.  Maybe at least we can have
> a documented option of a somewhat-persistent (but not guaranteed
> persistent) scratch directory that any renderer can use if it wants.

Smorgasbord 0.1 has a loader with optional caching.  For persistence
you just pickle the entire cache to a file, and unpickle it when the
application restarts.  That assumes that native template objects are
pickleable, which we don't really know yet.

> > There will be more issues with template options and rendering options,
> > especially trying to rationalize Pylons' existing built-in options.
> > I'm tempted to move from this:
> >
> >     config['buffet.template_options']   # Default options like 
> > "kid.encoding".
> >     config['buffet.template_engines']  # List of {entry point, root
> > dir, options, alias} dicts.
> >     config.add_template_engine()        # Front end for previous.
> >
> > To this:
> >
> >     config['template_options']['kid']['encoding'] = 'utf8'
> >     config['template_engines'] => dict keyed by engine name
> >     config['default_template_engine'] = 'genshi'
>
> I think we could do more parsing of the config names for some of this.
> I tend to do stuff like this in a lot of my paste deploy configuration
> stuff:
>
> for name, value in app_conf.items():
>      if name.startswith('template '):
>          key = name[len('template ')].strip()
>          template_options[key] = value
>          del app_conf[name]
>
> We could make a function for that and use it more widely in Pylons.

Some config extraction tools in Paste would be very welcome.  Mako had
to implement its own prefix-stripping engine_from_config() because it
wasn't in Paste and it looked like it would be a long time before it
ever was.

-- 
Mike Orr <[EMAIL PROTECTED]>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pylons-devel" group.
To post to this group, send email to pylons-devel@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pylons-devel?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to