Automatically modify variables rendered in jinja2 template

25 Views Asked by At

I'm rendering a markdown content with a jinja2 template. I need to ensure that no characters rendered from variables are interpreted as markdown special characters.

Example

template = Environment().from_string("{{ a }}")
print(markdown_to_html(template.render({"a": "*b*"})))

should print

<p>*b*</p>

I have tried to use an extension that automatically applies a filter to all print statements in a template.

from jinja2 import Environment
from jinja2.ext import Extension
from jinja2.lexer import Token


MARKDOWN_SPECIAL_CHARACTERS = ["*"]  # TODO: add other characters


def escape_markdown(text: str) -> str:
    for char in MARKDOWN_SPECIAL_CHARACTERS:
        text = text.replace(char, f"\\{char}")
    return text


class EscapePrintExtension(Extension):

    def filter_stream(self, stream):
        for token in stream:
            if token.type == "variable_end":
                yield Token(lineno=token.lineno, type="pipe", value="|")
                yield Token(lineno=token.lineno, type="name", value="escape_markdown")

            yield token


env = Environment(extensions=[EscapePrintExtension])
env.filters["escape_markdown"] = escape_markdown
template = env.from_string("{{ a }}")
print(template.render({"a": "*b*"}))  # prints "\*b\*"

However it seems to be quite complex to implement the escape_markdown properly. Simple escaping of special markdown characters does not cover cases like lists. I would probably need to use a markdown parser to figure out what needs to be escaped.

I could implement escape_markdown to wrap the value to backtics and render it as code.

def escape_markdown(text: str) -> str:
    return f"`{text}`"

This seems should be working solution, however feels a quite hacky. I would need modify markdown renderer to render code as plain text, which would mean I won't be able to render code.

Is there some better way to achive this?

Note:

  • I can assume that all the context values will be single line strings, so I do not need to bother with handling paragraphs
0

There are 0 best solutions below