From 3c336967bb259dca098fa982b76becd77f248601 Mon Sep 17 00:00:00 2001 From: Afonso Ribeiro Date: Fri, 16 Oct 2026 05:56:00 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=91=8C=20Make=20`StateBase.src`=20a?= =?UTF-8?q?=20plain=20attribute=20(~6%=20faster=20parsing)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + markdown_it/ruler.py | 24 ++++++++++++------------ tests/test_api/test_main.py | 12 ++++++++++++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 040ff138..c979df0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* šŸ‘Œ Speed up parsing (~6% on the CommonMark spec) by making `StateBase.src` a plain attribute instead of a property * ✨ Add `--enable-tables` to the CLI for file, standard input and interactive parsing in [#422](https://github.com/executablebooks/markdown-it-py/pull/422) * šŸ› Fix CLI interactive mode joining input lines with an extra newline, which split every line into its own paragraph and broke hard line breaks, in [#172](https://github.com/executablebooks/markdown-it-py/issues/172) * šŸ› Fix trimming and splitting with the Python whitespace set instead of the CommonMark one, which dropped U+001C–U+001F and U+0085 from paragraphs, headings, table cells and fence info strings and let distinct reference labels resolve each other, in [#418](https://github.com/executablebooks/markdown-it-py/pull/418), thanks to [@Nexory](https://github.com/Nexory) diff --git a/markdown_it/ruler.py b/markdown_it/ruler.py index 91ab5804..34a4af92 100644 --- a/markdown_it/ruler.py +++ b/markdown_it/ruler.py @@ -30,20 +30,19 @@ class Ruler class StateBase: + # `src` is a plain attribute, not a property: rules read it on almost + # every step, and a property call there costs ~5% of total parse time. + src: str + + # Cache for the deprecated `srcCharCode`, as `(src, char_codes)`. + # This is a class-level default, since subclasses do not call `__init__`. + _src_char_code: tuple[str, tuple[int, ...]] | None = None + def __init__(self, src: str, md: MarkdownIt, env: EnvType): self.src = src self.env = env self.md = md - @property - def src(self) -> str: - return self._src - - @src.setter - def src(self, value: str) -> None: - self._src = value - self._srcCharCode: tuple[int, ...] | None = None - @property def srcCharCode(self) -> tuple[int, ...]: warnings.warn( @@ -51,9 +50,10 @@ def srcCharCode(self) -> tuple[int, ...]: DeprecationWarning, stacklevel=2, ) - if self._srcCharCode is None: - self._srcCharCode = tuple(ord(c) for c in self._src) - return self._srcCharCode + # rebuild the cache if `src` has been reassigned since it was built + if self._src_char_code is None or self._src_char_code[0] is not self.src: + self._src_char_code = (self.src, tuple(ord(c) for c in self.src)) + return self._src_char_code[1] class RuleOptionsType(TypedDict, total=False): diff --git a/tests/test_api/test_main.py b/tests/test_api/test_main.py index c84e33de..91f5f37b 100644 --- a/tests/test_api/test_main.py +++ b/tests/test_api/test_main.py @@ -544,3 +544,15 @@ def peek_rule(state, silent): # the rule saw the text accumulate one character at a time assert seen[:4] == [0, 1, 2, 3] assert max(seen) == 9_999 + + +def test_src_char_code_follows_src(): + """The deprecated ``srcCharCode`` is rebuilt when ``src`` is reassigned.""" + from markdown_it.rules_block.state_block import StateBlock + + state = StateBlock("ab", MarkdownIt(), {}, []) + with pytest.warns(DeprecationWarning): + assert state.srcCharCode == (97, 98) + state.src = "xyz" + with pytest.warns(DeprecationWarning): + assert state.srcCharCode == (120, 121, 122) From 5b85c67c21a02e49b7a52effb18662c1b1becce5 Mon Sep 17 00:00:00 2001 From: Afonso Ribeiro Date: Fri, 16 Oct 2026 05:57:16 +0100 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9A=20Link=20#450=20in=20the=20cha?= =?UTF-8?q?ngelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c979df0d..1cc16b8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -* šŸ‘Œ Speed up parsing (~6% on the CommonMark spec) by making `StateBase.src` a plain attribute instead of a property +* šŸ‘Œ Speed up parsing (~6% on the CommonMark spec) by making `StateBase.src` a plain attribute instead of a property, in [#450](https://github.com/executablebooks/markdown-it-py/pull/450) * ✨ Add `--enable-tables` to the CLI for file, standard input and interactive parsing in [#422](https://github.com/executablebooks/markdown-it-py/pull/422) * šŸ› Fix CLI interactive mode joining input lines with an extra newline, which split every line into its own paragraph and broke hard line breaks, in [#172](https://github.com/executablebooks/markdown-it-py/issues/172) * šŸ› Fix trimming and splitting with the Python whitespace set instead of the CommonMark one, which dropped U+001C–U+001F and U+0085 from paragraphs, headings, table cells and fence info strings and let distinct reference labels resolve each other, in [#418](https://github.com/executablebooks/markdown-it-py/pull/418), thanks to [@Nexory](https://github.com/Nexory) From f4d7b0e1212096b77bf92b07b339a7c0084981f7 Mon Sep 17 00:00:00 2001 From: Afonso Ribeiro Date: Fri, 16 Oct 2026 06:10:38 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=AA=20Fix=20mypy=20comparison-over?= =?UTF-8?q?lap=20in=20the=20srcCharCode=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_api/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api/test_main.py b/tests/test_api/test_main.py index 91f5f37b..0f106603 100644 --- a/tests/test_api/test_main.py +++ b/tests/test_api/test_main.py @@ -555,4 +555,4 @@ def test_src_char_code_follows_src(): assert state.srcCharCode == (97, 98) state.src = "xyz" with pytest.warns(DeprecationWarning): - assert state.srcCharCode == (120, 121, 122) + assert list(state.srcCharCode) == [120, 121, 122]