# Original source: https://brandonjay.dev/posts/2021/render-markdown-html-in-python-with-jinja2 from types import SimpleNamespace from app.cache import time_cache_async from mistune import HTMLRenderer, Markdown from pygments import highlight from pygments.formatters import html from pygments.lexers import get_lexer_by_name class MarkdownRenderer(HTMLRenderer): _allow_harmful_protocols = True def __init__(self, app, template): self.template = template self.app = app self.env = self.app.jinja2_env formatter = html.HtmlFormatter() self.env.globals["highlight_styles"] = formatter.get_style_defs() def _escape(self, str): return str ##escape(str) def block_code(self, code, lang=None, info=None): if not lang: lang = info if not lang: return f"
{code}
" # return '\n
%s
\n' % escape(code) lexer = get_lexer_by_name(lang, stripall=True) formatter = html.HtmlFormatter(lineseparator="
") return highlight(code, lexer, formatter) def render(self): markdown_string = self.app.template_path.joinpath(self.template).read_text() renderer = MarkdownRenderer(self.app, self.template) markdown = Markdown(renderer=renderer) return markdown(markdown_string) def render_markdown_sync(app, markdown_string): renderer = MarkdownRenderer(app, None) markdown = Markdown(renderer=renderer) return markdown(markdown_string) @time_cache_async(120) async def render_markdown(app, markdown_string): return render_markdown_sync(app, markdown_string) from jinja2 import TemplateSyntaxError, nodes from jinja2.ext import Extension from jinja2.nodes import Const # Source: https://ron.sh/how-to-write-a-jinja2-extension/ class MarkdownExtension(Extension): tags = {"markdown"} def __init__(self, environment): self.app = SimpleNamespace(jinja2_env=environment) super(MarkdownExtension, self).__init__(environment) def parse(self, parser): line_number = next(parser.stream).lineno md_file = [Const("")] body = "" try: md_file = [parser.parse_expression()] except TemplateSyntaxError: body = parser.parse_statements(["name:endmarkdown"], drop_needle=True) return nodes.CallBlock( self.call_method("_to_html", md_file), [], [], body ).set_lineno(line_number) def _to_html(self, md_file, caller): return render_markdown_sync(self.app, caller())