Jinja2 simplates todo

57 views
Skip to first unread message

Jussi Arpalahti

unread,
May 2, 2012, 4:36:59 AM5/2/12
to aspen-users
Hi.

I wrote a basic Jinja2 simplate renderer (see below). I'd like to clarify some things before I submit a pull request.

- how to decide template encoding? is it configuration's dynamic encoding or what?

- how to implement error handling and pretty error messages?
-> with my current implementation tracebacks go to server console

- change reloading: how to get aspen's information about template updates to jinja2 or how to reload them myself?
-> Jinja2 has excellent support for reloading, but what information is available in Aspen renderer when changes happen

- where to put my renderer in aspen: just as a manually configurable module or put the code inside aspen like tornado and pymustache is?
-> so that I could make a better pull request when the code is finished.

Below is the code. Some values are hard coded to ease testing.

from aspen.rendering import Renderer, Factory

from jinja2 import BaseLoader, TemplateNotFound, Environment, FileSystemLoader, Template

def no():
    return False # Jinja2 asks the given function if template should be reloaded, default is always

class SimplateLoader(BaseLoader):
   
    def get_source(self, environment, template):
        return (unicode(open("root/%s" % template).read(), 'utf-8'), template, no)
   
    def load(self, environment, name, globals=None):
        if not globals.has_key("raw"):
            return super(SimplateLoader, self).load(environment, name, globals)
        else:
            code = environment.compile(globals["raw"].decode("utf-8"), name, name)
            return environment.template_class.from_code(environment, code,
                                                    globals, no)

class Jinja2Renderer(Renderer):
   
    def compile(self, filepath, raw):
        return self.meta.get_template(filepath, globals={"raw":raw})

    def render_content(self, compiled, context):
        return compiled.render(context).encode("utf-8")

class Jinja2Factory(Factory):

    Renderer = Jinja2Renderer

    def __init__(self, configuration):
        self.env = Environment(loader = SimplateLoader())
        super(Jinja2Factory, self).__init__(configuration)

    def compile_meta(self, configuration):
        return self.env

Also, I run into some oddities. When trying to print context argument from Jinja2Renderer.render_content for debugging, it failed with this:

Traceback (most recent call last):
  File "/local/lib/python2.7/site-packages/aspen/website.py", line 69, in handle
    response = request.resource.respond(request)
  File "/local/lib/python2.7/site-packages/aspen/resources/dynamic_resource.py", line 60, in respond
    response = self.get_response(context)
  File "/local/lib/python2.7/site-packages/aspen/resources/negotiated_resource.py", line 153, in get_response
    response.body = render(context)
  File "/local/lib/python2.7/site-packages/aspen/rendering.py", line 122, in __call__
    return self.render_content(self.compiled, context)
  File "/home/arpalah/projekti/yber/root/jinja2_renderer.py", line 35, in render_content
    print context
  File "/local/lib/python2.7/site-packages/aspen/http/request.py", line 236, in __repr__
    return str.__repr__(str(self))
  File "/local/lib/python2.7/site-packages/aspen/http/request.py", line 232, in __str__
    self._raw = fmt % (self.line.raw, self.headers.raw, self.body.raw)
  File "/local/lib/python2.7/site-packages/aspen/http/request.py", line 568, in raw
    self._raw = "".join(self.s_iter)
TypeError

When I mistakenly returned an unicode object directly from Jinja2's template render and not the bytestring, my UTF-8 content was somehow converted to ISO-8859-1. With  --charset_dynamic=UTF-8 --charset_static=UTF-8 the behaviour stayed the same. This relates to my encoding question, that is how to decide on which enconding to use for reading templates and writing responses.

Other than that, developing Jinja2 renderer was rather smooth.


Jussi

Chad Whitacre

unread,
May 4, 2012, 9:03:42 PM5/4/12
to Jussi Arpalahti, aspen-users
Jussi,

I wrote a basic Jinja2 simplate renderer (see below). I'd like to clarify some things before I submit a pull request.

Huzzah! :D


- how to implement error handling and pretty error messages?
-> with my current implementation tracebacks go to server console

Tracebacks to server console is fine for now. There's an option --show_tracebacks to send those to the browser as well. Were you envisioning interactive in-browser debugging or some such?

 
- change reloading: how to get aspen's information about template updates to jinja2 or how to reload them myself?
-> Jinja2 has excellent support for reloading, but what information is available in Aspen renderer when changes happen

