Edgewall Software

Changes between Initial Version and Version 1 of GenshiRecipes/web2py


Ignore:
Timestamp:
Dec 12, 2008, 3:45:56 PM (15 years ago)
Author:
tfarrell@…
Comment:

Here's the page describing use in web2py.

Legend:

Unmodified
Added
Removed
Modified
  • GenshiRecipes/web2py

    v1 v1  
     1web2py allows for almost seemless integration with Genshi.
     2
     3The first thing to do, after you've installed [http://www.web2py.com 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).
     4
     5{{{
     6#!/usr/bin/env python
     7
     8'''
     9The Genshi4web2py module provides Genshi markup templating functionality to the
     10web2py framework.  It does not provide Genshi's text-based templating since
     11web2py's built-in template is so similar, it would be redundant.
     12
     13To enable Genshi templating on a controller basis, put the following code in
     14your controller::
     15
     16    from Genshi4web2py import render
     17    response.postprocessing.append(lambda x: render(x, request, response))
     18
     19If you would prefer to enable Genshi for a complete Application, the above code
     20can be placed in a model file.  This module can also make use of web2py's
     21cache system for a large speed boost.  To do so pass cache to render like so::
     22
     23    response.postprocessing.append(lambda x: render(x, request, response, cache=cache))
     24
     25Note that using the cache for templates will cause frustration in a development
     26environment since changes to a view will only be reflected when the cache
     27refreshes (the default is every 10 minutes).
     28
     29@var regex_include: a compiled regular expression used to identify Genshi
     30    templates.
     31@var Genshi: indicates if Genshi support is enabled
     32@var READLIMIT: the max number of bytes of a template to read when identifying
     33    a template as a Genshi template
     34'''
     35import re
     36import os
     37import types
     38
     39try:
     40    from genshi.template import TemplateLoader, Context, TemplateNotFound, TemplateSyntaxError
     41    from genshi.filters.html import HTMLFormFiller
     42    from genshi import HTML
     43    Genshi = True
     44    READLIMIT=512
     45    regex_include=re.compile('(?P<all>(?:\{\{\s*include\s+[\'"]|' +
     46                             '<xi:include\s+href=[\'"])(?P<name>[^\'"]*)' +
     47                             '[\'"]\s*(?:\}\}|(?:/>|>\s*</xi:include>)))')
     48except ImportError, e: Genshi = False
     49
     50def render(vars, request, response, **kwargs):
     51    '''Renders the controller output with a Genshi Template
     52
     53    B{Render Options} - "renderOptions" can be specified as a dictionary in
     54    L{vars}.  It is used to specify certain options the rendering engine
     55    should use.  Specifying any of these is optional.  Below are the specific
     56    valid rendering options::
     57
     58        - B{viewdirs}: a list of paths to template folders in which to look
     59            for views
     60        - B{view}: the name of the view to use for the controller calling
     61            L{render}
     62        - B{Content-Type}: the Content-Type header as it should be sent to
     63            the browser
     64        - B{HTMLFormFill}: a dictionary of values used to fill elements of
     65            forms in side the template after template parsing
     66        - B{GenshiDoctype}: the doctype that Genshi will apply to the resulting
     67            rendered output
     68        - B{GenshiRenderEngine}: the engine Genshi will use to render the view
     69
     70    @type vars: dict
     71    @param vars: a dictionary of variables to pass to the template
     72    @param request: the web2py request instance.  Used to determine the current
     73        application folder
     74    @param response: the web2py response instance.  Used to set headers if
     75        needed.
     76    @type kwargs: dict
     77    @param kwargs: any additional keyword arguments will be made available for
     78        the run environment of the view.
     79
     80    @returns: If the template exists and is valid, a normal string will be
     81        returned; otherwise the passed L{vars} will be returned to be parsed by
     82        a downstream template renderer.
     83
     84    @raises TemplateSyntaxError: In the event that a template has a syntax
     85        error, a TemplateSyntaxError will be raised.
     86    '''
     87
     88    if type(vars) == types.StringType:
     89        return vars
     90
     91    cache = kwargs.get('cache', None)
     92    appfolder = os.path.dirname(os.path.normpath(request.folder))
     93    initview = os.path.join(appfolder, 'init', 'views')
     94    viewdirs = [ os.path.join(request.folder, 'views') ]
     95
     96    if os.path.exists(initview) and viewdirs[0] != initview:
     97        viewdirs.append(initview)
     98
     99    opts = {'viewdirs': viewdirs
     100            ,'view':response.view
     101            ,'Content-Type':"text/html; charset=utf-8"
     102            ,'GenshiDoctype':'html'
     103            ,'GenshiRenderEngine':'html'
     104            ,'HTMLFormFill':vars.get('HTMLFormFill')
     105            }
     106
     107    if vars.get('renderOptions'):
     108        opts.update(vars['renderOptions'])
     109
     110    cachekey = ''.join(opts['viewdirs'])
     111    if cache:
     112        loader = cache.ram(cachekey
     113                           , lambda:TemplateLoader(opts['viewdirs']
     114                                                   , auto_reload=False))
     115    else:
     116        loader = TemplateLoader(opts['viewdirs'], auto_reload=False)
     117
     118    try:
     119        if cache:
     120            output = cache.ram(cachekey+opts['view']
     121                               , lambda:loader.load(opts['view']))
     122        else:
     123            output = loader.load(opts['view'])
     124    except TemplateNotFound, e:
     125        return vars
     126    except TemplateSyntaxError, e:
     127        f = open(os.path.normpath(e.filename)).read(READLIMIT)
     128        if not regex_include.search(f):
     129            return vars
     130        else:
     131            raise e
     132
     133    ctxt = Context(request=request, response=response, HTML=HTML)
     134    ctxt.update(vars)
     135    ctxt.update(kwargs)
     136    output = output.generate(ctxt)
     137    if opts.get('HTMLFormFill'):
     138        try:
     139            output = output.filter(HTMLFormFiller(data=opts['HTMLFormFill']))
     140        except:
     141            pass
     142    output = output.render(opts['GenshiRenderEngine']
     143                           , doctype=opts['GenshiDoctype'])
     144
     145    response.headers['Content-Type']=opts['Content-Type']
     146    return output
     147
     148}}}
     149
     150(You may also follow the instructions in the docstring of the module for usage instructions.)
     151
     152Second, 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:
     153
     154{{{
     155    from Genshi4web2py import render
     156    response.postprocessing.append(lambda x: render(x, request, response))
     157}}}
     158
     159If 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.
     160
     161To 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.
     162
     163