From ed9d49539bd4d680bea894a49ba428a836f91b1c Mon Sep 17 00:00:00 2001
From: cherith <315844584+Cherith1222@users.noreply.github.com>
Date: Wed, 7 Oct 2026 06:47:24 +0800
Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Keep=20escapes=20and?=
=?UTF-8?q?=20entities=20in=20image=20alt=20text?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Image alt text dropped text_special tokens, so a backslash escape or an HTML entity disappeared from the alt attribute.
AI-assisted (Cursor). Not yet reviewed by a human.
---
myst_parser/mdit_to_docutils/base.py | 7 ++++---
tests/test_image_alt.py | 30 ++++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 3 deletions(-)
create mode 100644 tests/test_image_alt.py
diff --git a/myst_parser/mdit_to_docutils/base.py b/myst_parser/mdit_to_docutils/base.py
index 68cfae72..0d853b6f 100644
--- a/myst_parser/mdit_to_docutils/base.py
+++ b/myst_parser/mdit_to_docutils/base.py
@@ -482,10 +482,11 @@ def renderInlineAsText(self, tokens: list[SyntaxTreeNode]) -> str: # noqa: N802
result = ""
for token in tokens or []:
- if token.type == "text":
+ if token.type in {"text", "text_special"}:
+ # An escape or entity is a text_special token with no children.
+ # Its content is the character the alt text must keep
+ # (executablebooks/MyST-Parser#1210).
result += token.content
- # elif token.type == "image":
- # result += self.renderInlineAsText(token.children)
else:
result += self.renderInlineAsText(token.children or [])
return result
diff --git a/tests/test_image_alt.py b/tests/test_image_alt.py
new file mode 100644
index 00000000..3f13405b
--- /dev/null
+++ b/tests/test_image_alt.py
@@ -0,0 +1,30 @@
+"""Image alt text keeps CommonMark escapes and entities."""
+
+from docutils.core import publish_parts
+
+from myst_parser.parsers.docutils_ import Parser
+
+
+def _html(source: str) -> str:
+ return publish_parts(source, parser=Parser(), writer_name="html5")["body"].strip()
+
+
+def test_image_alt_keeps_escapes_and_entities():
+ """Escapes and entities inside an image alt are not dropped.
+
+ markdown-it-py represents them as ``text_special`` tokens with no children.
+ Reading only ``text`` tokens used to drop the character
+ (executablebooks/MyST-Parser#1210). The same source in a link already
+ rendered the character.
+ """
+ assert _html(r"") == '

'
+ assert _html("") == '
'
+ assert (
+ _html(r"")
+ == '
'
+ )
+ assert (
+ _html(r"[a \* b & c](https://example.com)")
+ == ''
+ "a * b & c
"
+ )
From bcf5d06da4da277506edd40e9972c3ca534cb4fe Mon Sep 17 00:00:00 2001
From: Chris Sewell
Date: Mon, 9 Nov 2026 20:32:56 +0100
Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A7=AA=20TEST:=20move=20the=20image?=
=?UTF-8?q?=20alt=20cases=20into=20the=20syntax=20fixtures?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Image rendering is pinned in `docutil_syntax_elements.md`, so the alt
cases live there as doctree fixtures rather than as html5 writer strings.
Adds an escape and entity inside emphasis, an escape in a nested image,
and a reference-style image. All four fail without the fix.
---
tests/test_image_alt.py | 30 ---------------
.../fixtures/docutil_syntax_elements.md | 38 +++++++++++++++++++
2 files changed, 38 insertions(+), 30 deletions(-)
delete mode 100644 tests/test_image_alt.py
diff --git a/tests/test_image_alt.py b/tests/test_image_alt.py
deleted file mode 100644
index 3f13405b..00000000
--- a/tests/test_image_alt.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""Image alt text keeps CommonMark escapes and entities."""
-
-from docutils.core import publish_parts
-
-from myst_parser.parsers.docutils_ import Parser
-
-
-def _html(source: str) -> str:
- return publish_parts(source, parser=Parser(), writer_name="html5")["body"].strip()
-
-
-def test_image_alt_keeps_escapes_and_entities():
- """Escapes and entities inside an image alt are not dropped.
-
- markdown-it-py represents them as ``text_special`` tokens with no children.
- Reading only ``text`` tokens used to drop the character
- (executablebooks/MyST-Parser#1210). The same source in a link already
- rendered the character.
- """
- assert _html(r"") == '
'
- assert _html("") == '
'
- assert (
- _html(r"")
- == '
'
- )
- assert (
- _html(r"[a \* b & c](https://example.com)")
- == ''
- "a * b & c
"
- )
diff --git a/tests/test_renderers/fixtures/docutil_syntax_elements.md b/tests/test_renderers/fixtures/docutil_syntax_elements.md
index c2307953..42c909a1 100644
--- a/tests/test_renderers/fixtures/docutil_syntax_elements.md
+++ b/tests/test_renderers/fixtures/docutil_syntax_elements.md
@@ -190,6 +190,44 @@ Image with escapable html:
.
+Image alt with escapes and entities:
+.
+
+.
+
+
+
+.
+
+Image alt with escapes and entities in emphasis:
+.
+
+.
+
+
+
+.
+
+Image alt with an escape in a nested image:
+.
+](x.png)
+.
+
+
+
+.
+
+Image alt with an escape, reference style:
+.
+![a \* b][r]
+
+[r]: x.png
+.
+
+
+
+.
+
Block Quote:
.
> *foo*