Mike Orr wrote:
> On Dec 17, 2007 12:10 PM, Ian Bicking <[EMAIL PROTECTED]> wrote:
>> Mike Orr wrote:
>>> StringTemplateRenderer.__init__ takes a loader, but
>>> StringTemplateRenderer.render doesn't use it. Isn't it supposed to?
>>> Or am I supposed to load the template separately?
>> It doesn't use it because template_object is an already-loaded object.
>> If it allowed for template references (something like #extends) then it
>> would use self.loader for that. Though... maybe template_object should
>> just have a loader attribute or method? That would be a method on
>> FileSource in the reference loader. That would remove the loader from
>> the renderer object almost entirely, which would make things a little
>> cleaner I think.
>>
>>> Also, the last line of .render should have 'template_object' instead of
>>> 'tmpl'.
>> Thanks, fixed. Do you want commit there? If so, send me a line from
>> htpasswd.
>
> Thanks, I'll do it when I get home.
>
> Right now I've got minimal Mako and Genshi renderers, and a Nose test
> for them. They don't take options yet.
>
> I've also refactored things a bit to straighten out the terminology in
> preparation for user docs. (TemplateProposal doesn't say how to *use*
> it.) I want to focus on:
>
> - Engine: the underlying template engine.
>
> - Renderer: the interface between user code and the underlying engine.
> ("plugin" or "engine" in Buffet 1) This is sometimes called "engine"
> or "template" in TemplateProposal, which is confusing.
>
> - Template (=> FileTemplate), a wrapper for template source code.
> TemplateProposal sometimes calls this something Source, but I think
> that's more cumbersome without being substantially clearer. It's
> perfectly appropriate for TemplateProposal to define what *it* thinks
> a template is, a wrapper around a native template like Renderer is a
> wrapper around an underlying engine.
>
> - Loader (=> FileLoader) is an object that knows how to look up a
> template by name and return a Template instance.
>
> 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.
>
> The latter really makes more sense to recommend. The loader doesn't
> know which renderer a template is compatible with. The user just has
> to "know" which template to feed to which renderer. Why not make
> this explicit and let the renderer manage the loader? That makes the
> object orientation more orderly.
> Of course, we should allow an externally-loaded template to be passed
> in, but that shouldn't be the general recommendation.
Come to think of it, this was how it was originally. It was changed, I
think at Matt Good's suggestion, to make it possible to cache templates
and pass in the cached template object.
Which still might make sense, and lead more to the former technique
(loading the template explicitly, then rendering it).
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.
> This further gets us to template caching. TemplateProposal provides
> cacheable templates but no infrastructure to store them. This
> naturally belongs to the loader. Even if we don't provide a template
> cache, we should at least map out how the user would build one, either
> with a Loader subclass or externally.
Agreed. Also, I would like to include some explicit way to purge or
avoid caching all together (mostly when thinking about the OLPC XO
laptop, where memory is low and caching can be problematic). In general
I'm getting pissed off more often with memory usage in Python
applications than with poor CPU performance.
> StringTemplateRenderer passes the template as a string to the
> underlying engine. Is that because string.Template can't load from a
> filename, or is it recommended for all renderers? I wrote
> MakoRenderer and GenshiRenderer in their preferred manner (Mako takes
> a filename, Genshi a file object). Of course MakoRenderer barely uses
> the Template object at all; it just reads the .real_filename attribute
> and ignores the access methods. This will be the case with most
> Renderers if we go the filename route. But I believe Genshi accepts
> only a file object and nothing else.
string.Template can't read from a file, correct. It also doesn't know
or care about the filename, since it has no fancy exception stuff.
All the methods are provided so that the template language can use its
most preferred technique. It should be remembered that some loaders
might have names that aren't filenames. E.g., some identifier that only
makes sense in the context of a database.
> I think we should support FileLoader and FileTemplate as the standard
> loader & template out of the box, and leave Loader and Template as
> abstract interfaces. 95% of the time users will want file-based
> templates. Template.open() requires a file-like object if the
> template is not file-based, which is fine.
I agree, those two should have standard implementations. Maybe fancy
them up a bit to do search paths (also makes them interesting enough
that people might notice when template renderers shortcircuit them when
they aren't supposed to).
> 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.
> 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.
> 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.
> Either that or *something* in Pylons or the Renderers should extract
> and unprefix the template options so they can be passed directly to
> the underlying engines.
>
> I want to move the default configuration for several renderers into
> some more appropriate structure grouped by renderer. I also want to
> move pylonsmyghty out of pylons.templates into a separate module or
> distribution. But I'm not going to be able to implement pylonsmyghty
> by myself; I couldn't get the unit test to work in Smorgasbord 0.1.
> Can we stop supporting Myghty yet?
--
Ian Bicking : [EMAIL PROTECTED] : http://blog.ianbicking.org
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"pylons-devel" group.
To post to this group, send email to [email protected]
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
-~----------~----~----~----~------~----~------~--~---