web2py allows for almost seemless integration with Genshi.
The first thing to do, after you've installed web2py and Genshi, is to save the code below into a module named Genshi4web2py.py and place it somewhere in your PYTHONPATH (site-packages or in the root of web2py).
#!/usr/bin/env python ''' The Genshi4web2py module provides Genshi markup templating functionality to the web2py framework. It does not provide Genshi's text-based templating since web2py's built-in template is so similar, it would be redundant. To enable Genshi templating on a controller basis, put the following code in your controller:: from Genshi4web2py import render response.postprocessing.append(lambda x: render(x, request, response)) If you would prefer to enable Genshi for a complete Application, the above code can be placed in a model file. This module can also make use of web2py's cache system for a large speed boost. To do so pass cache to render like so:: response.postprocessing.append(lambda x: render(x, request, response, cache=cache)) Note that using the cache for templates will cause frustration in a development environment since changes to a view will only be reflected when the cache refreshes (the default is every 10 minutes). @var regex_include: a compiled regular expression used to identify Genshi templates. @var Genshi: indicates if Genshi support is enabled @var READLIMIT: the max number of bytes of a template to read when identifying a template as a Genshi template ''' import re import os import types try: from genshi import HTML from genshi.filters.html import HTMLFormFiller from genshi.template import Context, TemplateSyntaxError from genshi.template.loader import TemplateLoader, TemplateNotFound Genshi = True READLIMIT=512 regex_include=re.compile('(?P<all>(?:\{\{\s*include\s+[\'"]|' + '<xi:include\s+href=[\'"])(?P<name>[^\'"]*)' + '[\'"]\s*(?:\}\}|(?:/>|>\s*</xi:include>)))') except ImportError, e: Genshi = False def render(vars, request, response, **kwargs): '''Renders the controller output with a Genshi Template B{Render Options} - "renderOptions" can be specified as a dictionary in L{vars}. It is used to specify certain options the rendering engine should use. Specifying any of these is optional. Below are the specific valid rendering options:: - B{viewdirs}: a list of paths to template folders in which to look for views - B{view}: the name of the view to use for the controller calling L{render} - B{Content-Type}: the Content-Type header as it should be sent to the browser - B{HTMLFormFill}: a dictionary of values used to fill elements of forms in side the template after template parsing - B{GenshiDoctype}: the doctype that Genshi will apply to the resulting rendered output - B{GenshiRenderEngine}: the engine Genshi will use to render the view @type vars: dict @param vars: a dictionary of variables to pass to the template @param request: the web2py request instance. Used to determine the current application folder @param response: the web2py response instance. Used to set headers if needed. @type kwargs: dict @param kwargs: any additional keyword arguments will be made available for the run environment of the view. @returns: If the template exists and is valid, a normal string will be returned; otherwise the passed L{vars} will be returned to be parsed by a downstream template renderer. @raises TemplateSyntaxError: In the event that a template has a syntax error, a TemplateSyntaxError will be raised. ''' if type(vars) == types.StringType: return vars cache = kwargs.get('cache', None) appfolder = os.path.dirname(os.path.normpath(request.folder)) initview = os.path.join(appfolder, 'init', 'views') viewdirs = [ os.path.join(request.folder, 'views') ] if os.path.exists(initview) and viewdirs[0] != initview: viewdirs.append(initview) opts = {'viewdirs': viewdirs ,'view':response.view ,'Content-Type':"text/html; charset=utf-8" ,'GenshiDoctype':'html' ,'GenshiRenderEngine':'html' ,'HTMLFormFill':vars.get('HTMLFormFill') } if vars.get('renderOptions'): opts.update(vars['renderOptions']) cachekey = ''.join(opts['viewdirs']) if cache: loader = cache.ram(cachekey , lambda:TemplateLoader(opts['viewdirs'] , auto_reload=False)) else: loader = TemplateLoader(opts['viewdirs'], auto_reload=False) try: if cache: output = cache.ram(cachekey+opts['view'] , lambda:loader.load(opts['view'])) else: output = loader.load(opts['view']) except TemplateNotFound, e: return vars except TemplateSyntaxError, e: f = open(os.path.normpath(e.filename)).read(READLIMIT) if not regex_include.search(f): return vars else: raise e ctxt = Context(request=request, response=response, HTML=HTML) ctxt.update(vars) ctxt.update(kwargs) output = output.generate(ctxt) if opts.get('HTMLFormFill'): try: output = output.filter(HTMLFormFiller(data=opts['HTMLFormFill'])) except: pass output = output.render(opts['GenshiRenderEngine'] , doctype=opts['GenshiDoctype']) response.headers['Content-Type']=opts['Content-Type'] return output
(You may also follow the instructions in the docstring of the module for usage instructions.)
Second, create an application in web2py. There are two ways to hook Genshi into web2py, application-wide or controller-wide. In either case, the inclusion code looks like:
from Genshi4web2py import render response.postprocessing.append(lambda x: render(x, request, response))
If you wish to use Genshi throughout that application, then you can place this code in a model file so that it is executed for every controller in the application. If, however, you wish to only use it with one controller, include this code somewhere in the controller.
To invoke Genshi there is nothing more to do, simply make your controllers as you would if using normal web2py templates and make your views as Genshi templates. Genshi4web2py will look for the view corresponding to the running controller and detect if it is a Genshi template. If so, it will render it, otherwise it will pass it on to the internal web2py template renderer.