summaryrefslogtreecommitdiffstats
path: root/contrib/python/markdown-it-py
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-05-24 13:39:51 +0300
committerrobot-piglet <[email protected]>2026-05-24 14:17:03 +0300
commita328b20007004568c994dcd2aad60d2f8dbab5db (patch)
tree92b24327bfadb198b1525cde8b341179dca4c0db /contrib/python/markdown-it-py
parent61c451edf88125e836d9af09faac48c197673cad (diff)
Intermediate changes
commit_hash:9e9eb9789dc376bac164b089669f6c8adeefdc63
Diffstat (limited to 'contrib/python/markdown-it-py')
-rw-r--r--contrib/python/markdown-it-py/.dist-info/METADATA2
-rw-r--r--contrib/python/markdown-it-py/markdown_it/__init__.py2
-rw-r--r--contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py3
-rw-r--r--contrib/python/markdown-it-py/markdown_it/rules_block/fence.py177
-rw-r--r--contrib/python/markdown-it-py/ya.make2
5 files changed, 116 insertions, 70 deletions
diff --git a/contrib/python/markdown-it-py/.dist-info/METADATA b/contrib/python/markdown-it-py/.dist-info/METADATA
index 488e089b08e..c44604a6677 100644
--- a/contrib/python/markdown-it-py/.dist-info/METADATA
+++ b/contrib/python/markdown-it-py/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: markdown-it-py
-Version: 4.1.0
+Version: 4.2.0
Summary: Python port of markdown-it. Markdown parsing, done right!
Keywords: markdown,lexer,parser,commonmark,markdown-it
Author-email: Chris Sewell <[email protected]>
diff --git a/contrib/python/markdown-it-py/markdown_it/__init__.py b/contrib/python/markdown-it-py/markdown_it/__init__.py
index 523d92ef4ba..58dfa3bcc46 100644
--- a/contrib/python/markdown-it-py/markdown_it/__init__.py
+++ b/contrib/python/markdown-it-py/markdown_it/__init__.py
@@ -1,6 +1,6 @@
"""A Python port of Markdown-It"""
__all__ = ("MarkdownIt",)
-__version__ = "4.1.0"
+__version__ = "4.2.0"
from .main import MarkdownIt
diff --git a/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py b/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py
index 517da2312aa..2c5efa85ccf 100644
--- a/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py
+++ b/contrib/python/markdown-it-py/markdown_it/rules_block/__init__.py
@@ -8,6 +8,7 @@ __all__ = (
"html_block",
"lheading",
"list_block",
+ "make_fence_rule",
"paragraph",
"reference",
"table",
@@ -15,7 +16,7 @@ __all__ = (
from .blockquote import blockquote
from .code import code
-from .fence import fence
+from .fence import fence, make_fence_rule
from .heading import heading
from .hr import hr
from .html_block import html_block
diff --git a/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py b/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py
index 263f1b8de8d..0d7e651eb06 100644
--- a/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py
+++ b/contrib/python/markdown-it-py/markdown_it/rules_block/fence.py
@@ -1,4 +1,7 @@
# fences (``` lang, ~~~ lang)
+from __future__ import annotations
+
+from collections.abc import Callable
import logging
from .state_block import StateBlock
@@ -6,96 +9,138 @@ from .state_block import StateBlock
LOGGER = logging.getLogger(__name__)
-def fence(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
- LOGGER.debug("entering fence: %s, %s, %s, %s", state, startLine, endLine, silent)
+def make_fence_rule(
+ *,
+ markers: tuple[str, ...] = ("~", "`"),
+ token_type: str = "fence",
+ exact_match: bool = False,
+ disallow_marker_in_info: tuple[str, ...] = ("`",),
+ min_markers: int = 3,
+) -> Callable[[StateBlock, int, int, bool], bool]:
+ """Create a fence parsing rule with configurable options.
- haveEndMarker = False
- pos = state.bMarks[startLine] + state.tShift[startLine]
- maximum = state.eMarks[startLine]
+ :param markers: Tuple of single characters that can be used as fence markers.
+ :param token_type: The token type name to emit (e.g. "fence", "colon_fence").
+ :param exact_match: If True, the closing fence must have exactly the same
+ number of marker characters as the opening fence (not "at least as many").
+ This enables nesting of fences with different marker counts.
+ :param disallow_marker_in_info: Tuple of marker characters that are not allowed
+ to appear in the info string. The check only applies when the actual opening
+ marker is in this tuple (e.g. a tilde fence is unaffected by ``"`"`` being
+ listed). Per CommonMark, backtick fences cannot have backticks in the info
+ string. Use ``()`` to disable this restriction.
+ :param min_markers: Minimum number of marker characters to form a fence.
+ :return: A block rule function with signature
+ ``(state, startLine, endLine, silent) -> bool``.
+ """
- if state.is_code_block(startLine):
- return False
+ closing_matcher: Callable[[int, int], bool]
+ if exact_match:
+ # closing code fence must have exactly the same number of markers as the opening one
+ closing_matcher = lambda opening_len, closing_len: closing_len == opening_len # noqa: E731
+ else:
+ # closing code fence must be at least as long as the opening one
+ closing_matcher = lambda opening_len, closing_len: closing_len >= opening_len # noqa: E731
- if pos + 3 > maximum:
- return False
+ def _fence_rule(
+ state: StateBlock, startLine: int, endLine: int, silent: bool
+ ) -> bool:
+ LOGGER.debug(
+ "entering fence: %s, %s, %s, %s", state, startLine, endLine, silent
+ )
- marker = state.src[pos]
+ haveEndMarker = False
+ pos = state.bMarks[startLine] + state.tShift[startLine]
+ maximum = state.eMarks[startLine]
- if marker not in ("~", "`"):
- return False
+ if state.is_code_block(startLine):
+ return False
- # scan marker length
- mem = pos
- pos = state.skipCharsStr(pos, marker)
+ if pos + min_markers > maximum:
+ return False
- length = pos - mem
+ marker = state.src[pos]
- if length < 3:
- return False
+ if marker not in markers:
+ return False
- markup = state.src[mem:pos]
- params = state.src[pos:maximum]
+ # scan marker length
+ mem = pos
+ pos = state.skipCharsStr(pos, marker)
- if marker == "`" and marker in params:
- return False
+ length = pos - mem
- # Since start is found, we can report success here in validation mode
- if silent:
- return True
+ if length < min_markers:
+ return False
- # search end of block
- nextLine = startLine
+ markup = state.src[mem:pos]
+ params = state.src[pos:maximum]
- while True:
- nextLine += 1
- if nextLine >= endLine:
- # unclosed block should be autoclosed by end of document.
- # also block seems to be autoclosed by end of parent
- break
+ if marker in disallow_marker_in_info and marker in params:
+ return False
- pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
- maximum = state.eMarks[nextLine]
+ # Since start is found, we can report success here in validation mode
+ if silent:
+ return True
- if pos < maximum and state.sCount[nextLine] < state.blkIndent:
- # non-empty line with negative indent should stop the list:
- # - ```
- # test
- break
+ # search end of block
+ nextLine = startLine
+
+ while True:
+ nextLine += 1
+ if nextLine >= endLine:
+ # unclosed block should be autoclosed by end of document.
+ # also block seems to be autoclosed by end of parent
+ break
+
+ pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
+ maximum = state.eMarks[nextLine]
+
+ if pos < maximum and state.sCount[nextLine] < state.blkIndent:
+ # non-empty line with negative indent should stop the list:
+ # - ```
+ # test
+ break
+
+ try:
+ if state.src[pos] != marker:
+ continue
+ except IndexError:
+ break
- try:
- if state.src[pos] != marker:
+ if state.is_code_block(nextLine):
continue
- except IndexError:
- break
- if state.is_code_block(nextLine):
- continue
+ pos = state.skipCharsStr(pos, marker)
- pos = state.skipCharsStr(pos, marker)
+ if not closing_matcher(length, pos - mem):
+ continue
- # closing code fence must be at least as long as the opening one
- if pos - mem < length:
- continue
+ # make sure tail has spaces only
+ pos = state.skipSpaces(pos)
- # make sure tail has spaces only
- pos = state.skipSpaces(pos)
+ if pos < maximum:
+ continue
- if pos < maximum:
- continue
+ haveEndMarker = True
+ # found!
+ break
+
+ # If a fence has heading spaces, they should be removed from its inner block
+ length = state.sCount[startLine]
- haveEndMarker = True
- # found!
- break
+ state.line = nextLine + (1 if haveEndMarker else 0)
- # If a fence has heading spaces, they should be removed from its inner block
- length = state.sCount[startLine]
+ token = state.push(token_type, "code", 0)
+ token.info = params
+ token.content = state.getLines(startLine + 1, nextLine, length, True)
+ token.markup = markup
+ token.map = [startLine, state.line]
+
+ return True
- state.line = nextLine + (1 if haveEndMarker else 0)
+ return _fence_rule
- token = state.push("fence", "code", 0)
- token.info = params
- token.content = state.getLines(startLine + 1, nextLine, length, True)
- token.markup = markup
- token.map = [startLine, state.line]
- return True
+#: The default fence rule (backtick and tilde markers, CommonMark compliant).
+fence = make_fence_rule()
diff --git a/contrib/python/markdown-it-py/ya.make b/contrib/python/markdown-it-py/ya.make
index dd380e6d343..bdbb0a782c7 100644
--- a/contrib/python/markdown-it-py/ya.make
+++ b/contrib/python/markdown-it-py/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(4.1.0)
+VERSION(4.2.0)
LICENSE(MIT)