From f8b312f85d6a188a8ca24a5543c8834dfea15281 Mon Sep 17 00:00:00 2001 From: Gyanu Mayank Date: Sat, 5 Sep 2026 10:56:45 +0530 Subject: [PATCH] Honor block-level tags registered after md_in_html loads. The extension copied Markdown.block_level_elements at init, so md.block_level_elements.append() after construction was ignored. Read the live list instead. Fixes #1246 --- docs/changelog.md | 2 ++ markdown/extensions/md_in_html.py | 18 +++++++---- .../test_syntax/extensions/test_md_in_html.py | 30 +++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index e949c44fb..913732761 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -24,6 +24,8 @@ See the [Contributing Guide](contributing.md) for details. ### Fixed * Fix an issue with excessive backtracking when matching inline code blocks (#1617). +* `md_in_html` now honors tags added to `Markdown.block_level_elements` after + the extension is loaded (#1246). ## [3.10.3] - 2026-07-30 diff --git a/markdown/extensions/md_in_html.py b/markdown/extensions/md_in_html.py index 32a9c11a2..d02d7a483 100644 --- a/markdown/extensions/md_in_html.py +++ b/markdown/extensions/md_in_html.py @@ -41,8 +41,6 @@ class HTMLExtractorExtra(HTMLExtractor): """ def __init__(self, md: Markdown, *args, **kwargs): - # All block-level tags. - self.block_level_tags = set(md.block_level_elements.copy()) # Block-level tags in which the content only gets span level parsing self.span_tags = set( ['address', 'dd', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'li', 'p', 'summary', 'td', 'th'] @@ -52,9 +50,19 @@ def __init__(self, md: Markdown, *args, **kwargs): super().__init__(md, *args, **kwargs) - # Block-level tags in which the content gets parsed as blocks - self.block_tags = set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags) - self.span_and_blocks_tags = self.block_tags | self.span_tags + @property + def block_level_tags(self) -> set[str]: + # Read from the Markdown instance so tags registered after the + # extension is loaded (md.block_level_elements.append) are honored. + return set(self.md.block_level_elements) + + @property + def block_tags(self) -> set[str]: + return set(self.block_level_tags) - (self.span_tags | self.raw_tags | self.empty_tags) + + @property + def span_and_blocks_tags(self) -> set[str]: + return self.block_tags | self.span_tags def reset(self): """Reset this instance. Loses all unprocessed data.""" diff --git a/tests/test_syntax/extensions/test_md_in_html.py b/tests/test_syntax/extensions/test_md_in_html.py index 903766fca..c89ae6f88 100644 --- a/tests/test_syntax/extensions/test_md_in_html.py +++ b/tests/test_syntax/extensions/test_md_in_html.py @@ -1556,6 +1556,36 @@ def test_noname_tag(self): ) ) + def test_registered_custom_element_is_block_level(self): + # Tags added to Markdown.block_level_elements after construction + # must still be treated as block-level HTML (#1246). + md = Markdown(extensions=['md_in_html']) + md.block_level_elements.append('a-b') + src = self.dedent( + """ + + + asdf + + + """ + ) + self.assertEqual(md.convert(src), '\n\nasdf\n\n') + + def test_registered_custom_element_markdown_attr(self): + md = Markdown(extensions=['md_in_html']) + md.block_level_elements.append('a-b') + src = self.dedent( + """ + + + *asdf* + + + """ + ) + self.assertEqual(md.convert(src), '\n

asdf

\n
') + def load_tests(loader, tests, pattern): """ Ensure `TestHTMLBlocks` doesn't get run twice by excluding it here. """