I have been exploring the extensibility of Nevow, and I'm finding it surprisingly easy to integrate other tools. I've only used Genshi for recent Trac hacking, but I hear more and more about it and it seems to be gaining a little momentum as one of the preferred template systems for python web application developers.
Using the code from the blog post Object Publishing with Nevow, I have created an example of how to integrate Genshi with Nevow. I will provide a link to the source code at the end of the post.
Integrating another templating system into Nevow is actually a no-brainer: you just need to have the "alien" template parsed by its own parsers prior to Nevow performing its parsing. This impacts only one module: nevow.loaders.
Before we dig any deeper, how about some eye-candy? Then a quick review, and finally the code. Here are some screenshots of the Genshi templates running along-side native Nevow templates:
The second image is the page in the little demo that showcases all the functionality of Genshi as documented on this page.
Traditionally with Nevow, Page classes contain data_*() and render_*() methods. These Page classes are "web resources" whose locateChild() method is called after an HTTP GET. Each page class that will have an HTML view needs to set a docFactory attribute; this is the loader instance I referred to above.
Here is a toy example of a page class from a previous blog post:
from nevow import rendWhat we need is to be able to do something link this:
from nevow import loaders
class SiteRoot(rend.Page):
docFactory = loaders.xmlstr('''<!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" lang="en" xml:lang="en"
xmlns:nevow="http://nevow.com/ns/nevow/0.1">
</html>''')
from nevow import rendTo make this work, all we have to do is create myloaders.py with the following content:
import myloaders
class GenshiStuffResource(rend.Page):
docFactory = myloaders.genshistr('''<!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" lang="en" xml:lang="en"
xmlns:nevow="http://nevow.com/ns/nevow/0.1"
xmlns:genshi="http://genshi.edgewall.org/">
<h1>Genshi Test</h1>
<div>
<span genshi:with="y=7; x=y**2; z=x+10">$x $y $z</span>
</div>
<div>
<span>Your name is ${username}</span>
</div>
</html>''')
from genshi.template import MarkupTemplateThere is a minimal amount of magic happening here: MarkupTemplate.generate() takes keywords (or a dict, with extended call syntax) and those key/value pairs are available to the template that is rendered. We are using our class's __dict__ for this; so if you want data in your template, you need to make it a class attribute. There are many ways to do this, and you can choose the one that is safest, cleanest, and least consumptive of resources for your application.
from nevow import inevow
from nevow.loaders import xmlstr
class genshistr(xmlstr):
def load(self, ctx=None, preprocessors=()):
attrs = inevow.IRenderer(ctx).__dict__
tmpl = MarkupTemplate(self.template)
xmlStr = tmpl.generate(**attrs).render('xhtml')
self.template = xmlStr
return super(genshistr, self).load(ctx=ctx,
preprocessors=preprocessors)
This is an example using a string as a template. The source code link below includes a folder where the templates are broken out into files and with a myloaders.genshifile class defined. The screenshots above were taken from a running instance of that example.
Browse the example source code here: nevow/templating/genshi.