2

Can one compile or revert a portion of the Jinja2 AST?

For example, is it possible to call a function or method from jinja2.environment or jinja2.compiler.generate or some equivalent on a list of nodes extracted from within a template?

For example, given a template y.html:

avant-tag
{% xyz %}
tag content {{ 3 + 5 }}
{% endxyz %}
apres-tag

and an extension y.py:

# -*- coding: utf-8 -*-
from jinja2 import nodes, Environment, FileSystemLoader
from jinja2.ext import Extension

class YExtension(Extension):
    tags = set(['y'])

    def __init__(self, environment):
        super(YExtension, self).__init__(environment)

    def parse(self, parser):
        tag = parser.stream.next()
        body = parser.parse_statements(['name:endy'], drop_needle=True)
        return nodes.Const("<!-- slurping: %s -->" % str(body))

env = Environment(
    loader      = FileSystemLoader('.'),
    extensions  = [YExtension],
    )

print env.get_template('x.html').render()

Running python y.py results in the expected output of:

avant-tag
 <!-- slurping: [Output(nodes=[TemplateData(data=u'\n    tag-content '),
   Add(left=Const(value=3), right=Const(value=5)),
   TemplateData(data=u'\n ')])] -->
sous-tag

In the parse method, how can one either:

  1. compile body to unicode (i.e. tag-content 8); or, alternatively
  2. revert body to its original source (i.e. tag-content {{ 3 + 5 }}).

As a matter of background, this question relates to two prior questions:

  1. Jinja2 compile extension after includes; and
  2. Insert javascript at top of including file in Jinja 2

Thank you for reading.

Brian

Community
  • 1
  • 1
Brian M. Hunt
  • 81,008
  • 74
  • 230
  • 343

1 Answers1

1

Compiling to unicode is not yet possible in the parse() method since you don't have the context available at that point. You can hack around it ofcourse but it would probably not be the best way to go.

Note that the parse() step is normally only executed once on a html file, after that it will use the parsed bytecode to render the template. The results of the parse step can be rendered given an environment.

You simply don't have the context available there, and getting the context in there... quite difficult ;)

To get the original source however... not much easier without hacking, but the hacking isn't too bad ;)

class YExtension(Extension):
    tags = set(['y'])

    def preprocess(self, source, name, filename=None):
        # insert some code here that replaces '{% xyz %}foo bar{% endxyz %}'
        # with something like: '{% xyz %}foo bar{% raw %}foo bar{% endraw %}{% endxyz %}'
        return source

After that you can read the text as the value from the {% raw %} node. Be sure to trash it after that or it will show in your template.

Wolph
  • 78,177
  • 11
  • 137
  • 148
  • Thanks WoLpH. I think that answers my question. The only residual question, I think, is whether there's a way to render the extracted results given an environment. – Brian M. Hunt Nov 30 '10 at 01:25
  • @Brian M. Hunt: that is no problem since you can render any string either way. You can use `Environment.from_string()` or `Template(nodes)` with anything you'd normally return. – Wolph Nov 30 '10 at 01:48
  • Oh right! Sorry, I was unclear... The only residual question, I think, is whether there's a way to render the extracted **nodes** (not text) given an environment. – Brian M. Hunt Nov 30 '10 at 01:53
  • @Brian M. Hunt: Wouldn't `Template(nodes).render()` do that for you? If not than I might be misunderstanding you. – Wolph Nov 30 '10 at 01:58
  • @WoLpH: I had thought as much earlier, but it gives me a `RuntimeError: maximum recursion depth exceeded` error. – Brian M. Hunt Nov 30 '10 at 03:15
  • @Brian M. Hunt: you're right, it's not working. And after about an hour of trying/testing I can't seem to find a way to get it working directly. – Wolph Nov 30 '10 at 10:16
  • @WoLpH: Thanks for trying. I believe there's some build-up/teardown code in nodes.Template that ought to be present (hence why only a nodes.Template can be compiled), but then again that may not be the issue at all. :) I'm not sure where the issue lies, but perhaps something a solution will come up. – Brian M. Hunt Nov 30 '10 at 15:24
  • @Brian M. Hunt: you can atleast do `template = nodes.Template(nodes); templates.set_environment(env); templates.set_ctx(context)`, but that doesn't render it yet. After that it should be possible to call `env.from_string` or something similar. But I got stuck there ;) – Wolph Nov 30 '10 at 15:34
  • @WoLpH: In my last edit to the http://stackoverflow.com/questions/4292630/insert-javascript-at-top-of-including-file-in-jinja-2 question you can see how `nodes.CallBlock` can be used to get the compiled output. – Brian M. Hunt Dec 01 '10 at 21:03