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