Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## python-markdown2 2.5.6 (not yet released)

- [pull #775] Speed up documents with many indented code blocks by skipping the `</code>` lookahead when the text has no `</code>`
- [pull #762] Speed up documents with many links by skipping the backslash-escape scan for text without a backslash
- [pull #748] Speed up `_unescape_special_chars` by skipping text with no hashes and dropping the full-text comparison per pass (#635)
- [pull #749] URL-encode destinations generated by the `link-patterns` extra (#55)
Expand Down
12 changes: 7 additions & 5 deletions lib/markdown2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2088,7 +2088,7 @@ def _html_class_str_from_tag(self, tag: str) -> str:
@mark_stage(Stage.CODE_BLOCKS)
def _do_code_blocks(self, text: str) -> str:
"""Process Markdown `<pre><code>` blocks."""
code_block_re = re.compile(r'''
code_block_pat = r'''
(?:\n\n|\A\n?)
( # $1 = the code block -- one or more lines, starting with a space/tab
(?:
Expand All @@ -2097,11 +2097,13 @@ def _do_code_blocks(self, text: str) -> str:
)+
)
((?=^[ ]{0,%d}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
''' % (self.tab_width, self.tab_width)
if '</code>' in text:
# Lookahead to make sure this block isn't already in a code block.
# Needed when syntax highlighting is being used.
(?!([^<]|<(/?)span)*\</code\>)
''' % (self.tab_width, self.tab_width),
re.M | re.X)
# Needed when syntax highlighting is being used. It scans ahead
# after every block, so leave it out when it can't match.
code_block_pat += r'(?!([^<]|<(/?)span)*\</code\>)'
code_block_re = re.compile(code_block_pat, re.M | re.X)
return code_block_re.sub(self._code_block_sub, text)

# Rules for a code span:
Expand Down
6 changes: 6 additions & 0 deletions test/test_redos.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ def issue_metadata_list_redos():
return "---\nitems:\n - " + " " * 32000 + "\n---\n\n# hello\n"


def many_code_blocks():
# not technically a redos: each code block used to rescan the rest of the text
return "".join(f"Paragraph {i}:\n\n code {i}\n\n" for i in range(3000))


# whack everything in a dict for easy lookup later on
CASES = {
fn.__name__: (fn, extras)
Expand All @@ -82,6 +87,7 @@ def issue_metadata_list_redos():
(issue_707_spaced_attrs, None),
(issue_metadata_redos, ['metadata']),
(issue_metadata_list_redos, ['metadata']),
(many_code_blocks, None),
]
}

Expand Down