This release fixes two quadratic-time parsing paths that let crafted input stall rendering for minutes (security),
aligns several parsing rules with CommonMark 0.31.2 and markdown-it 15.0.x,
turns parser rules that never advance into a RuntimeError instead of an infinite loop,
makes rendering linear on PyPy and free-threaded CPython, and adds support for Python 3.14.
Python 3.10 is still supported.
If you write plugins or render rules, or edit SyntaxTreeNode trees, please read the upgrade notes at the end.
š Security
- š Fix quadratic-time inline parsing of long runs of characters that start an inline rule without completing one (such as
&,<,~or incomplete entities), which let a few hundred kilobytes of input stall rendering for seconds to minutes, in #423, thanks to @hdimer (#411) - š Reject unterminated inline HTML (
<!--,<?,<![CDATA[,<!X) in constant time whenhtmlis enabled (as in the defaultcommonmarkpreset), instead of rescanning to the end of the input for every opener, in #428
⨠New
- ⨠Add
--enable-tablesto the CLI for file, standard input and interactive parsing in #425, thanks to @user01010111 (#422); requested by @hartwork in #352 - ā¬ļø Add support for Python 3.14 in #462
š Improved
- š Raise
RuntimeErrorinstead of looping forever when a block or inline rule reports a match without advancing (or moves backwards), or when no block rule matches a line, as markdown-it does, in #444, thanks to @rupayon123, @hukkin (#336) and @inchang-ing (#443); requested by @acaprino in #441 - š Fix quadratic-time HTML rendering on PyPy, GraalPy and free-threaded CPython by joining the rendered fragments once, in #458, thanks to @DicksonWu654
- š Make
StateBase.srca plain attribute, for about 4ā11% faster parsing on CPython, in #450, thanks to @deadstrobe5 - š Build block line markers with one scan per line instead of one step per character in #449, thanks to @John-Cusack
- š Name the Windows end-of-input keys (Ctrl-Z then Enter) in the CLI's interactive prompt and
-htext in #463
š Fixed
- š Fix HTML blocks of types 1ā5 (comments, processing instructions, declarations, CDATA,
script/pre/style/textarea) ending at a blank line inside a list item or blockquote, porting markdown-it 14.2.0, in #406, thanks to @binggao1230; reported by @Jayman2000 in #377 - š Fix HTML block starts per CommonMark 0.31.2: lowercase declarations such as
<!doctype html>start a block and interrupt a paragraph, and tag names match case-insensitively in ASCII only, in #440, thanks to @rupayon123; reported by @p5ych0 in #437 - š Fix code spans rendered as literal backticks after an unclosed link or image label, and count only U+0020 spaces (not tabs or no-break spaces) when deciding whether to strip a code span's padding, porting markdown-it 15.0.1, in #439, thanks to @SparkUiX; reported by @felzab in #438
- š Trim and split with the CommonMark whitespace set instead of Python's, so U+001CāU+001F and U+0085 are no longer dropped from paragraphs, headings, table cells and fence info strings, and distinct reference labels no longer match each other, in #433, thanks to @Nexory (#418)
- š Preserve IPv6 host brackets (e.g.
http://[::1]:8000/) in normalized link and image URLs, porting markdown-it 15.0.1, in #459, thanks to @Jinyong-Li - š Fix task-list detection consuming a literal
[ ]/[x]at the start of an indented code block in a list item in #451, thanks to @AryanRG - š Fix an
IndexErrorin thehtml_blockandheadingrules at the end of the input (a table in a blockquote that ends on>) in #416, thanks to @saket3395; reported by @WilcoPancras in #415 - š Keep backslash escapes and entities in image alt text (
givesalt="a * b"), porting markdown-it 15.0.0, in #446, thanks to @gyanu2507; reported by @kdeldycke in #445 - š Keep code span text in image alt text, porting markdown-it 15.0.0, in #460, thanks to @jayhemnani9910
- š Keep hard breaks and inline HTML in image alt text in #409, thanks to @chuenchen309
- š Keep edits to the children of inline and image nodes in
SyntaxTreeNode.to_tokens()in #453, thanks to @KennyMcSimpson; edited tokens are copied, so the token stream the tree was built from is left unchanged (#464) - š Fix rule chains staying stale after
Ruler.enable,disableorenableOnlyraised on an unknown rule name in #457, thanks to @IlyaKhalafi; reported in #456 - š Fix
MarkdownIt.enable()anddisable()applying a generator of rule names only to the core chain in #447, thanks to @muneikh - š Fix the CLI's interactive mode joining input lines with an extra newline, which split every line into its own paragraph and broke hard line breaks, in #425; reported by @jamesquilty in #172
- š CLI helpers create a default parser only when none is given in #455, thanks to @Quinnod345
š Documentation
- š Add a "Third-party renderers" section (mdformat, mdit_plain, MyST-Parser) and document
RendererProtocolfor writing your own renderer, in #463 - š Document that
MarkdownItpasses itself torenderer_cls, unlike markdown-it's renderer constructor, in #421, thanks to @user01010111 - š Clarify that the name given to
add_render_rulemust match a token type in #432, thanks to @hikmetba-bit - š Fix typos and use HTTPS for the CommonMark spec links in #402, #407 and #408, thanks to @nikolas, @maxtaran2010 and @deeferentleeg
š§ Maintenance
- š§ Publish to PyPI with trusted publishing and attestations, uploading distributions built with
python -m buildand checked on every pull request, in #397 and #461 - š§ Include
tests/in the sdist (next totox.iniandCHANGELOG.md), so packagers can run the test suite from it, in #426, thanks to @BetterAndBetterII (#420), and #461; requested by @mtelka in #261 - š§ Move
coverageandpytest-covfrom thetestingextra to a newcoverageextra, and skip the linkify tests when linkify-it-py is not installed, in #463; requested by @befeleme in #195 - š§ Read the CommonMark spec fixtures as UTF-8, so the tests pass on Windows with a non-UTF-8 locale, in #454, thanks to @Aschenbath
- š§ Drop the optional dependency on the unmaintained
commonmarkpackage from thecompareextra in #427, thanks to @nikolas (#401) - š§ Build the documentation on Python 3.13 and Sphinx 9 in #462
Upgrade notes
- Rules that do not advance now raise.
A block or inline rule that returnsTruewithout movingstate.lineorstate.posforward now raisesRuntimeError(block rule didn't increment state.line,inline rule didn't increment state.pos), where it used to hang.
So does a line that no block rule accepts, which is only possible with theparagraphrule disabled (none of the block rules matched). - Image
childrenhold joinedtexttokens.
As in markdown-it 15,text_joinnow also joins thetext_specialtokens among an image token'schildren, so code that reads alt text from them gets escaped characters and entities as plaintexttokens (a nested image's ownchildrenare still not joined, as upstream). - Render rules must return
str.
The rendered fragments are now joined withstr.join, so a rule that returns astrsubclass (such asmarkupsafe.Markup) gives a plainstrfromrender(), and any non-strresult (even one that defines__radd__, which 4.2.0 let through) raisesTypeErrorwhen the fragments are joined. SyntaxTreeNode.to_tokens()follows the tree.
The tree'schildrenare the source of truth: a change made directly to a token'schildrenlist no longer appears in the output, andto_tokens()does not modify the token stream the tree was built from.- Rule names given as a generator to
MarkdownIt.enable()ordisable()are validated like a list, so an unknown name now raisesValueError. - Internals plugins may touch.
StateInline.pendingis now a property backed by a buffer:state.pending += textstill works, and the newstate.append_pending(text)appends in constant time.
HTML_TAG_RE(markdown_it.common.html_re) andDIGITAL_REandNAMED_RE(markdown_it.rules_inline.entity) no longer start with^, because they are applied with.match(src, pos); code that calls.search()on them now matches anywhere in the string.
The privateStateBase._srcand_srcCharCodeattributes are gone (srcCharCodeitself is still there, and still deprecated). - Packaging.
Thetestingextra no longer installscoverageandpytest-cov: install.[testing,coverage]to run the tests with--cov.
The sdist now containstests/, and no longer contains repository files such as.github/,.pre-commit-config.yamlandscripts/.
Full Changelog: v4.2.0...v4.3.0
Full changelog: v4.2.0...v4.3.0