Aspen's current strategy is simply to reinstantiate all loader-type objects when --changes_reload is true, by calling compile_meta. Skimming your code I think what you want to do is return a jinja2.Environment from compile_meta, which is close to what you have.
 
 
- where to put my renderer in aspen: just as a manually configurable module or put the code inside aspen like tornado and pymustache is?

I refactored the rendering module into a renderers package. I split out pystache and tornado into their own modules within that package. I also reworked the renderers machinery so that you can perform imports at the top level of your module, and they won't be raised until you try to use the renderer in a simplate. Have a look at those two examples and add jinja2_.py in the renderers subpackage. Also add it to RENDERERS in aspen/__init__.py.

One API change to be aware of: render_content now takes one argument, context. Use self.raw or self.compiled to access the raw bytestring or your compiled template inside of render_content.

 
 Also, I run into some oddities. When trying to print context argument from Jinja2Renderer.render_content for debugging, it failed with this:
Traceback (most recent call last):
    ...
TypeError

Sorry, I believe this is fixed now.


- how to decide template encoding? is it configuration's dynamic encoding or what?
 
When I mistakenly returned an unicode object directly from Jinja2's template render and not the bytestring, my UTF-8 content was somehow converted to ISO-8859-1. With  --charset_dynamic=UTF-8 --charset_static=UTF-8 the behaviour stayed the same. This relates to my encoding question, that is how to decide on which enconding to use for reading templates and writing responses.

Sorry, I believe this is fixed now as well. The --charset_dynamic configuration is the one to use. It wasn't being applied but is now (I added a test as well). This defaults to UTF-8 so hopefully you should find that you don't need to worry about it in your renderer.


Thanks Jussi! Looking forward to merging this in when you have it ready. :)




chad








Jussi

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

Jussi Arpalahti

unread,
May 6, 2012, 3:40:56 PM5/6/12
to Chad Whitacre, aspen-users
On 5 May 2012 04:03, Chad Whitacre <ch...@zetaweb.com> wrote:
Jussi,

I wrote a basic Jinja2 simplate renderer (see below). I'd like to clarify some things before I submit a pull request.

Huzzah! :D


- how to implement error handling and pretty error messages?
-> with my current implementation tracebacks go to server console

Tracebacks to server console is fine for now. There's an option --show_tracebacks to send those to the browser as well. Were you envisioning interactive in-browser debugging or some such?

Nothing too fancy. Just printing tracebacks in the browser should be good (when show_tracebacks is on). I will look into printing the offending part of the template as well. I'm thinking the error page could try to load a javascript module for syntax highlighting.

Of course it would be nice to have a Django like error page with variables and code snippets, or even Werkzeug's interactive Python prompt...


 
- change reloading: how to get aspen's information about template updates to jinja2 or how to reload them myself?
-> Jinja2 has excellent support for reloading, but what information is available in Aspen renderer when changes happen

Aspen's current strategy is simply to reinstantiate all loader-type objects when --changes_reload is true, by calling compile_meta. Skimming your code I think what you want to do is return a jinja2.Environment from compile_meta, which is close to what you have.

Well, there is two parts to this. Simplate's template content is one, but the extended template files are another. For the latter I can use Jinja2's file system loader with its build in reloading. For the former I think I can rely on Aspen to call the necessary methods.
 
 
 
- where to put my renderer in aspen: just as a manually configurable module or put the code inside aspen like tornado and pymustache is?

I refactored the rendering module into a renderers package. I split out pystache and tornado into their own modules within that package. I also reworked the renderers machinery so that you can perform imports at the top level of your module, and they won't be raised until you try to use the renderer in a simplate. Have a look at those two examples and add jinja2_.py in the renderers subpackage. Also add it to RENDERERS in aspen/__init__.py.

One API change to be aware of: render_content now takes one argument, context. Use self.raw or self.compiled to access the raw bytestring or your compiled template inside of render_content.


Ok. I'll try to keep up with the pace of change.. ;)


Jussi
 

Chad Whitacre

unread,
May 8, 2012, 2:51:25 PM5/8/12
to Jussi Arpalahti, aspen-users
Jussi,
 
Ok. I'll try to keep up with the pace of change.. ;)

Only the render_content parameter change should affect your code. The rest was done so you can safely do top-level imports, which it really seems like you want to do with Jinja2 since you're subclassing BaseLoader. I think aspen is stronger as a result. :)



chad

Reply all
Reply to author
Forward
0 new messages