From 838709ede0d28dc30cac899a9665644aee0706cd Mon Sep 17 00:00:00 2001 From: facelessuser Date: Wed, 14 Oct 2026 17:02:42 -0600 Subject: [PATCH 1/2] Revert enforcement of CommonMark punctuation rules Some foreign languages are hindered by CommonMark style punctuation rules. For backwards compatibility, revert enforcement of CommonMark punctuation rules and leave that up to 3rd party libraries if desired. --- docs/changelog.md | 5 + markdown/inlinepatterns.py | 111 ++-------------------- tests/test_syntax/inline/test_emphasis.py | 40 +++----- 3 files changed, 26 insertions(+), 130 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4f5c00729..468cd3f6d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,11 @@ and this project adheres to the [Python Version Specification](https://packaging.python.org/en/latest/specifications/version-specifiers/). See the [Contributing Guide](contributing.md) for details. +## [Unreleased] + +* For backwards compatibility in some languages, revert the enforcement of CommonMark + style punctuation rules (#1657). + ## [3.11.1] - 2026-10-13 ### Fixed diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py index e42b297ac..785585d03 100644 --- a/markdown/inlinepatterns.py +++ b/markdown/inlinepatterns.py @@ -563,83 +563,6 @@ def _unescape(m: re.Match[str]) -> str: return RE.sub(_unescape, text) -PUNCT = ( - b'!-/:-@\\[-`{-\\~\xc2\xa1-\xc2\xa9\xc2\xab-\xc2\xac\xc2\xae-\xc2\xb1\xc2\xb4\xc2\xb6-\xc2\xb8\xc2\xbb\xc2\xbf\xc3' - b'\x97\xc3\xb7\xcb\x82-\xcb\x85\xcb\x92-\xcb\x9f\xcb\xa5-\xcb\xab\xcb\xad\xcb\xaf-\xcb\xbf\xcd\xb5\xcd\xbe\xce\x84' - b'-\xce\x85\xce\x87\xcf\xb6\xd2\x82\xd5\x9a-\xd5\x9f\xd6\x89-\xd6\x8a\xd6\x8d-\xd6\x8f\xd6\xbe\xd7\x80\xd7\x83\xd7' - b'\x86\xd7\xb3-\xd7\xb4\xd8\x86-\xd8\x8f\xd8\x9b\xd8\x9d-\xd8\x9f\xd9\xaa-\xd9\xad\xdb\x94\xdb\x9e\xdb\xa9\xdb\xbd' - b'-\xdb\xbe\xdc\x80-\xdc\x8d\xdf\xb6-\xdf\xb9\xdf\xbe-\xdf\xbf\xe0\xa0\xb0-\xe0\xa0\xbe\xe0\xa1\x9e\xe0\xa2\x88' - b'\xe0\xa5\xa4-\xe0\xa5\xa5\xe0\xa5\xb0\xe0\xa7\xb2-\xe0\xa7\xb3\xe0\xa7\xba-\xe0\xa7\xbb\xe0\xa7\xbd\xe0\xa9\xb6' - b'\xe0\xab\xb0-\xe0\xab\xb1\xe0\xad\xb0\xe0\xaf\xb3-\xe0\xaf\xba\xe0\xb1\xb7\xe0\xb1\xbf\xe0\xb2\x84\xe0\xb5\x8f' - b'\xe0\xb5\xb9\xe0\xb7\xb4\xe0\xb8\xbf\xe0\xb9\x8f\xe0\xb9\x9a-\xe0\xb9\x9b\xe0\xbc\x81-\xe0\xbc\x97\xe0\xbc\x9a-' - b'\xe0\xbc\x9f\xe0\xbc\xb4\xe0\xbc\xb6\xe0\xbc\xb8\xe0\xbc\xba-\xe0\xbc\xbd\xe0\xbe\x85\xe0\xbe\xbe-\xe0\xbf\x85' - b'\xe0\xbf\x87-\xe0\xbf\x8c\xe0\xbf\x8e-\xe0\xbf\x9a\xe1\x81\x8a-\xe1\x81\x8f\xe1\x82\x9e-\xe1\x82\x9f\xe1\x83\xbb' - b'\xe1\x8d\xa0-\xe1\x8d\xa8\xe1\x8e\x90-\xe1\x8e\x99\xe1\x90\x80\xe1\x99\xad-\xe1\x99\xae\xe1\x9a\x9b-\xe1\x9a\x9c' - b'\xe1\x9b\xab-\xe1\x9b\xad\xe1\x9c\xb5-\xe1\x9c\xb6\xe1\x9f\x94-\xe1\x9f\x96\xe1\x9f\x98-\xe1\x9f\x9b\xe1\xa0\x80' - b'-\xe1\xa0\x8a\xe1\xa5\x80\xe1\xa5\x84-\xe1\xa5\x85\xe1\xa7\x9e-\xe1\xa7\xbf\xe1\xa8\x9e-\xe1\xa8\x9f\xe1\xaa\xa0' - b'-\xe1\xaa\xa6\xe1\xaa\xa8-\xe1\xaa\xad\xe1\xad\x8e-\xe1\xad\x8f\xe1\xad\x9a-\xe1\xad\xaa\xe1\xad\xb4-\xe1\xad' - b'\xbf\xe1\xaf\xbc-\xe1\xaf\xbf\xe1\xb0\xbb-\xe1\xb0\xbf\xe1\xb1\xbe-\xe1\xb1\xbf\xe1\xb3\x80-\xe1\xb3\x87\xe1\xb3' - b'\x93\xe1\xbe\xbd\xe1\xbe\xbf-\xe1\xbf\x81\xe1\xbf\x8d-\xe1\xbf\x8f\xe1\xbf\x9d-\xe1\xbf\x9f\xe1\xbf\xad-\xe1\xbf' - b'\xaf\xe1\xbf\xbd-\xe1\xbf\xbe\xe2\x80\x90-\xe2\x80\xa7\xe2\x80\xb0-\xe2\x81\x9e\xe2\x81\xba-\xe2\x81\xbe\xe2\x82' - b'\x8a-\xe2\x82\x8e\xe2\x82\xa0-\xe2\x83\x80\xe2\x84\x80-\xe2\x84\x81\xe2\x84\x83-\xe2\x84\x86\xe2\x84\x88-\xe2' - b'\x84\x89\xe2\x84\x94\xe2\x84\x96-\xe2\x84\x98\xe2\x84\x9e-\xe2\x84\xa3\xe2\x84\xa5\xe2\x84\xa7\xe2\x84\xa9\xe2' - b'\x84\xae\xe2\x84\xba-\xe2\x84\xbb\xe2\x85\x80-\xe2\x85\x84\xe2\x85\x8a-\xe2\x85\x8d\xe2\x85\x8f\xe2\x86\x8a-\xe2' - b'\x86\x8b\xe2\x86\x90-\xe2\x90\xa9\xe2\x91\x80-\xe2\x91\x8a\xe2\x92\x9c-\xe2\x93\xa9\xe2\x94\x80-\xe2\x9d\xb5\xe2' - b'\x9e\x94-\xe2\xad\xb3\xe2\xad\xb6-\xe2\xae\x95\xe2\xae\x97-\xe2\xaf\xbf\xe2\xb3\xa5-\xe2\xb3\xaa\xe2\xb3\xb9-' - b'\xe2\xb3\xbc\xe2\xb3\xbe-\xe2\xb3\xbf\xe2\xb5\xb0\xe2\xb8\x80-\xe2\xb8\xae\xe2\xb8\xb0-\xe2\xb9\x9d\xe2\xba\x80-' - b'\xe2\xba\x99\xe2\xba\x9b-\xe2\xbb\xb3\xe2\xbc\x80-\xe2\xbf\x95\xe2\xbf\xb0-\xe2\xbf\xbf\xe3\x80\x81-\xe3\x80\x84' - b'\xe3\x80\x88-\xe3\x80\xa0\xe3\x80\xb0\xe3\x80\xb6-\xe3\x80\xb7\xe3\x80\xbd-\xe3\x80\xbf\xe3\x82\x9b-\xe3\x82\x9c' - b'\xe3\x82\xa0\xe3\x83\xbb\xe3\x86\x90-\xe3\x86\x91\xe3\x86\x96-\xe3\x86\x9f\xe3\x87\x80-\xe3\x87\xa5\xe3\x87\xaf' - b'\xe3\x88\x80-\xe3\x88\x9e\xe3\x88\xaa-\xe3\x89\x87\xe3\x89\x90\xe3\x89\xa0-\xe3\x89\xbf\xe3\x8a\x8a-\xe3\x8a\xb0' - b'\xe3\x8b\x80-\xe3\x8f\xbf\xe4\xb7\x80-\xe4\xb7\xbf\xea\x92\x90-\xea\x93\x86\xea\x93\xbe-\xea\x93\xbf\xea\x98\x8d' - b'-\xea\x98\x8f\xea\x99\xb3\xea\x99\xbe\xea\x9b\xb2-\xea\x9b\xb7\xea\x9c\x80-\xea\x9c\x96\xea\x9c\xa0-\xea\x9c\xa1' - b'\xea\x9e\x89-\xea\x9e\x8a\xea\xa0\xa8-\xea\xa0\xab\xea\xa0\xb6-\xea\xa0\xb9\xea\xa1\xb4-\xea\xa1\xb7\xea\xa3\x8e' - b'-\xea\xa3\x8f\xea\xa3\xb8-\xea\xa3\xba\xea\xa3\xbc\xea\xa4\xae-\xea\xa4\xaf\xea\xa5\x9f\xea\xa7\x81-\xea\xa7\x8d' - b'\xea\xa7\x9e-\xea\xa7\x9f\xea\xa9\x9c-\xea\xa9\x9f\xea\xa9\xb7-\xea\xa9\xb9\xea\xab\x9e-\xea\xab\x9f\xea\xab\xb0' - b'-\xea\xab\xb1\xea\xad\x9b\xea\xad\xaa-\xea\xad\xab\xea\xaf\xab\xef\xac\xa9\xef\xae\xb2-\xef\xaf\x82\xef\xb4\xbe-' - b'\xef\xb5\x8f\xef\xb7\x8f\xef\xb7\xbc-\xef\xb7\xbf\xef\xb8\x90-\xef\xb8\x99\xef\xb8\xb0-\xef\xb9\x92\xef\xb9\x94-' - b'\xef\xb9\xa6\xef\xb9\xa8-\xef\xb9\xab\xef\xbc\x81-\xef\xbc\x8f\xef\xbc\x9a-\xef\xbc\xa0\xef\xbc\xbb-\xef\xbd\x80' - b'\xef\xbd\x9b-\xef\xbd\xa5\xef\xbf\xa0-\xef\xbf\xa6\xef\xbf\xa8-\xef\xbf\xae\xef\xbf\xbc-\xef\xbf\xbd\xf0\x90\x84' - b'\x80-\xf0\x90\x84\x82\xf0\x90\x84\xb7-\xf0\x90\x84\xbf\xf0\x90\x85\xb9-\xf0\x90\x86\x89\xf0\x90\x86\x8c-\xf0\x90' - b'\x86\x8e\xf0\x90\x86\x90-\xf0\x90\x86\x9c\xf0\x90\x86\xa0\xf0\x90\x87\x90-\xf0\x90\x87\xbc\xf0\x90\x8e\x9f\xf0' - b'\x90\x8f\x90\xf0\x90\x95\xaf\xf0\x90\xa1\x97\xf0\x90\xa1\xb7-\xf0\x90\xa1\xb8\xf0\x90\xa4\x9f\xf0\x90\xa4\xbf' - b'\xf0\x90\xa9\x90-\xf0\x90\xa9\x98\xf0\x90\xa9\xbf\xf0\x90\xab\x88\xf0\x90\xab\xb0-\xf0\x90\xab\xb6\xf0\x90\xac' - b'\xb9-\xf0\x90\xac\xbf\xf0\x90\xae\x99-\xf0\x90\xae\x9c\xf0\x90\xb5\xae\xf0\x90\xb6\x8e-\xf0\x90\xb6\x8f\xf0\x90' - b'\xba\xad\xf0\x90\xbd\x95-\xf0\x90\xbd\x99\xf0\x90\xbe\x86-\xf0\x90\xbe\x89\xf0\x91\x81\x87-\xf0\x91\x81\x8d\xf0' - b'\x91\x82\xbb-\xf0\x91\x82\xbc\xf0\x91\x82\xbe-\xf0\x91\x83\x81\xf0\x91\x85\x80-\xf0\x91\x85\x83\xf0\x91\x85\xb4-' - b'\xf0\x91\x85\xb5\xf0\x91\x87\x85-\xf0\x91\x87\x88\xf0\x91\x87\x8d\xf0\x91\x87\x9b\xf0\x91\x87\x9d-\xf0\x91\x87' - b'\x9f\xf0\x91\x88\xb8-\xf0\x91\x88\xbd\xf0\x91\x8a\xa9\xf0\x91\x8f\x94-\xf0\x91\x8f\x95\xf0\x91\x8f\x97-\xf0\x91' - b'\x8f\x98\xf0\x91\x91\x8b-\xf0\x91\x91\x8f\xf0\x91\x91\x9a-\xf0\x91\x91\x9b\xf0\x91\x91\x9d\xf0\x91\x93\x86\xf0' - b'\x91\x97\x81-\xf0\x91\x97\x97\xf0\x91\x99\x81-\xf0\x91\x99\x83\xf0\x91\x99\xa0-\xf0\x91\x99\xac\xf0\x91\x9a\xb9' - b'\xf0\x91\x9c\xbc-\xf0\x91\x9c\xbf\xf0\x91\xa0\xbb\xf0\x91\xa5\x84-\xf0\x91\xa5\x86\xf0\x91\xa7\xa2\xf0\x91\xa8' - b'\xbf-\xf0\x91\xa9\x86\xf0\x91\xaa\x9a-\xf0\x91\xaa\x9c\xf0\x91\xaa\x9e-\xf0\x91\xaa\xa2\xf0\x91\xac\x80-\xf0\x91' - b'\xac\x89\xf0\x91\xaf\xa1\xf0\x91\xb1\x81-\xf0\x91\xb1\x85\xf0\x91\xb1\xb0-\xf0\x91\xb1\xb1\xf0\x91\xbb\xb7-\xf0' - b'\x91\xbb\xb8\xf0\x91\xbd\x83-\xf0\x91\xbd\x8f\xf0\x91\xbf\x95-\xf0\x91\xbf\xb1\xf0\x91\xbf\xbf\xf0\x92\x91\xb0-' - b'\xf0\x92\x91\xb4\xf0\x92\xbf\xb1-\xf0\x92\xbf\xb2\xf0\x96\xa9\xae-\xf0\x96\xa9\xaf\xf0\x96\xab\xb5\xf0\x96\xac' - b'\xb7-\xf0\x96\xac\xbf\xf0\x96\xad\x84-\xf0\x96\xad\x85\xf0\x96\xb5\xad-\xf0\x96\xb5\xaf\xf0\x96\xba\x97-\xf0\x96' - b'\xba\x9a\xf0\x96\xbf\xa2\xf0\x9b\xb2\x9c\xf0\x9b\xb2\x9f\xf0\x9c\xb0\x80-\xf0\x9c\xb3\xaf\xf0\x9c\xb4\x80-\xf0' - b'\x9c\xba\xb3\xf0\x9c\xbd\x90-\xf0\x9c\xbf\x83\xf0\x9d\x80\x80-\xf0\x9d\x83\xb5\xf0\x9d\x84\x80-\xf0\x9d\x84\xa6' - b'\xf0\x9d\x84\xa9-\xf0\x9d\x85\xa4\xf0\x9d\x85\xaa-\xf0\x9d\x85\xac\xf0\x9d\x86\x83-\xf0\x9d\x86\x84\xf0\x9d\x86' - b'\x8c-\xf0\x9d\x86\xa9\xf0\x9d\x86\xae-\xf0\x9d\x87\xaa\xf0\x9d\x88\x80-\xf0\x9d\x89\x81\xf0\x9d\x89\x85\xf0\x9d' - b'\x8c\x80-\xf0\x9d\x8d\x96\xf0\x9d\x9b\x81\xf0\x9d\x9b\x9b\xf0\x9d\x9b\xbb\xf0\x9d\x9c\x95\xf0\x9d\x9c\xb5\xf0' - b'\x9d\x9d\x8f\xf0\x9d\x9d\xaf\xf0\x9d\x9e\x89\xf0\x9d\x9e\xa9\xf0\x9d\x9f\x83\xf0\x9d\xa0\x80-\xf0\x9d\xa7\xbf' - b'\xf0\x9d\xa8\xb7-\xf0\x9d\xa8\xba\xf0\x9d\xa9\xad-\xf0\x9d\xa9\xb4\xf0\x9d\xa9\xb6-\xf0\x9d\xaa\x83\xf0\x9d\xaa' - b'\x85-\xf0\x9d\xaa\x8b\xf0\x9e\x85\x8f\xf0\x9e\x8b\xbf\xf0\x9e\x97\xbf\xf0\x9e\xa5\x9e-\xf0\x9e\xa5\x9f\xf0\x9e' - b'\xb2\xac\xf0\x9e\xb2\xb0\xf0\x9e\xb4\xae\xf0\x9e\xbb\xb0-\xf0\x9e\xbb\xb1\xf0\x9f\x80\x80-\xf0\x9f\x80\xab\xf0' - b'\x9f\x80\xb0-\xf0\x9f\x82\x93\xf0\x9f\x82\xa0-\xf0\x9f\x82\xae\xf0\x9f\x82\xb1-\xf0\x9f\x82\xbf\xf0\x9f\x83\x81-' - b'\xf0\x9f\x83\x8f\xf0\x9f\x83\x91-\xf0\x9f\x83\xb5\xf0\x9f\x84\x8d-\xf0\x9f\x86\xad\xf0\x9f\x87\xa6-\xf0\x9f\x88' - b'\x82\xf0\x9f\x88\x90-\xf0\x9f\x88\xbb\xf0\x9f\x89\x80-\xf0\x9f\x89\x88\xf0\x9f\x89\x90-\xf0\x9f\x89\x91\xf0\x9f' - b'\x89\xa0-\xf0\x9f\x89\xa5\xf0\x9f\x8c\x80-\xf0\x9f\x9b\x97\xf0\x9f\x9b\x9c-\xf0\x9f\x9b\xac\xf0\x9f\x9b\xb0-\xf0' - b'\x9f\x9b\xbc\xf0\x9f\x9c\x80-\xf0\x9f\x9d\xb6\xf0\x9f\x9d\xbb-\xf0\x9f\x9f\x99\xf0\x9f\x9f\xa0-\xf0\x9f\x9f\xab' - b'\xf0\x9f\x9f\xb0\xf0\x9f\xa0\x80-\xf0\x9f\xa0\x8b\xf0\x9f\xa0\x90-\xf0\x9f\xa1\x87\xf0\x9f\xa1\x90-\xf0\x9f\xa1' - b'\x99\xf0\x9f\xa1\xa0-\xf0\x9f\xa2\x87\xf0\x9f\xa2\x90-\xf0\x9f\xa2\xad\xf0\x9f\xa2\xb0-\xf0\x9f\xa2\xbb\xf0\x9f' - b'\xa3\x80-\xf0\x9f\xa3\x81\xf0\x9f\xa4\x80-\xf0\x9f\xa9\x93\xf0\x9f\xa9\xa0-\xf0\x9f\xa9\xad\xf0\x9f\xa9\xb0-\xf0' - b'\x9f\xa9\xbc\xf0\x9f\xaa\x80-\xf0\x9f\xaa\x89\xf0\x9f\xaa\x8f-\xf0\x9f\xab\x86\xf0\x9f\xab\x8e-\xf0\x9f\xab\x9c' - b'\xf0\x9f\xab\x9f-\xf0\x9f\xab\xa9\xf0\x9f\xab\xb0-\xf0\x9f\xab\xb8\xf0\x9f\xac\x80-\xf0\x9f\xae\x92\xf0\x9f\xae' - b'\x94-\xf0\x9f\xaf\xaf' -).decode('utf-8') - - class Delimiter: """Delimiter.""" @@ -668,26 +591,13 @@ def _build_patterns(self, token: str) -> str: if self.tag_count != 2 and not self.double: self.max_size = 1 - # Python Markdown uses `STX` and `ETX` for placeholders. - # Include handling for these characters in addition to CommonMark rules. - stx, etx = '\x02', '\x03' - # Patterns for "smart" cases. if self.smart: self.boundary = re.compile( fr'''(?x) - (?P - (? - (? - {xstart}{etoken}{{1,}}(?![\s{etoken}{PUNCT}])(?!$)| - (?:(?<=[\s{etx}{PUNCT}])|^)(?(?(?{xstart}{etoken}{{1,}}(?![\s{etoken}])(?!$)) ''', flags=re.UNICODE ) @@ -695,18 +605,9 @@ def _build_patterns(self, token: str) -> str: else: self.boundary = re.compile( fr'''(?x) - (?P - (? - (? - {etoken}{{1,}}(?![\s{etoken}{PUNCT}])(?!$)| - (?:(?<=[\s{etx}{PUNCT}])|^)(?(?(?{etoken}{{1,}}(?![\s{etoken}])(?!$)) ''', flags=re.UNICODE ) diff --git a/tests/test_syntax/inline/test_emphasis.py b/tests/test_syntax/inline/test_emphasis.py index c4dbd2948..3ed6da9d1 100644 --- a/tests/test_syntax/inline/test_emphasis.py +++ b/tests/test_syntax/inline/test_emphasis.py @@ -126,6 +126,9 @@ def test_standalone_understores_at_begin_end(self): '

_ bar _

' ) + +class TestEmphasis(TestCase): + def test_complex_emphasis_asterisk(self): self.assertMarkdownRenders( 'This is text **bold *italic bold*** with more text', @@ -402,12 +405,23 @@ def test_advanced_nesting(self): ) ) + def test_non_english(self): + + self.assertMarkdownRenders( + '**단어(설명)**입니다.', + '

단어(설명)입니다.

' + ) + class TestCommonMark(TestCase): """Test CommonMark.""" def test_commonmark(self): - """Test CommonMark.""" + """ + Test CommonMark. + + Omit some punctuation tests as we do not enforce CommonMark punctuation rules. + """ self.maxDiff = None @@ -418,14 +432,6 @@ def test_commonmark(self): a * foo bar* - a*"foo"* - - *$*alpha. - - *£*bravo. - - *€*charlie. - test * a * @@ -456,10 +462,6 @@ def test_commonmark(self): *foo bar * - *(*foo) - - *(*foo*)* - *foo*bar _foo bar _ @@ -480,8 +482,6 @@ def test_commonmark(self): ** foo bar** - a**"foo"** - foo**bar** __foo bar__ @@ -505,8 +505,6 @@ def test_commonmark(self): **foo bar ** - **(**foo) - *(**foo**)* **Gomphocarpus (*Gomphocarpus physocarpus*, syn. @@ -696,10 +694,6 @@ def test_commonmark(self): """

foo bar

a * foo bar*

-

a*"foo"*

-

*$*alpha.

-

*£*bravo.

-

*€*charlie.

test * a *

foobar

@@ -716,8 +710,6 @@ def test_commonmark(self):

*foo bar *

*foo bar *

-

*(*foo)

-

(foo)

foobar

_foo bar _

_(_foo)

@@ -728,7 +720,6 @@ def test_commonmark(self):

(bar).

foo bar

** foo bar**

-

a**"foo"**

foobar

foo bar

__ foo bar__

@@ -741,7 +732,6 @@ def test_commonmark(self):

foo, bar, baz

foo-(bar)

**foo bar **

-

**(**foo)

(foo)

Gomphocarpus (Gomphocarpus physocarpus, syn. Asclepias physocarpa)

From 053831f52e3e36248ed8d46646ca5cf6d9fd87b9 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 15 Oct 2026 21:34:38 -0400 Subject: [PATCH 2/2] Add fixed category to changelog --- docs/changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index 468cd3f6d..5e6fc0117 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,6 +14,8 @@ See the [Contributing Guide](contributing.md) for details. ## [Unreleased] +### Fixed + * For backwards compatibility in some languages, revert the enforcement of CommonMark style punctuation rules (#1657).