From 1209f3421e5057bb2525c7059c1a38ddf205ef2e Mon Sep 17 00:00:00 2001
From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com>
Date: Sat, 25 Jul 2026 15:15:20 -0700
Subject: [PATCH] fix: apply escape flag to a user-supplied HTML renderer
create_markdown() only passed the escape flag to the renderer it built
itself, so passing escape=False together with a custom HTMLRenderer
subclass silently left HTML escaped.
escape now defaults to None, meaning 'keep the renderer's own setting',
and an explicit True/False is applied to a supplied HTMLRenderer
instance as well.
Closes #420
---
src/mistune/__init__.py | 10 +++++++---
tests/test_misc.py | 14 ++++++++++++++
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/mistune/__init__.py b/src/mistune/__init__.py
index 42b5d6f..60ab0f3 100644
--- a/src/mistune/__init__.py
+++ b/src/mistune/__init__.py
@@ -22,14 +22,15 @@
def create_markdown(
- escape: bool = True,
+ escape: Optional[bool] = None,
hard_wrap: bool = False,
renderer: Optional[RendererRef] = "html",
plugins: Optional[Iterable[PluginRef]] = None,
) -> Markdown:
"""Create a Markdown instance based on the given condition.
- :param escape: Boolean. If using html renderer, escape html.
+ :param escape: Boolean. If using html renderer, escape html. When it is
+ ``None``, the renderer keeps its own escape setting.
:param hard_wrap: Boolean. Break every new line into ``
``.
:param renderer: renderer instance, default is HTMLRenderer.
:param plugins: List of plugins.
@@ -47,7 +48,10 @@ def create_markdown(
# explicit and more similar to 2.x's API
renderer = None
elif renderer == "html":
- renderer = HTMLRenderer(escape=escape)
+ renderer = HTMLRenderer(escape=True if escape is None else escape)
+ elif escape is not None and isinstance(renderer, HTMLRenderer):
+ # a renderer instance was given, honor the explicit escape flag
+ renderer._escape = escape
inline = InlineParser(hard_wrap=hard_wrap)
real_plugins: Optional[Iterable[Plugin]] = None
diff --git a/tests/test_misc.py b/tests/test_misc.py
index e3a834f..bbdce68 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -17,6 +17,20 @@ def _add_name(md, state):
md.parse("", state)
self.assertEqual(state.env["name"], "test")
+ def test_escape_applies_to_custom_renderer(self):
+ class CustomRenderer(mistune.HTMLRenderer):
+ pass
+
+ md = mistune.create_markdown(renderer=CustomRenderer(), escape=False)
+ self.assertEqual(md("hi").strip(), "
hi
") + + md = mistune.create_markdown(renderer=CustomRenderer(escape=False), escape=True) + self.assertEqual(md("hi").strip(), "<b>hi</b>
") + + # without an explicit escape flag the renderer keeps its own setting + md = mistune.create_markdown(renderer=CustomRenderer(escape=False)) + self.assertEqual(md("hi").strip(), "hi
") + def test_hard_wrap(self): md = mistune.create_markdown(escape=False, hard_wrap=True) result = md("foo\nbar")