Edgewall Software

Changes between Version 47 and Version 48 of TracDev/PortingFromGenshiToJinja


Ignore:
Timestamp:
Apr 8, 2020, 11:22:17 PM (4 years ago)
Author:
Ryan J Ollos
Comment:

Direct TracLinks to main repository.

Legend:

Unmodified
Added
Removed
Modified
  • TracDev/PortingFromGenshiToJinja

    v47 v48  
    1212In the last part of this document, we try to cover all the Genshi features used by Trac and present their Jinja2 equivalent. Whenever possible, we tried to minimize these differences by customizing the Jinja2 syntax. For example, we use `${...}` for variable expansion, like Genshi does, instead of `{{...}}`. Another aspect of our usage convention is that we favor [#ifthenelse line statements] over `{% ... %}`. So even someone familiar with the "default" Jinja2 syntax should glance through this document to see how "we" use Jinja2, as summarized in the table below.
    1313
    14 Note that Genshi will be supported concurrently with Jinja2 only for a short while, for the 1.3.x development period and for the 1.4-stable period. This support will be removed in Trac [milestone:1.5.1]. If for some reason you're stuck to having to support Genshi templates, you'll have to stick to Trac 1.2.x or 1.3.1. But you really should make the transition effort as Jinja2 templates are 5-10x faster than their Genshi equivalent, for only a 1/5th of their cost in memory usage.
     14Note that Genshi will be supported concurrently with Jinja2 only for a short while, for the 1.3.x development period and for the 1.4-stable period. This support is removed in Trac [milestone:1.5.1]. If for some reason you're stuck to having to support Genshi templates, you'll have to stick to Trac 1.2.x or 1.3.1. But you really should make the transition effort as Jinja2 templates are 5-10x faster than their Genshi equivalent, for only a 1/5th of their cost in memory usage.
    1515
    1616== Examples!
     
    2121Let's first take a simple full-contained example from the Trac source, the simple index.html / jindex.html templates.
    2222
    23  - Genshi [source:sandbox/genshi/templates/index.html@3728 index.html]:
     23 - Genshi [source:tags/trac-1.2/genshi/templates/index.html index.html]:
    2424   {{{#!html+genshi
    2525<!DOCTYPE html
     
    4747</html>
    4848   }}}
    49  - Jinja [source:cboos.git/trac/templates/index.html@jinja2-trunk-r15379 index.html]:
     49 - Jinja [source:/tags/trac-1.4/trac/templates/index.html index.html]:
    5050   {{{#!html+jinja
    5151<!DOCTYPE html>
     
    7676In this small example, there's no common Trac layout used (as the index is a bit special).
    7777For how a "normal" template looks like, see for example
    78 [source:cboos.git/trac/templates/diff_view.html@jinja2-trunk-r15379 diff_view.html], another small template. The generic templates in [source:cboos.git/trac/templates@jinja2-trunk-r15379 trac/templates] are also interesting because we still have their Genshi equivalent close by, in [source:cboos.git/trac/templates/genshi@jinja2-trunk-r15379 trac/templates/genshi], so you can easily compare them if you're stuck during the migration of your own templates.
     78[source:tags/trac-1.4/trac/templates/diff_view.html diff_view.html], another small template. The generic templates in [source:tags/trac-1.4/trac/templates trac/templates] are also interesting because we still have their Genshi equivalent close by, in [source:tags/trac-1.4/trac/templates/genshi trac/templates/genshi], so you can easily compare them if you're stuck during the migration of your own templates.
    7979
    8080Note that a Jinja2 .html template can usually be rendered directly in the browser, to have a rough idea about how it will look like:
     
    101101**Never** go back to the bad old habits from the ClearSilver time, were sometimes the logic in those templates took advantage of the lack of well-formedness constraints, e.g. by conditionally inserting end/start pairs of tags to split sequences. Such templates were hard to maintain, and you always have cleaner alternatives.
    102102
    103 The [./Checker jinjachecker] tool should also help you maintain well-formed templates by stripping off Jinja2 expressions and line statements before attempting to XML validate the document ([pypi:lxml] should be installed for this feature).
     103The [JinjaChecker jinjachecker] tool should also help you maintain well-formed templates by stripping off Jinja2 expressions and line statements before attempting to XML validate the document ([pypi:lxml] should be installed for this feature).
    104104
    105105
     
    110110Instead of the Genshi way of including a template containing filters, the Jinja2 way follows an "object oriented" approach, with inheritance and overriders. Consider that some named sections (or "blocks") of the base template are similar to "methods", imagine that you only have to "subclass" this base template and "reimplement" the overridable methods with your specific content, and there you have it.
    111111
    112 More specifically, you'll have to "extend" the [source:cboos.git/trac/templates/layout.html@jinja2-trunk-r15379 layout.html] template, and redefine the "head", and "content" blocks if needed.
    113 
    114 All the details are available in HtmlTemplates#Jinja2architecture, including a walkthrough for the specific example of the [source:cboos.git/trac/search/templates/search.html@jinja2-trunk-r15379 search.html] template.
    115 
    116 For the search.html example we focus on the //structure// of the templates, the //include// relationship and the decomposition in //blocks//.
    117 
    118 
    119 But we also have a complete conversion [./Example example], which displays the Genshi wiki_view.html template and the Jinja2 wiki_view.html template side-by-side, along with comments explaining the conversion choices.
     112More specifically, you'll have to "extend" the [source:/tags/trac-1.4/trac/templates/layout.html layout.html] template, and redefine the "head", and "content" blocks if needed.
     113
     114All the details are available in HtmlTemplates#Jinja2architecture, including a walkthrough for the specific example of the [source:tags/trac-1.4/trac/search/templates/search.html search.html] template.
     115
     116For the `search.html` example we focus on the //structure// of the templates, the //include// relationship and the decomposition in //blocks//.
     117
     118But we also have a complete conversion [./Example example], which displays the Genshi wiki_view.html template and the Jinja2 `wiki_view.html` template side-by-side, along with comments explaining the conversion choices.
    120119
    121120
     
    172171`render_fragment` can be used instead. It returns a `Markup` string when generating output for the web (`text=False`) or an `unicode` string when generating plain text output (`text=True`).
    173172
    174 //See [source:cboos.git/trac/ticket/query.py@jinja2-trunk-r15379#L1423 Ticket Query] macro (''table'' mode)//
     173//See [source:tags/trac-1.4/trac/ticket/query.py#L1455 Ticket Query] macro (''table'' mode)//
    175174
    176175When the fragment needs to be sent to the client, there's still a better choice than `render_template`, it's `generate_fragment`, as it won't impose as much overhead on the data dictionary as `render_template`. It's best suited for responding to XHRs:
     
    185184
    186185There's even a lower-level public API in `Chrome` for generating content using Jinja2 templates, which provides even greater control.
    187 //See the [source:cboos.git/trac/ticket/notification.py@jinja2-trunk-r15379#L311 rendering of the ticket change notification e-mail]//
     186//See the [source:tags/trac-1.4/trac/ticket/notification.py#L311 rendering of the ticket change notification e-mail]//
    188187
    189188See the API documentation for further details.
     
    194193Genshi provided a nice Python API for programmatically building (X)HTML elements. This consisted of the `Fragment`, `Element` and the `tag` builder, all from the `genshi.builder` module:
    195194{{{#!python
    196 from genshi import Fragment, Element, tag
     195from genshi import Element, Fragment, tag
    197196}}}
    198197This has now been replaced by an equivalent API which lives in `trac.util.html`, so the above import should be replaced with:
    199198{{{#!python
    200 from trac.util.html import Fragment, Element, tag
     199from trac.util.html import Element, Fragment, tag
    201200}}}
    202201Note that the `html` symbol from `trac.util.html` which used to be a alias to `genshi.builder.tag` is now naturally an alias to `trac.util.html.tag`.
     
    251250}}}
    252251
    253 The `jmacros` in the above corresponds to the [source:cboos.git/trac/templates/macros.html@jinja2-trunk-r15371 trac/templates/macros.html] default macros, and this file is included by default (in `layout.html`), so you don't have to bother to include it yourself, as long as your template extends `layout.html`.
     252The `jmacros` in the above corresponds to the [source:tags/trac-1.4/trac/templates/macros.html trac/templates/macros.html] default macros, and this file is included by default (in `layout.html`), so you don't have to bother to include it yourself, as long as your template extends `layout.html`.
    254253
    255254For the accessibility key, it's also quite simple: instead of hard-coding the key as an `accesskey="e"` attribute, simply use the `accesskey('e')` function call, it will know if it has to produce the attribute or not depending on the current user preferences.
     
    279278    - instead of using the Transform filter API to append/prepend the content to some place in the input stream identified by XPath expressions, use jQuery DOM manipulation API
    280279
    281 We'll discuss the specific example of the ticket [source:cboos.git/tracopt/ticket/deleter.py deleter].
     280We'll discuss the specific example of the ticket [source:tags/trac-1.4/tracopt/ticket/deleter.py deleter].
    282281
    283282==== Implement `ITemplateProvider.get_htdocs_dirs` to be able to provide extra JavaScript code
     
    381380  """
    382381      }}}
    383       (`captioned_button(req, symbol, text)` is a small Python utility function, which is trivial to adapt in JavaScript; note however that it's for this part of the logic that we needed to pass `req.session.get('ui.use_symbols')` from Python to JavaScript's `ui.use_symbols` via the call to `add_script_data` in 2.)
     382      `captioned_button(req, symbol, text)` is a small Python utility function, which is trivial to adapt in JavaScript; note however that it's for this part of the logic that we needed to pass `req.session.get('ui.use_symbols')` from Python to JavaScript's `ui.use_symbols` via the call to `add_script_data` in 2.
    384383      \\ \\
    385384      The second Python helper method:
     
    461460The basics remain the same, we use Babel for extracting the source strings from Jinja2 templates and for performing the translations at runtime. One notable differences though is how the //domain// used by the plugin specific catalogs is specified. With Genshi, the templates themselves contained a directive which specified the name of the domain, while Jinja2 doesn't provide this facility. It is therefore up to the controller to do this job, and we use for that the //metadata// `dict` returned by [#IRequestHandler IRequestHandler.process_request].
    462461
    463 The specification for the extraction is slightly more verbose than it use to be with Genshi. As we use a syntax for the Jinja2 templates which is different than the default ([#TheJinja2syntaxusedbyTrac see below]), it has to be specified for the extractor as well.
     462The specification for the extraction is slightly more verbose than with Genshi. As we use a syntax for the Jinja2 templates which is different than the default ([#TheJinja2syntaxusedbyTrac see below]), it has to be specified for the extractor as well.
    464463
    465464In what follows, we'll take the example of the SpamFilter plugin.