Edgewall Software

Genshi Recipes: Using Genshi with Pylons

In Pylons 0.9.6rc1, you need to replace template_engine='mako' with template_engine='genshi' in config/environment.py.

Apparently as of Pylons 0.9.5, if you have Genshi installed you may invoke Genshi templates just by using render_response("genshi","<templatename>"). The instructions below are helpful for making genshi the "default" templates.

To use Genshi with Pylons 0.9.4 and below, you need to take some additional steps, which is what's going to be explained here. See also the Pylons Cookbook variant

Adding Genshi Template Engine

First you need some changes in your application's middleware.py.

Genshi only

    # Load our Pylons configuration defaults
    config = load_environment()
    config.init_app(global_conf, app_conf, package='<appname>')

    # Setup Genshi(only) Template Engine
    config.template_engines = []
    config.add_template_engine('genshi', '<appname>.templates', {})

Genshi and Myghty

    # Load our Pylons configuration defaults
    config = load_environment()
    config.init_app(global_conf, app_conf, package='<appname>')

    # Setup Genshi Template Engine
   myghty = config.template_engines.pop()
   config.add_template_engine('genshi', '<appname>.templates', {})
   config.template_engines.append(myghty)

Finally, the Pylons template plug-ins currently expect paths to act as module imports, so you will also need to create an empty __init__.py file inside appname/templates.

WebHelpers and Genshi

To use WebHelpers within a Genshi template, the helper return values need to be wrapped in a Markup object, as it would otherwise be escaped. Think carefully before following this suggestion, however, as this effectively works around the white-listing effect of the Markup object, automatically running any HTML that users may submit to your site. It is safer (albiet much more annoying) to manually wrap specific WebHelpers? in your pages.

The Hard Way

You can do for example(I won't explain the template, for that consult the Genshi Guide):

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      py:strip="">
<head py:match="head">
  <title>The Title</title>
</head>
  <body>
     <h1>hello</h1>
     ${XML(h.<helper>)}
  </body>
</html>

...but the above becomes quite painfull when using several WebHelpers.

The Easy Way

The solution is to wrap all helper functions into Markup objects. For that, edit <appname>/lib/helpers.py and add:

from genshi.core import Markup

def wrap_helpers(localdict):
    def helper_wrapper(func):
        def wrapped_helper(*args, **kw):
            if not callable(func(*args, **kw)):
                return Markup(func(*args, **kw))
            else:
                return Markup(func(*args, **kw)())
        wrapped_helper.__name__ = func.__name__
        return wrapped_helper
    for name, func in localdict.iteritems():
        if not callable(func) or not func.__module__.startswith('webhelpers.rails'):
            continue
        localdict[name] = helper_wrapper(func)
wrap_helpers(locals())

Note: as of WebHelpers 1.0 the webhelpers.rails package has been removed and it seems the wrap_helpers workaround above isn't required anymore(?)


Now edit <appname>/config/middleware.py and just add:

from <appname>.lib import helpers

The above will cause all helpers to be wrapped uppon application initialization.

Now you can use the WebHelpers like you used to with Myghty, for example:

<!DOCTYPE html
    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      py:strip="">
<head py:match="head">
  <title>The Title</title>
</head>
  <body>
     <h1>hello</h1>
     ${h.<helper>}
  </body>
</html>

Final Thoughts

You might also think about adding to <appname>/lib/helpers.py:

from genshi.builder import tag

The Genshi tag object is just like the content_tag() WebHelper, but it will be faster since it does not have to pass through the wrapper.

And that's all....

Last modified 14 years ago Last modified on Jun 4, 2010, 12:38:55 AM