Repository navigation
Fix quadratic processing time in fenced_code preprocessor (up to ~10x faster) - #1660
Conversation
|
You deleted the PR template. Please provide the requested information or this issue will be closed. |
|
Filled in the template, sorry about that. |
Cover text before, between and after blocks, a block that opens on the line right after another one closes, a block that ends the document without a trailing newline, and fifty blocks in one document.
|
Fair question. I checked it by mutating the new loop six ways (dropping the newline before or after the placeholder, dropping the text after the last block, resuming one character late, stopping after the first block, and not advancing the slice start): the existing Still, nothing in there says it directly, so I added four tests in db5968a: text before, between and after two blocks, a block that opens on the line right after another one closes, a block that ends the document without a trailing newline, and fifty blocks in one document. They pass on master as well, since the output is unchanged. The changelog conflict is fixed too (merged master, entry kept under Unreleased / Fixed). |
waylan
left a comment
There was a problem hiding this comment.
Thanks for doing the work on this.
Description
The
FencedBlockPreprocessorrebuilt the whole document text after every fenced code block it stashed (text = f'{text[:m.start()]}\n{placeholder}\n{text[m.end():]}'), so its cost grew with document size × number of blocks. This collects the output in pieces and joins them once.Matching is unchanged: the search now resumes at
m.end()in the original text instead of just after the placeholder in the rewritten text. The two differ only by the inserted\n, and since the fence pattern ends in[ ]*$,text[m.end()]is always\nor the end of the text, so no fence can begin at that position either way.The project's own
docs/*.mdconcatenated (250 KB, ~220 fenced blocks), and the same text ×4 (1 MB). Python 3.12, Apple M5, 7 alternating processes per side:markdown(text, extensions=['extra']), 250 KBmarkdown(text, extensions=['extra']), 1 MBextraon a 10 KB documentRe-measured on CPython 3.14.7 (7 alternating processes per side, median): the docs concatenated with the extra extensions went from 89.3 ms to 78.7 ms (1.14x).
Output is byte-identical to the current code (same SHA-256) for 169 files (all
tests/**/*.txtfixtures anddocs/**/*.md) × 4 extension sets, plus 12,000 randomized documents with fence edge cases (unterminated fences, invalid attribute lists, missing trailing newline, fences inside blockquotes and lists) underfenced_code,fenced_code+attr_list, andextra.python -m unittest discover tests: 1127 OK; flake8 clean on the changed file. No new test, since the output does not change and the existing fenced_code tests cover the paths touched.Sorry for dropping the template the first time.
AI Assistance Disclosure
No AI tools were used in preparing this PR.
AI tools were used and I have disclosed which ones, and have fully reviewed and verified their output.
AI tools were use to assist with the following:
Checklist