anonymous-CAP commited on
Commit
52c13bd
·
verified ·
1 Parent(s): de3bbe0

Upload folder using huggingface_hub (part 6)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +3 -0
  2. venv/lib/python3.12/site-packages/markdown_it/common/html_blocks.py +69 -0
  3. venv/lib/python3.12/site-packages/markdown_it/common/html_re.py +39 -0
  4. venv/lib/python3.12/site-packages/markdown_it/common/normalize_url.py +81 -0
  5. venv/lib/python3.12/site-packages/markdown_it/common/utils.py +313 -0
  6. venv/lib/python3.12/site-packages/markdown_it/helpers/__init__.py +6 -0
  7. venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/__init__.cpython-312.pyc +0 -0
  8. venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/parse_link_destination.cpython-312.pyc +0 -0
  9. venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/parse_link_label.cpython-312.pyc +0 -0
  10. venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/parse_link_title.cpython-312.pyc +0 -0
  11. venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_destination.py +83 -0
  12. venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_label.py +44 -0
  13. venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_title.py +75 -0
  14. venv/lib/python3.12/site-packages/markdown_it/main.py +351 -0
  15. venv/lib/python3.12/site-packages/markdown_it/parser_block.py +113 -0
  16. venv/lib/python3.12/site-packages/markdown_it/parser_core.py +46 -0
  17. venv/lib/python3.12/site-packages/markdown_it/parser_inline.py +215 -0
  18. venv/lib/python3.12/site-packages/markdown_it/port.yaml +48 -0
  19. venv/lib/python3.12/site-packages/markdown_it/presets/__init__.py +48 -0
  20. venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/__init__.cpython-312.pyc +0 -0
  21. venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/commonmark.cpython-312.pyc +0 -0
  22. venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/default.cpython-312.pyc +0 -0
  23. venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/zero.cpython-312.pyc +0 -0
  24. venv/lib/python3.12/site-packages/markdown_it/presets/commonmark.py +75 -0
  25. venv/lib/python3.12/site-packages/markdown_it/presets/default.py +36 -0
  26. venv/lib/python3.12/site-packages/markdown_it/presets/zero.py +44 -0
  27. venv/lib/python3.12/site-packages/markdown_it/py.typed +1 -0
  28. venv/lib/python3.12/site-packages/markdown_it/renderer.py +356 -0
  29. venv/lib/python3.12/site-packages/markdown_it/ruler.py +275 -0
  30. venv/lib/python3.12/site-packages/markdown_it/rules_block/__init__.py +28 -0
  31. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/__init__.cpython-312.pyc +0 -0
  32. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/blockquote.cpython-312.pyc +0 -0
  33. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/code.cpython-312.pyc +0 -0
  34. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/fence.cpython-312.pyc +0 -0
  35. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/heading.cpython-312.pyc +0 -0
  36. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/hr.cpython-312.pyc +0 -0
  37. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/html_block.cpython-312.pyc +0 -0
  38. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/lheading.cpython-312.pyc +0 -0
  39. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/list.cpython-312.pyc +0 -0
  40. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/paragraph.cpython-312.pyc +0 -0
  41. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/reference.cpython-312.pyc +0 -0
  42. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/state_block.cpython-312.pyc +0 -0
  43. venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/table.cpython-312.pyc +0 -0
  44. venv/lib/python3.12/site-packages/markdown_it/rules_block/blockquote.py +368 -0
  45. venv/lib/python3.12/site-packages/markdown_it/rules_block/code.py +36 -0
  46. venv/lib/python3.12/site-packages/markdown_it/rules_block/fence.py +146 -0
  47. venv/lib/python3.12/site-packages/markdown_it/rules_block/heading.py +69 -0
  48. venv/lib/python3.12/site-packages/markdown_it/rules_block/hr.py +56 -0
  49. venv/lib/python3.12/site-packages/markdown_it/rules_block/html_block.py +90 -0
  50. venv/lib/python3.12/site-packages/markdown_it/rules_block/lheading.py +86 -0
.gitattributes CHANGED
@@ -64,3 +64,6 @@ venv/lib/python3.12/site-packages/huggingface_hub/inference/__pycache__/_client.
64
  venv/lib/python3.12/site-packages/huggingface_hub/inference/_generated/__pycache__/_async_client.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
65
  venv/lib/python3.12/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
66
  venv/lib/python3.12/site-packages/joblib/test/__pycache__/test_parallel.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
 
 
 
 
64
  venv/lib/python3.12/site-packages/huggingface_hub/inference/_generated/__pycache__/_async_client.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
65
  venv/lib/python3.12/site-packages/jinja2/__pycache__/compiler.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
66
  venv/lib/python3.12/site-packages/joblib/test/__pycache__/test_parallel.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
67
+ venv/lib/python3.12/site-packages/mpmath/__pycache__/function_docs.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
68
+ venv/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_fp.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
69
+ venv/lib/python3.12/site-packages/mpmath/tests/__pycache__/test_functions2.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
venv/lib/python3.12/site-packages/markdown_it/common/html_blocks.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """List of valid html blocks names, according to commonmark spec
2
+ http://jgm.github.io/CommonMark/spec.html#html-blocks
3
+ """
4
+
5
+ # see https://spec.commonmark.org/0.31.2/#html-blocks
6
+ block_names = [
7
+ "address",
8
+ "article",
9
+ "aside",
10
+ "base",
11
+ "basefont",
12
+ "blockquote",
13
+ "body",
14
+ "caption",
15
+ "center",
16
+ "col",
17
+ "colgroup",
18
+ "dd",
19
+ "details",
20
+ "dialog",
21
+ "dir",
22
+ "div",
23
+ "dl",
24
+ "dt",
25
+ "fieldset",
26
+ "figcaption",
27
+ "figure",
28
+ "footer",
29
+ "form",
30
+ "frame",
31
+ "frameset",
32
+ "h1",
33
+ "h2",
34
+ "h3",
35
+ "h4",
36
+ "h5",
37
+ "h6",
38
+ "head",
39
+ "header",
40
+ "hr",
41
+ "html",
42
+ "iframe",
43
+ "legend",
44
+ "li",
45
+ "link",
46
+ "main",
47
+ "menu",
48
+ "menuitem",
49
+ "nav",
50
+ "noframes",
51
+ "ol",
52
+ "optgroup",
53
+ "option",
54
+ "p",
55
+ "param",
56
+ "search",
57
+ "section",
58
+ "summary",
59
+ "table",
60
+ "tbody",
61
+ "td",
62
+ "tfoot",
63
+ "th",
64
+ "thead",
65
+ "title",
66
+ "tr",
67
+ "track",
68
+ "ul",
69
+ ]
venv/lib/python3.12/site-packages/markdown_it/common/html_re.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Regexps to match html elements"""
2
+
3
+ import re
4
+
5
+ attr_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*"
6
+
7
+ unquoted = "[^\"'=<>`\\x00-\\x20]+"
8
+ single_quoted = "'[^']*'"
9
+ double_quoted = '"[^"]*"'
10
+
11
+ attr_value = "(?:" + unquoted + "|" + single_quoted + "|" + double_quoted + ")"
12
+
13
+ attribute = "(?:\\s+" + attr_name + "(?:\\s*=\\s*" + attr_value + ")?)"
14
+
15
+ open_tag = "<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/?>"
16
+
17
+ close_tag = "<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>"
18
+ comment = "<!---?>|<!--(?:[^-]|-[^-]|--[^>])*-->"
19
+ processing = "<[?][\\s\\S]*?[?]>"
20
+ declaration = "<![A-Za-z][^>]*>"
21
+ cdata = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"
22
+
23
+ HTML_TAG_RE = re.compile(
24
+ "^(?:"
25
+ + open_tag
26
+ + "|"
27
+ + close_tag
28
+ + "|"
29
+ + comment
30
+ + "|"
31
+ + processing
32
+ + "|"
33
+ + declaration
34
+ + "|"
35
+ + cdata
36
+ + ")"
37
+ )
38
+ HTML_OPEN_CLOSE_TAG_STR = "^(?:" + open_tag + "|" + close_tag + ")"
39
+ HTML_OPEN_CLOSE_TAG_RE = re.compile(HTML_OPEN_CLOSE_TAG_STR)
venv/lib/python3.12/site-packages/markdown_it/common/normalize_url.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ from contextlib import suppress
5
+ import re
6
+ from urllib.parse import quote, unquote, urlparse, urlunparse # noqa: F401
7
+
8
+ import mdurl
9
+
10
+ from .. import _punycode
11
+
12
+ RECODE_HOSTNAME_FOR = ("http:", "https:", "mailto:")
13
+
14
+
15
+ def normalizeLink(url: str) -> str:
16
+ """Normalize destination URLs in links
17
+
18
+ ::
19
+
20
+ [label]: destination 'title'
21
+ ^^^^^^^^^^^
22
+ """
23
+ parsed = mdurl.parse(url, slashes_denote_host=True)
24
+
25
+ # Encode hostnames in urls like:
26
+ # `http://host/`, `https://host/`, `mailto:user@host`, `//host/`
27
+ #
28
+ # We don't encode unknown schemas, because it's likely that we encode
29
+ # something we shouldn't (e.g. `skype:name` treated as `skype:host`)
30
+ #
31
+ if parsed.hostname and (
32
+ not parsed.protocol or parsed.protocol in RECODE_HOSTNAME_FOR
33
+ ):
34
+ with suppress(Exception):
35
+ parsed = parsed._replace(hostname=_punycode.to_ascii(parsed.hostname))
36
+
37
+ return mdurl.encode(mdurl.format(parsed))
38
+
39
+
40
+ def normalizeLinkText(url: str) -> str:
41
+ """Normalize autolink content
42
+
43
+ ::
44
+
45
+ <destination>
46
+ ~~~~~~~~~~~
47
+ """
48
+ parsed = mdurl.parse(url, slashes_denote_host=True)
49
+
50
+ # Encode hostnames in urls like:
51
+ # `http://host/`, `https://host/`, `mailto:user@host`, `//host/`
52
+ #
53
+ # We don't encode unknown schemas, because it's likely that we encode
54
+ # something we shouldn't (e.g. `skype:name` treated as `skype:host`)
55
+ #
56
+ if parsed.hostname and (
57
+ not parsed.protocol or parsed.protocol in RECODE_HOSTNAME_FOR
58
+ ):
59
+ with suppress(Exception):
60
+ parsed = parsed._replace(hostname=_punycode.to_unicode(parsed.hostname))
61
+
62
+ # add '%' to exclude list because of https://github.com/markdown-it/markdown-it/issues/720
63
+ return mdurl.decode(mdurl.format(parsed), mdurl.DECODE_DEFAULT_CHARS + "%")
64
+
65
+
66
+ BAD_PROTO_RE = re.compile(r"^(vbscript|javascript|file|data):")
67
+ GOOD_DATA_RE = re.compile(r"^data:image\/(gif|png|jpeg|webp);")
68
+
69
+
70
+ def validateLink(url: str, validator: Callable[[str], bool] | None = None) -> bool:
71
+ """Validate URL link is allowed in output.
72
+
73
+ This validator can prohibit more than really needed to prevent XSS.
74
+ It's a tradeoff to keep code simple and to be secure by default.
75
+
76
+ Note: url should be normalized at this point, and existing entities decoded.
77
+ """
78
+ if validator is not None:
79
+ return validator(url)
80
+ url = url.strip().lower()
81
+ return bool(GOOD_DATA_RE.search(url)) if BAD_PROTO_RE.search(url) else True
venv/lib/python3.12/site-packages/markdown_it/common/utils.py ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities for parsing source text"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from re import Match
7
+ from typing import TypeVar
8
+ import unicodedata
9
+
10
+ from .entities import entities
11
+
12
+
13
+ def charCodeAt(src: str, pos: int) -> int | None:
14
+ """
15
+ Returns the Unicode value of the character at the specified location.
16
+
17
+ @param - index The zero-based index of the desired character.
18
+ If there is no character at the specified index, NaN is returned.
19
+
20
+ This was added for compatibility with python
21
+ """
22
+ try:
23
+ return ord(src[pos])
24
+ except IndexError:
25
+ return None
26
+
27
+
28
+ def charStrAt(src: str, pos: int) -> str | None:
29
+ """
30
+ Returns the Unicode value of the character at the specified location.
31
+
32
+ @param - index The zero-based index of the desired character.
33
+ If there is no character at the specified index, NaN is returned.
34
+
35
+ This was added for compatibility with python
36
+ """
37
+ try:
38
+ return src[pos]
39
+ except IndexError:
40
+ return None
41
+
42
+
43
+ _ItemTV = TypeVar("_ItemTV")
44
+
45
+
46
+ def arrayReplaceAt(
47
+ src: list[_ItemTV], pos: int, newElements: list[_ItemTV]
48
+ ) -> list[_ItemTV]:
49
+ """
50
+ Remove element from array and put another array at those position.
51
+ Useful for some operations with tokens
52
+ """
53
+ return src[:pos] + newElements + src[pos + 1 :]
54
+
55
+
56
+ def isValidEntityCode(c: int) -> bool:
57
+ # broken sequence
58
+ if c >= 0xD800 and c <= 0xDFFF:
59
+ return False
60
+ # never used
61
+ if c >= 0xFDD0 and c <= 0xFDEF:
62
+ return False
63
+ if ((c & 0xFFFF) == 0xFFFF) or ((c & 0xFFFF) == 0xFFFE):
64
+ return False
65
+ # control codes
66
+ if c >= 0x00 and c <= 0x08:
67
+ return False
68
+ if c == 0x0B:
69
+ return False
70
+ if c >= 0x0E and c <= 0x1F:
71
+ return False
72
+ if c >= 0x7F and c <= 0x9F:
73
+ return False
74
+ # out of range
75
+ return not (c > 0x10FFFF)
76
+
77
+
78
+ def fromCodePoint(c: int) -> str:
79
+ """Convert ordinal to unicode.
80
+
81
+ Note, in the original Javascript two string characters were required,
82
+ for codepoints larger than `0xFFFF`.
83
+ But Python 3 can represent any unicode codepoint in one character.
84
+ """
85
+ return chr(c)
86
+
87
+
88
+ # UNESCAPE_MD_RE = re.compile(r'\\([!"#$%&\'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])')
89
+ # ENTITY_RE_g = re.compile(r'&([a-z#][a-z0-9]{1,31})', re.IGNORECASE)
90
+ UNESCAPE_ALL_RE = re.compile(
91
+ r'\\([!"#$%&\'()*+,\-.\/:;<=>?@[\\\]^_`{|}~])' + "|" + r"&([a-z#][a-z0-9]{1,31});",
92
+ re.IGNORECASE,
93
+ )
94
+ DIGITAL_ENTITY_BASE10_RE = re.compile(r"#([0-9]{1,8})")
95
+ DIGITAL_ENTITY_BASE16_RE = re.compile(r"#x([a-f0-9]{1,8})", re.IGNORECASE)
96
+
97
+
98
+ def replaceEntityPattern(match: str, name: str) -> str:
99
+ """Convert HTML entity patterns,
100
+ see https://spec.commonmark.org/0.30/#entity-references
101
+ """
102
+ if name in entities:
103
+ return entities[name]
104
+
105
+ code: None | int = None
106
+ if pat := DIGITAL_ENTITY_BASE10_RE.fullmatch(name):
107
+ code = int(pat.group(1), 10)
108
+ elif pat := DIGITAL_ENTITY_BASE16_RE.fullmatch(name):
109
+ code = int(pat.group(1), 16)
110
+
111
+ if code is not None and isValidEntityCode(code):
112
+ return fromCodePoint(code)
113
+
114
+ return match
115
+
116
+
117
+ def unescapeAll(string: str) -> str:
118
+ def replacer_func(match: Match[str]) -> str:
119
+ escaped = match.group(1)
120
+ if escaped:
121
+ return escaped
122
+ entity = match.group(2)
123
+ return replaceEntityPattern(match.group(), entity)
124
+
125
+ if "\\" not in string and "&" not in string:
126
+ return string
127
+ return UNESCAPE_ALL_RE.sub(replacer_func, string)
128
+
129
+
130
+ ESCAPABLE = r"""\\!"#$%&'()*+,./:;<=>?@\[\]^`{}|_~-"""
131
+ ESCAPE_CHAR = re.compile(r"\\([" + ESCAPABLE + r"])")
132
+
133
+
134
+ def stripEscape(string: str) -> str:
135
+ """Strip escape \\ characters"""
136
+ return ESCAPE_CHAR.sub(r"\1", string)
137
+
138
+
139
+ def escapeHtml(raw: str) -> str:
140
+ """Replace special characters "&", "<", ">" and '"' to HTML-safe sequences."""
141
+ # like html.escape, but without escaping single quotes
142
+ raw = raw.replace("&", "&amp;") # Must be done first!
143
+ raw = raw.replace("<", "&lt;")
144
+ raw = raw.replace(">", "&gt;")
145
+ raw = raw.replace('"', "&quot;")
146
+ return raw
147
+
148
+
149
+ # //////////////////////////////////////////////////////////////////////////////
150
+
151
+ REGEXP_ESCAPE_RE = re.compile(r"[.?*+^$[\]\\(){}|-]")
152
+
153
+
154
+ def escapeRE(string: str) -> str:
155
+ string = REGEXP_ESCAPE_RE.sub("\\$&", string)
156
+ return string
157
+
158
+
159
+ # //////////////////////////////////////////////////////////////////////////////
160
+
161
+
162
+ def isSpace(code: int | None) -> bool:
163
+ """Check if character code is a whitespace."""
164
+ return code in (0x09, 0x20)
165
+
166
+
167
+ def isStrSpace(ch: str | None) -> bool:
168
+ """Check if character is a whitespace."""
169
+ return ch in ("\t", " ")
170
+
171
+
172
+ MD_WHITESPACE = {
173
+ 0x09, # \t
174
+ 0x0A, # \n
175
+ 0x0B, # \v
176
+ 0x0C, # \f
177
+ 0x0D, # \r
178
+ 0x20, # space
179
+ 0xA0,
180
+ 0x1680,
181
+ 0x202F,
182
+ 0x205F,
183
+ 0x3000,
184
+ }
185
+
186
+
187
+ def isWhiteSpace(code: int) -> bool:
188
+ r"""Zs (unicode class) || [\t\f\v\r\n]"""
189
+ if code >= 0x2000 and code <= 0x200A:
190
+ return True
191
+ return code in MD_WHITESPACE
192
+
193
+
194
+ # //////////////////////////////////////////////////////////////////////////////
195
+
196
+
197
+ def isPunctChar(ch: str) -> bool:
198
+ """Check if character is a punctuation character."""
199
+ return unicodedata.category(ch).startswith(("P", "S"))
200
+
201
+
202
+ MD_ASCII_PUNCT = {
203
+ 0x21, # /* ! */
204
+ 0x22, # /* " */
205
+ 0x23, # /* # */
206
+ 0x24, # /* $ */
207
+ 0x25, # /* % */
208
+ 0x26, # /* & */
209
+ 0x27, # /* ' */
210
+ 0x28, # /* ( */
211
+ 0x29, # /* ) */
212
+ 0x2A, # /* * */
213
+ 0x2B, # /* + */
214
+ 0x2C, # /* , */
215
+ 0x2D, # /* - */
216
+ 0x2E, # /* . */
217
+ 0x2F, # /* / */
218
+ 0x3A, # /* : */
219
+ 0x3B, # /* ; */
220
+ 0x3C, # /* < */
221
+ 0x3D, # /* = */
222
+ 0x3E, # /* > */
223
+ 0x3F, # /* ? */
224
+ 0x40, # /* @ */
225
+ 0x5B, # /* [ */
226
+ 0x5C, # /* \ */
227
+ 0x5D, # /* ] */
228
+ 0x5E, # /* ^ */
229
+ 0x5F, # /* _ */
230
+ 0x60, # /* ` */
231
+ 0x7B, # /* { */
232
+ 0x7C, # /* | */
233
+ 0x7D, # /* } */
234
+ 0x7E, # /* ~ */
235
+ }
236
+
237
+
238
+ def isMdAsciiPunct(ch: int) -> bool:
239
+ """Markdown ASCII punctuation characters.
240
+
241
+ ::
242
+
243
+ !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \\, ], ^, _, `, {, |, }, or ~
244
+
245
+ See http://spec.commonmark.org/0.15/#ascii-punctuation-character
246
+
247
+ Don't confuse with unicode punctuation !!! It lacks some chars in ascii range.
248
+
249
+ """
250
+ return ch in MD_ASCII_PUNCT
251
+
252
+
253
+ def normalizeReference(string: str) -> str:
254
+ """Helper to unify [reference labels]."""
255
+ # Trim and collapse whitespace
256
+ #
257
+ string = re.sub(r"\s+", " ", string.strip())
258
+
259
+ # In node v10 'ẞ'.toLowerCase() === 'Ṿ', which is presumed to be a bug
260
+ # fixed in v12 (couldn't find any details).
261
+ #
262
+ # So treat this one as a special case
263
+ # (remove this when node v10 is no longer supported).
264
+ #
265
+ # if ('ẞ'.toLowerCase() === 'Ṿ') {
266
+ # str = str.replace(/ẞ/g, 'ß')
267
+ # }
268
+
269
+ # .toLowerCase().toUpperCase() should get rid of all differences
270
+ # between letter variants.
271
+ #
272
+ # Simple .toLowerCase() doesn't normalize 125 code points correctly,
273
+ # and .toUpperCase doesn't normalize 6 of them (list of exceptions:
274
+ # İ, ϴ, ẞ, Ω, K, Å - those are already uppercased, but have differently
275
+ # uppercased versions).
276
+ #
277
+ # Here's an example showing how it happens. Lets take greek letter omega:
278
+ # uppercase U+0398 (Θ), U+03f4 (ϴ) and lowercase U+03b8 (θ), U+03d1 (ϑ)
279
+ #
280
+ # Unicode entries:
281
+ # 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8
282
+ # 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398
283
+ # 03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398
284
+ # 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8
285
+ #
286
+ # Case-insensitive comparison should treat all of them as equivalent.
287
+ #
288
+ # But .toLowerCase() doesn't change ϑ (it's already lowercase),
289
+ # and .toUpperCase() doesn't change ϴ (already uppercase).
290
+ #
291
+ # Applying first lower then upper case normalizes any character:
292
+ # '\u0398\u03f4\u03b8\u03d1'.toLowerCase().toUpperCase() === '\u0398\u0398\u0398\u0398'
293
+ #
294
+ # Note: this is equivalent to unicode case folding; unicode normalization
295
+ # is a different step that is not required here.
296
+ #
297
+ # Final result should be uppercased, because it's later stored in an object
298
+ # (this avoid a conflict with Object.prototype members,
299
+ # most notably, `__proto__`)
300
+ #
301
+ return string.lower().upper()
302
+
303
+
304
+ LINK_OPEN_RE = re.compile(r"^<a[>\s]", flags=re.IGNORECASE)
305
+ LINK_CLOSE_RE = re.compile(r"^</a\s*>", flags=re.IGNORECASE)
306
+
307
+
308
+ def isLinkOpen(string: str) -> bool:
309
+ return bool(LINK_OPEN_RE.search(string))
310
+
311
+
312
+ def isLinkClose(string: str) -> bool:
313
+ return bool(LINK_CLOSE_RE.search(string))
venv/lib/python3.12/site-packages/markdown_it/helpers/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ """Functions for parsing Links"""
2
+
3
+ __all__ = ("parseLinkDestination", "parseLinkLabel", "parseLinkTitle")
4
+ from .parse_link_destination import parseLinkDestination
5
+ from .parse_link_label import parseLinkLabel
6
+ from .parse_link_title import parseLinkTitle
venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (443 Bytes). View file
 
venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/parse_link_destination.cpython-312.pyc ADDED
Binary file (2.1 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/parse_link_label.cpython-312.pyc ADDED
Binary file (1.41 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/helpers/__pycache__/parse_link_title.cpython-312.pyc ADDED
Binary file (2.39 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_destination.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Parse link destination
3
+ """
4
+
5
+ from ..common.utils import charCodeAt, unescapeAll
6
+
7
+
8
+ class _Result:
9
+ __slots__ = ("ok", "pos", "str")
10
+
11
+ def __init__(self) -> None:
12
+ self.ok = False
13
+ self.pos = 0
14
+ self.str = ""
15
+
16
+
17
+ def parseLinkDestination(string: str, pos: int, maximum: int) -> _Result:
18
+ start = pos
19
+ result = _Result()
20
+
21
+ if charCodeAt(string, pos) == 0x3C: # /* < */
22
+ pos += 1
23
+ while pos < maximum:
24
+ code = charCodeAt(string, pos)
25
+ if code == 0x0A: # /* \n */)
26
+ return result
27
+ if code == 0x3C: # / * < * /
28
+ return result
29
+ if code == 0x3E: # /* > */) {
30
+ result.pos = pos + 1
31
+ result.str = unescapeAll(string[start + 1 : pos])
32
+ result.ok = True
33
+ return result
34
+
35
+ if code == 0x5C and pos + 1 < maximum: # \
36
+ pos += 2
37
+ continue
38
+
39
+ pos += 1
40
+
41
+ # no closing '>'
42
+ return result
43
+
44
+ # this should be ... } else { ... branch
45
+
46
+ level = 0
47
+ while pos < maximum:
48
+ code = charCodeAt(string, pos)
49
+
50
+ if code is None or code == 0x20:
51
+ break
52
+
53
+ # ascii control characters
54
+ if code < 0x20 or code == 0x7F:
55
+ break
56
+
57
+ if code == 0x5C and pos + 1 < maximum:
58
+ if charCodeAt(string, pos + 1) == 0x20:
59
+ break
60
+ pos += 2
61
+ continue
62
+
63
+ if code == 0x28: # /* ( */)
64
+ level += 1
65
+ if level > 32:
66
+ return result
67
+
68
+ if code == 0x29: # /* ) */)
69
+ if level == 0:
70
+ break
71
+ level -= 1
72
+
73
+ pos += 1
74
+
75
+ if start == pos:
76
+ return result
77
+ if level != 0:
78
+ return result
79
+
80
+ result.str = unescapeAll(string[start:pos])
81
+ result.pos = pos
82
+ result.ok = True
83
+ return result
venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_label.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Parse link label
3
+
4
+ this function assumes that first character ("[") already matches
5
+ returns the end of the label
6
+
7
+ """
8
+
9
+ from markdown_it.rules_inline import StateInline
10
+
11
+
12
+ def parseLinkLabel(state: StateInline, start: int, disableNested: bool = False) -> int:
13
+ labelEnd = -1
14
+ oldPos = state.pos
15
+ found = False
16
+
17
+ state.pos = start + 1
18
+ level = 1
19
+
20
+ while state.pos < state.posMax:
21
+ marker = state.src[state.pos]
22
+ if marker == "]":
23
+ level -= 1
24
+ if level == 0:
25
+ found = True
26
+ break
27
+
28
+ prevPos = state.pos
29
+ state.md.inline.skipToken(state)
30
+ if marker == "[":
31
+ if prevPos == state.pos - 1:
32
+ # increase level if we find text `[`,
33
+ # which is not a part of any token
34
+ level += 1
35
+ elif disableNested:
36
+ state.pos = oldPos
37
+ return -1
38
+ if found:
39
+ labelEnd = state.pos
40
+
41
+ # restore old state
42
+ state.pos = oldPos
43
+
44
+ return labelEnd
venv/lib/python3.12/site-packages/markdown_it/helpers/parse_link_title.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Parse link title"""
2
+
3
+ from ..common.utils import charCodeAt, unescapeAll
4
+
5
+
6
+ class _State:
7
+ __slots__ = ("can_continue", "marker", "ok", "pos", "str")
8
+
9
+ def __init__(self) -> None:
10
+ self.ok = False
11
+ """if `true`, this is a valid link title"""
12
+ self.can_continue = False
13
+ """if `true`, this link can be continued on the next line"""
14
+ self.pos = 0
15
+ """if `ok`, it's the position of the first character after the closing marker"""
16
+ self.str = ""
17
+ """if `ok`, it's the unescaped title"""
18
+ self.marker = 0
19
+ """expected closing marker character code"""
20
+
21
+ def __str__(self) -> str:
22
+ return self.str
23
+
24
+
25
+ def parseLinkTitle(
26
+ string: str, start: int, maximum: int, prev_state: _State | None = None
27
+ ) -> _State:
28
+ """Parse link title within `str` in [start, max] range,
29
+ or continue previous parsing if `prev_state` is defined (equal to result of last execution).
30
+ """
31
+ pos = start
32
+ state = _State()
33
+
34
+ if prev_state is not None:
35
+ # this is a continuation of a previous parseLinkTitle call on the next line,
36
+ # used in reference links only
37
+ state.str = prev_state.str
38
+ state.marker = prev_state.marker
39
+ else:
40
+ if pos >= maximum:
41
+ return state
42
+
43
+ marker = charCodeAt(string, pos)
44
+
45
+ # /* " */ /* ' */ /* ( */
46
+ if marker != 0x22 and marker != 0x27 and marker != 0x28:
47
+ return state
48
+
49
+ start += 1
50
+ pos += 1
51
+
52
+ # if opening marker is "(", switch it to closing marker ")"
53
+ if marker == 0x28:
54
+ marker = 0x29
55
+
56
+ state.marker = marker
57
+
58
+ while pos < maximum:
59
+ code = charCodeAt(string, pos)
60
+ if code == state.marker:
61
+ state.pos = pos + 1
62
+ state.str += unescapeAll(string[start:pos])
63
+ state.ok = True
64
+ return state
65
+ elif code == 0x28 and state.marker == 0x29: # /* ( */ /* ) */
66
+ return state
67
+ elif code == 0x5C and pos + 1 < maximum: # /* \ */
68
+ pos += 1
69
+
70
+ pos += 1
71
+
72
+ # no closing marker found, but this link title may continue on the next line (for references)
73
+ state.can_continue = True
74
+ state.str += unescapeAll(string[start:pos])
75
+ return state
venv/lib/python3.12/site-packages/markdown_it/main.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping
4
+ from contextlib import contextmanager
5
+ from typing import Any, Literal, overload
6
+
7
+ from . import helpers, presets
8
+ from .common import normalize_url, utils
9
+ from .parser_block import ParserBlock
10
+ from .parser_core import ParserCore
11
+ from .parser_inline import ParserInline
12
+ from .renderer import RendererHTML, RendererProtocol
13
+ from .rules_core.state_core import StateCore
14
+ from .token import Token
15
+ from .utils import EnvType, OptionsDict, OptionsType, PresetType
16
+
17
+ try:
18
+ import linkify_it
19
+ except ModuleNotFoundError:
20
+ linkify_it = None
21
+
22
+
23
+ _PRESETS: dict[str, PresetType] = {
24
+ "default": presets.default.make(),
25
+ "js-default": presets.js_default.make(),
26
+ "zero": presets.zero.make(),
27
+ "commonmark": presets.commonmark.make(),
28
+ "gfm-like": presets.gfm_like.make(),
29
+ "gfm-like2": presets.gfm_like2.make(),
30
+ }
31
+
32
+
33
+ class MarkdownIt:
34
+ def __init__(
35
+ self,
36
+ config: str | PresetType = "commonmark",
37
+ options_update: Mapping[str, Any] | None = None,
38
+ *,
39
+ renderer_cls: Callable[[MarkdownIt], RendererProtocol] = RendererHTML,
40
+ ):
41
+ """Main parser class
42
+
43
+ :param config: name of configuration to load or a pre-defined dictionary
44
+ :param options_update: dictionary that will be merged into ``config["options"]``
45
+ :param renderer_cls: the class to load as the renderer:
46
+ ``self.renderer = renderer_cls(self)
47
+ """
48
+ # add modules
49
+ self.utils = utils
50
+ self.helpers = helpers
51
+
52
+ # initialise classes
53
+ self.inline = ParserInline()
54
+ self.block = ParserBlock()
55
+ self.core = ParserCore()
56
+ self.renderer = renderer_cls(self)
57
+ self.linkify = linkify_it.LinkifyIt() if linkify_it else None
58
+
59
+ # set the configuration
60
+ if options_update and not isinstance(options_update, Mapping):
61
+ # catch signature change where renderer_cls was not used as a key-word
62
+ raise TypeError(
63
+ f"options_update should be a mapping: {options_update}"
64
+ "\n(Perhaps you intended this to be the renderer_cls?)"
65
+ )
66
+ self.configure(config, options_update=options_update)
67
+
68
+ def __repr__(self) -> str:
69
+ return f"{self.__class__.__module__}.{self.__class__.__name__}()"
70
+
71
+ @overload
72
+ def __getitem__(self, name: Literal["inline"]) -> ParserInline: ...
73
+
74
+ @overload
75
+ def __getitem__(self, name: Literal["block"]) -> ParserBlock: ...
76
+
77
+ @overload
78
+ def __getitem__(self, name: Literal["core"]) -> ParserCore: ...
79
+
80
+ @overload
81
+ def __getitem__(self, name: Literal["renderer"]) -> RendererProtocol: ...
82
+
83
+ @overload
84
+ def __getitem__(self, name: str) -> Any: ...
85
+
86
+ def __getitem__(self, name: str) -> Any:
87
+ return {
88
+ "inline": self.inline,
89
+ "block": self.block,
90
+ "core": self.core,
91
+ "renderer": self.renderer,
92
+ }[name]
93
+
94
+ def set(self, options: OptionsType) -> None:
95
+ """Set parser options (in the same format as in constructor).
96
+ Probably, you will never need it, but you can change options after constructor call.
97
+
98
+ __Note:__ To achieve the best possible performance, don't modify a
99
+ `markdown-it` instance options on the fly. If you need multiple configurations
100
+ it's best to create multiple instances and initialize each with separate config.
101
+ """
102
+ self.options = OptionsDict(options)
103
+
104
+ def configure(
105
+ self, presets: str | PresetType, options_update: Mapping[str, Any] | None = None
106
+ ) -> MarkdownIt:
107
+ """Batch load of all options and component settings.
108
+ This is an internal method, and you probably will not need it.
109
+ But if you will - see available presets and data structure
110
+ [here](https://github.com/markdown-it/markdown-it/tree/master/lib/presets)
111
+
112
+ We strongly recommend to use presets instead of direct config loads.
113
+ That will give better compatibility with next versions.
114
+ """
115
+ if isinstance(presets, str):
116
+ if presets not in _PRESETS:
117
+ raise KeyError(f"Wrong `markdown-it` preset '{presets}', check name")
118
+ config = _PRESETS[presets]
119
+ else:
120
+ config = presets
121
+
122
+ if not config:
123
+ raise ValueError("Wrong `markdown-it` config, can't be empty")
124
+
125
+ options = config.get("options", {}) or {}
126
+ if options_update:
127
+ options = {**options, **options_update} # type: ignore
128
+
129
+ self.set(options)
130
+
131
+ if "components" in config:
132
+ for name, component in config["components"].items():
133
+ rules = component.get("rules", None)
134
+ if rules:
135
+ self[name].ruler.enableOnly(rules)
136
+ rules2 = component.get("rules2", None)
137
+ if rules2:
138
+ self[name].ruler2.enableOnly(rules2)
139
+
140
+ return self
141
+
142
+ def get_all_rules(self) -> dict[str, list[str]]:
143
+ """Return the names of all active rules."""
144
+ rules = {
145
+ chain: self[chain].ruler.get_all_rules()
146
+ for chain in ["core", "block", "inline"]
147
+ }
148
+ rules["inline2"] = self.inline.ruler2.get_all_rules()
149
+ return rules
150
+
151
+ def get_active_rules(self) -> dict[str, list[str]]:
152
+ """Return the names of all active rules."""
153
+ rules = {
154
+ chain: self[chain].ruler.get_active_rules()
155
+ for chain in ["core", "block", "inline"]
156
+ }
157
+ rules["inline2"] = self.inline.ruler2.get_active_rules()
158
+ return rules
159
+
160
+ def enable(
161
+ self, names: str | Iterable[str], ignoreInvalid: bool = False
162
+ ) -> MarkdownIt:
163
+ """Enable list or rules. (chainable)
164
+
165
+ :param names: rule name or list of rule names to enable.
166
+ :param ignoreInvalid: set `true` to ignore errors when rule not found.
167
+
168
+ It will automatically find appropriate components,
169
+ containing rules with given names. If rule not found, and `ignoreInvalid`
170
+ not set - throws exception.
171
+
172
+ Example::
173
+
174
+ md = MarkdownIt().enable(['sub', 'sup']).disable('smartquotes')
175
+
176
+ """
177
+ result = []
178
+
179
+ if isinstance(names, str):
180
+ names = [names]
181
+
182
+ for chain in ["core", "block", "inline"]:
183
+ result.extend(self[chain].ruler.enable(names, True))
184
+ result.extend(self.inline.ruler2.enable(names, True))
185
+
186
+ missed = [name for name in names if name not in result]
187
+ if missed and not ignoreInvalid:
188
+ raise ValueError(f"MarkdownIt. Failed to enable unknown rule(s): {missed}")
189
+
190
+ return self
191
+
192
+ def disable(
193
+ self, names: str | Iterable[str], ignoreInvalid: bool = False
194
+ ) -> MarkdownIt:
195
+ """The same as [[MarkdownIt.enable]], but turn specified rules off. (chainable)
196
+
197
+ :param names: rule name or list of rule names to disable.
198
+ :param ignoreInvalid: set `true` to ignore errors when rule not found.
199
+
200
+ """
201
+ result = []
202
+
203
+ if isinstance(names, str):
204
+ names = [names]
205
+
206
+ for chain in ["core", "block", "inline"]:
207
+ result.extend(self[chain].ruler.disable(names, True))
208
+ result.extend(self.inline.ruler2.disable(names, True))
209
+
210
+ missed = [name for name in names if name not in result]
211
+ if missed and not ignoreInvalid:
212
+ raise ValueError(f"MarkdownIt. Failed to disable unknown rule(s): {missed}")
213
+ return self
214
+
215
+ @contextmanager
216
+ def reset_rules(self) -> Generator[None, None, None]:
217
+ """A context manager, that will reset the current enabled rules on exit."""
218
+ chain_rules = self.get_active_rules()
219
+ yield
220
+ for chain, rules in chain_rules.items():
221
+ if chain != "inline2":
222
+ self[chain].ruler.enableOnly(rules)
223
+ self.inline.ruler2.enableOnly(chain_rules["inline2"])
224
+
225
+ def add_render_rule(
226
+ self, name: str, function: Callable[..., Any], fmt: str = "html"
227
+ ) -> None:
228
+ """Add a rule for rendering a particular Token type.
229
+
230
+ Only applied when ``renderer.__output__ == fmt``
231
+ """
232
+ if self.renderer.__output__ == fmt:
233
+ self.renderer.rules[name] = function.__get__(self.renderer) # type: ignore
234
+
235
+ def use(
236
+ self, plugin: Callable[..., None], *params: Any, **options: Any
237
+ ) -> MarkdownIt:
238
+ """Load specified plugin with given params into current parser instance. (chainable)
239
+
240
+ It's just a sugar to call `plugin(md, params)` with curring.
241
+
242
+ Example::
243
+
244
+ def func(tokens, idx):
245
+ tokens[idx].content = tokens[idx].content.replace('foo', 'bar')
246
+ md = MarkdownIt().use(plugin, 'foo_replace', 'text', func)
247
+
248
+ """
249
+ plugin(self, *params, **options)
250
+ return self
251
+
252
+ def parse(self, src: str, env: EnvType | None = None) -> list[Token]:
253
+ """Parse the source string to a token stream
254
+
255
+ :param src: source string
256
+ :param env: environment sandbox
257
+
258
+ Parse input string and return list of block tokens (special token type
259
+ "inline" will contain list of inline tokens).
260
+
261
+ `env` is used to pass data between "distributed" rules and return additional
262
+ metadata like reference info, needed for the renderer. It also can be used to
263
+ inject data in specific cases. Usually, you will be ok to pass `{}`,
264
+ and then pass updated object to renderer.
265
+ """
266
+ env = {} if env is None else env
267
+ if not isinstance(env, MutableMapping):
268
+ raise TypeError(f"Input data should be a MutableMapping, not {type(env)}")
269
+ if not isinstance(src, str):
270
+ raise TypeError(f"Input data should be a string, not {type(src)}")
271
+ state = StateCore(src, self, env)
272
+ self.core.process(state)
273
+ return state.tokens
274
+
275
+ def render(self, src: str, env: EnvType | None = None) -> Any:
276
+ """Render markdown string into html. It does all magic for you :).
277
+
278
+ :param src: source string
279
+ :param env: environment sandbox
280
+ :returns: The output of the loaded renderer
281
+
282
+ `env` can be used to inject additional metadata (`{}` by default).
283
+ But you will not need it with high probability. See also comment
284
+ in [[MarkdownIt.parse]].
285
+ """
286
+ env = {} if env is None else env
287
+ return self.renderer.render(self.parse(src, env), self.options, env)
288
+
289
+ def parseInline(self, src: str, env: EnvType | None = None) -> list[Token]:
290
+ """The same as [[MarkdownIt.parse]] but skip all block rules.
291
+
292
+ :param src: source string
293
+ :param env: environment sandbox
294
+
295
+ It returns the
296
+ block tokens list with the single `inline` element, containing parsed inline
297
+ tokens in `children` property. Also updates `env` object.
298
+ """
299
+ env = {} if env is None else env
300
+ if not isinstance(env, MutableMapping):
301
+ raise TypeError(f"Input data should be an MutableMapping, not {type(env)}")
302
+ if not isinstance(src, str):
303
+ raise TypeError(f"Input data should be a string, not {type(src)}")
304
+ state = StateCore(src, self, env)
305
+ state.inlineMode = True
306
+ self.core.process(state)
307
+ return state.tokens
308
+
309
+ def renderInline(self, src: str, env: EnvType | None = None) -> Any:
310
+ """Similar to [[MarkdownIt.render]] but for single paragraph content.
311
+
312
+ :param src: source string
313
+ :param env: environment sandbox
314
+
315
+ Similar to [[MarkdownIt.render]] but for single paragraph content. Result
316
+ will NOT be wrapped into `<p>` tags.
317
+ """
318
+ env = {} if env is None else env
319
+ return self.renderer.render(self.parseInline(src, env), self.options, env)
320
+
321
+ # link methods
322
+
323
+ def validateLink(self, url: str) -> bool:
324
+ """Validate if the URL link is allowed in output.
325
+
326
+ This validator can prohibit more than really needed to prevent XSS.
327
+ It's a tradeoff to keep code simple and to be secure by default.
328
+
329
+ Note: the url should be normalized at this point, and existing entities decoded.
330
+ """
331
+ return normalize_url.validateLink(url)
332
+
333
+ def normalizeLink(self, url: str) -> str:
334
+ """Normalize destination URLs in links
335
+
336
+ ::
337
+
338
+ [label]: destination 'title'
339
+ ^^^^^^^^^^^
340
+ """
341
+ return normalize_url.normalizeLink(url)
342
+
343
+ def normalizeLinkText(self, link: str) -> str:
344
+ """Normalize autolink content
345
+
346
+ ::
347
+
348
+ <destination>
349
+ ~~~~~~~~~~~
350
+ """
351
+ return normalize_url.normalizeLinkText(link)
venv/lib/python3.12/site-packages/markdown_it/parser_block.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Block-level tokenizer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ import logging
7
+ from typing import TYPE_CHECKING
8
+
9
+ from . import rules_block
10
+ from .ruler import Ruler
11
+ from .rules_block.state_block import StateBlock
12
+ from .token import Token
13
+ from .utils import EnvType
14
+
15
+ if TYPE_CHECKING:
16
+ from markdown_it import MarkdownIt
17
+
18
+ LOGGER = logging.getLogger(__name__)
19
+
20
+
21
+ RuleFuncBlockType = Callable[[StateBlock, int, int, bool], bool]
22
+ """(state: StateBlock, startLine: int, endLine: int, silent: bool) -> matched: bool)
23
+
24
+ `silent` disables token generation, useful for lookahead.
25
+ """
26
+
27
+ _rules: list[tuple[str, RuleFuncBlockType, list[str]]] = [
28
+ # First 2 params - rule name & source. Secondary array - list of rules,
29
+ # which can be terminated by this one.
30
+ ("table", rules_block.table, ["paragraph", "reference"]),
31
+ ("code", rules_block.code, []),
32
+ ("fence", rules_block.fence, ["paragraph", "reference", "blockquote", "list"]),
33
+ (
34
+ "blockquote",
35
+ rules_block.blockquote,
36
+ ["paragraph", "reference", "blockquote", "list"],
37
+ ),
38
+ ("hr", rules_block.hr, ["paragraph", "reference", "blockquote", "list"]),
39
+ ("list", rules_block.list_block, ["paragraph", "reference", "blockquote"]),
40
+ ("reference", rules_block.reference, []),
41
+ ("html_block", rules_block.html_block, ["paragraph", "reference", "blockquote"]),
42
+ ("heading", rules_block.heading, ["paragraph", "reference", "blockquote"]),
43
+ ("lheading", rules_block.lheading, []),
44
+ ("paragraph", rules_block.paragraph, []),
45
+ ]
46
+
47
+
48
+ class ParserBlock:
49
+ """
50
+ ParserBlock#ruler -> Ruler
51
+
52
+ [[Ruler]] instance. Keep configuration of block rules.
53
+ """
54
+
55
+ def __init__(self) -> None:
56
+ self.ruler = Ruler[RuleFuncBlockType]()
57
+ for name, rule, alt in _rules:
58
+ self.ruler.push(name, rule, {"alt": alt})
59
+
60
+ def tokenize(self, state: StateBlock, startLine: int, endLine: int) -> None:
61
+ """Generate tokens for input range."""
62
+ rules = self.ruler.getRules("")
63
+ line = startLine
64
+ maxNesting = state.md.options.maxNesting
65
+ hasEmptyLines = False
66
+
67
+ while line < endLine:
68
+ state.line = line = state.skipEmptyLines(line)
69
+ if line >= endLine:
70
+ break
71
+ if state.sCount[line] < state.blkIndent:
72
+ # Termination condition for nested calls.
73
+ # Nested calls currently used for blockquotes & lists
74
+ break
75
+ if state.level >= maxNesting:
76
+ # If nesting level exceeded - skip tail to the end.
77
+ # That's not ordinary situation and we should not care about content.
78
+ state.line = endLine
79
+ break
80
+
81
+ # Try all possible rules.
82
+ # On success, rule should:
83
+ # - update `state.line`
84
+ # - update `state.tokens`
85
+ # - return True
86
+ for rule in rules:
87
+ if rule(state, line, endLine, False):
88
+ break
89
+
90
+ # set state.tight if we had an empty line before current tag
91
+ # i.e. latest empty line should not count
92
+ state.tight = not hasEmptyLines
93
+
94
+ line = state.line
95
+
96
+ # paragraph might "eat" one newline after it in nested lists
97
+ if (line - 1) < endLine and state.isEmpty(line - 1):
98
+ hasEmptyLines = True
99
+
100
+ if line < endLine and state.isEmpty(line):
101
+ hasEmptyLines = True
102
+ line += 1
103
+ state.line = line
104
+
105
+ def parse(
106
+ self, src: str, md: MarkdownIt, env: EnvType, outTokens: list[Token]
107
+ ) -> list[Token] | None:
108
+ """Process input string and push block tokens into `outTokens`."""
109
+ if not src:
110
+ return None
111
+ state = StateBlock(src, md, env, outTokens)
112
+ self.tokenize(state, state.line, state.lineMax)
113
+ return state.tokens
venv/lib/python3.12/site-packages/markdown_it/parser_core.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ * class Core
3
+ *
4
+ * Top-level rules executor. Glues block/inline parsers and does intermediate
5
+ * transformations.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Callable
11
+
12
+ from .ruler import Ruler
13
+ from .rules_core import (
14
+ block,
15
+ inline,
16
+ linkify,
17
+ normalize,
18
+ replace,
19
+ smartquotes,
20
+ text_join,
21
+ )
22
+ from .rules_core.state_core import StateCore
23
+
24
+ RuleFuncCoreType = Callable[[StateCore], None]
25
+
26
+ _rules: list[tuple[str, RuleFuncCoreType]] = [
27
+ ("normalize", normalize),
28
+ ("block", block),
29
+ ("inline", inline),
30
+ ("linkify", linkify),
31
+ ("replacements", replace),
32
+ ("smartquotes", smartquotes),
33
+ ("text_join", text_join),
34
+ ]
35
+
36
+
37
+ class ParserCore:
38
+ def __init__(self) -> None:
39
+ self.ruler = Ruler[RuleFuncCoreType]()
40
+ for name, rule in _rules:
41
+ self.ruler.push(name, rule)
42
+
43
+ def process(self, state: StateCore) -> None:
44
+ """Executes core chain rules."""
45
+ for rule in self.ruler.getRules(""):
46
+ rule(state)
venv/lib/python3.12/site-packages/markdown_it/parser_inline.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tokenizes paragraph content."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ import functools
7
+ import re
8
+ from typing import TYPE_CHECKING
9
+
10
+ from . import rules_inline
11
+ from .ruler import Ruler
12
+ from .rules_inline.state_inline import StateInline
13
+ from .token import Token
14
+ from .utils import EnvType
15
+
16
+ if TYPE_CHECKING:
17
+ from markdown_it import MarkdownIt
18
+
19
+
20
+ # Default set of characters that terminate a text token and allow inline rules to fire.
21
+ # '{}$%@~+=:' reserved for extensions.
22
+ # Note: Don't confuse with "Markdown ASCII Punctuation" chars.
23
+ # http://spec.commonmark.org/0.15/#ascii-punctuation-character
24
+ _DEFAULT_TERMINATORS: frozenset[str] = frozenset(
25
+ {
26
+ "\n",
27
+ "!",
28
+ "#",
29
+ "$",
30
+ "%",
31
+ "&",
32
+ "*",
33
+ "+",
34
+ "-",
35
+ ":",
36
+ "<",
37
+ "=",
38
+ ">",
39
+ "@",
40
+ "[",
41
+ "\\",
42
+ "]",
43
+ "^",
44
+ "_",
45
+ "`",
46
+ "{",
47
+ "}",
48
+ "~",
49
+ }
50
+ )
51
+
52
+
53
+ # Lazily compiled regex for the default terminator set. The @cache ensures it is
54
+ # compiled at most once (on first ParserInline instantiation) and shared across all
55
+ # instances that have not added extra chars, keeping __init__ cost near zero.
56
+ @functools.cache
57
+ def _default_terminator_re() -> re.Pattern[str]:
58
+ return re.compile("[" + re.escape("".join(_DEFAULT_TERMINATORS)) + "]")
59
+
60
+
61
+ # Parser rules
62
+ RuleFuncInlineType = Callable[[StateInline, bool], bool]
63
+ """(state: StateInline, silent: bool) -> matched: bool)
64
+
65
+ `silent` disables token generation, useful for lookahead.
66
+ """
67
+ _rules: list[tuple[str, RuleFuncInlineType]] = [
68
+ ("text", rules_inline.text),
69
+ ("linkify", rules_inline.linkify),
70
+ ("newline", rules_inline.newline),
71
+ ("escape", rules_inline.escape),
72
+ ("backticks", rules_inline.backtick),
73
+ ("strikethrough", rules_inline.strikethrough.tokenize),
74
+ ("emphasis", rules_inline.emphasis.tokenize),
75
+ ("link", rules_inline.link),
76
+ ("image", rules_inline.image),
77
+ ("autolink", rules_inline.autolink),
78
+ ("html_inline", rules_inline.html_inline),
79
+ ("entity", rules_inline.entity),
80
+ ]
81
+
82
+ # Note `rule2` ruleset was created specifically for emphasis/strikethrough
83
+ # post-processing and may be changed in the future.
84
+ #
85
+ # Don't use this for anything except pairs (plugins working with `balance_pairs`).
86
+ #
87
+ RuleFuncInline2Type = Callable[[StateInline], None]
88
+ _rules2: list[tuple[str, RuleFuncInline2Type]] = [
89
+ ("balance_pairs", rules_inline.link_pairs),
90
+ ("strikethrough", rules_inline.strikethrough.postProcess),
91
+ ("emphasis", rules_inline.emphasis.postProcess),
92
+ # rules for pairs separate '**' into its own text tokens, which may be left unused,
93
+ # rule below merges unused segments back with the rest of the text
94
+ ("fragments_join", rules_inline.fragments_join),
95
+ ]
96
+
97
+
98
+ class ParserInline:
99
+ def __init__(self) -> None:
100
+ self.ruler = Ruler[RuleFuncInlineType]()
101
+ for name, rule in _rules:
102
+ self.ruler.push(name, rule)
103
+ # Second ruler used for post-processing (e.g. in emphasis-like rules)
104
+ self.ruler2 = Ruler[RuleFuncInline2Type]()
105
+ for name, rule2 in _rules2:
106
+ self.ruler2.push(name, rule2)
107
+ # Characters that stop the text rule, allowing other inline rules to fire.
108
+ # _extra_terminator_chars is only allocated when add_terminator_char() is called
109
+ # with a char outside the defaults, keeping __init__ allocation-free.
110
+ self._extra_terminator_chars: set[str] = set()
111
+ # Pre-compiled regex shared with all default instances (no copy in the common path).
112
+ self.terminator_re: re.Pattern[str] = _default_terminator_re()
113
+
114
+ def add_terminator_char(self, ch: str) -> None:
115
+ """Register a character that stops the ``text`` rule, allowing inline rules to fire.
116
+
117
+ This lets plugins declare which characters their inline rules react to,
118
+ mirroring the ``MARKER`` mechanism in the Rust markdown-it implementation.
119
+
120
+ :param ch: A single character to add to the terminator set.
121
+ """
122
+ if ch not in _DEFAULT_TERMINATORS and ch not in self._extra_terminator_chars:
123
+ self._extra_terminator_chars.add(ch)
124
+ self.terminator_re = re.compile(
125
+ "["
126
+ + re.escape(
127
+ "".join(_DEFAULT_TERMINATORS | self._extra_terminator_chars)
128
+ )
129
+ + "]"
130
+ )
131
+
132
+ def skipToken(self, state: StateInline) -> None:
133
+ """Skip single token by running all rules in validation mode;
134
+ returns `True` if any rule reported success
135
+ """
136
+ ok = False
137
+ pos = state.pos
138
+ rules = self.ruler.getRules("")
139
+ maxNesting = state.md.options["maxNesting"]
140
+ cache = state.cache
141
+
142
+ if pos in cache:
143
+ state.pos = cache[pos]
144
+ return
145
+
146
+ if state.level < maxNesting:
147
+ for rule in rules:
148
+ # Increment state.level and decrement it later to limit recursion.
149
+ # It's harmless to do here, because no tokens are created.
150
+ # But ideally, we'd need a separate private state variable for this purpose.
151
+ state.level += 1
152
+ ok = rule(state, True)
153
+ state.level -= 1
154
+ if ok:
155
+ break
156
+ else:
157
+ # Too much nesting, just skip until the end of the paragraph.
158
+ #
159
+ # NOTE: this will cause links to behave incorrectly in the following case,
160
+ # when an amount of `[` is exactly equal to `maxNesting + 1`:
161
+ #
162
+ # [[[[[[[[[[[[[[[[[[[[[foo]()
163
+ #
164
+ # TODO: remove this workaround when CM standard will allow nested links
165
+ # (we can replace it by preventing links from being parsed in
166
+ # validation mode)
167
+ #
168
+ state.pos = state.posMax
169
+
170
+ if not ok:
171
+ state.pos += 1
172
+ cache[pos] = state.pos
173
+
174
+ def tokenize(self, state: StateInline) -> None:
175
+ """Generate tokens for input range."""
176
+ ok = False
177
+ rules = self.ruler.getRules("")
178
+ end = state.posMax
179
+ maxNesting = state.md.options["maxNesting"]
180
+
181
+ while state.pos < end:
182
+ # Try all possible rules.
183
+ # On success, rule should:
184
+ #
185
+ # - update `state.pos`
186
+ # - update `state.tokens`
187
+ # - return true
188
+
189
+ if state.level < maxNesting:
190
+ for rule in rules:
191
+ ok = rule(state, False)
192
+ if ok:
193
+ break
194
+
195
+ if ok:
196
+ if state.pos >= end:
197
+ break
198
+ continue
199
+
200
+ state.pending += state.src[state.pos]
201
+ state.pos += 1
202
+
203
+ if state.pending:
204
+ state.pushPending()
205
+
206
+ def parse(
207
+ self, src: str, md: MarkdownIt, env: EnvType, tokens: list[Token]
208
+ ) -> list[Token]:
209
+ """Process input string and push inline tokens into `tokens`"""
210
+ state = StateInline(src, md, env, tokens)
211
+ self.tokenize(state)
212
+ rules2 = self.ruler2.getRules("")
213
+ for rule in rules2:
214
+ rule(state)
215
+ return state.tokens
venv/lib/python3.12/site-packages/markdown_it/port.yaml ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ - package: markdown-it/markdown-it
2
+ version: 14.1.0
3
+ commit: 0fe7ccb4b7f30236fb05f623be6924961d296d3d
4
+ date: Mar 19, 2024
5
+ notes:
6
+ - Rename variables that use python built-in names, e.g.
7
+ - `max` -> `maximum`
8
+ - `len` -> `length`
9
+ - `str` -> `string`
10
+ - |
11
+ Convert JS `for` loops to `while` loops
12
+ this is generally the main difference between the codes,
13
+ because in python you can't do e.g. `for {i=1;i<x;i++} {}`
14
+ - |
15
+ `env` is a common Python dictionary, and so does not have attribute access to keys,
16
+ as with JavaScript dictionaries.
17
+ `options` have attribute access only to core markdownit configuration options
18
+ - |
19
+ `Token.attrs` is a dictionary, instead of a list of lists.
20
+ Upstream the list format is only used to guarantee order: https://github.com/markdown-it/markdown-it/issues/142,
21
+ but in Python 3.7+ order of dictionaries is guaranteed.
22
+ One should anyhow use the `attrGet`, `attrSet`, `attrPush` and `attrJoin` methods
23
+ to manipulate `Token.attrs`, which have an identical signature to those upstream.
24
+ - Use python version of `charCodeAt`
25
+ - |
26
+ Use `str` units instead of `int`s to represent Unicode codepoints.
27
+ This provides a significant performance boost
28
+ - |
29
+ In markdown_it/rules_block/reference.py,
30
+ record line range in state.env["references"] and add state.env["duplicate_refs"]
31
+ This is to allow renderers to report on issues regarding references
32
+ - |
33
+ The `MarkdownIt.__init__` signature is slightly different for updating options,
34
+ since you must always specify the config first, e.g.
35
+ use `MarkdownIt("commonmark", {"html": False})` instead of `MarkdownIt({"html": False})`
36
+ - The default configuration preset for `MarkdownIt` is "commonmark" not "default"
37
+ - Allow custom renderer to be passed to `MarkdownIt`
38
+ - |
39
+ change render method signatures
40
+ `func(tokens, idx, options, env, slf)` to
41
+ `func(self, tokens, idx, options, env)`
42
+ - |
43
+ Extensions add render methods by format
44
+ `MarkdownIt.add_render_rule(name, function, fmt="html")`,
45
+ rather than `MarkdownIt.renderer.rules[name] = function`
46
+ and renderers should declare a class property `__output__ = "html"`.
47
+ This allows for extensibility to more than just HTML renderers
48
+ - inline tokens in tables are assigned a map (this is helpful for propagation to children)
venv/lib/python3.12/site-packages/markdown_it/presets/__init__.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __all__ = ("commonmark", "default", "gfm_like", "gfm_like2", "js_default", "zero")
2
+
3
+ from ..utils import PresetType
4
+ from . import commonmark, default, zero
5
+
6
+ js_default = default
7
+
8
+
9
+ class gfm_like: # noqa: N801
10
+ """GitHub Flavoured Markdown (GFM) like.
11
+
12
+ This adds the linkify, table and strikethrough components to CommmonMark.
13
+
14
+ Note, it lacks task-list items and raw HTML filtering,
15
+ to meet the the full GFM specification
16
+ (see https://github.github.com/gfm/#autolinks-extension-).
17
+ """
18
+
19
+ @staticmethod
20
+ def make() -> PresetType:
21
+ config = commonmark.make()
22
+ config["components"]["core"]["rules"].append("linkify")
23
+ config["components"]["block"]["rules"].append("table")
24
+ config["components"]["inline"]["rules"].extend(["strikethrough", "linkify"])
25
+ config["components"]["inline"]["rules2"].append("strikethrough")
26
+ config["options"]["linkify"] = True
27
+ config["options"]["html"] = True
28
+ return config
29
+
30
+
31
+ class gfm_like2: # noqa: N801
32
+ """GitHub Flavoured Markdown (GFM) like, extended.
33
+
34
+ Builds on ``gfm-like`` and additionally enables:
35
+
36
+ - Task lists (``- [x] done``)
37
+ - Alerts (``> [!NOTE]``)
38
+ - Single-tilde strikethrough (``~text~`` in addition to ``~~text~~``)
39
+ """
40
+
41
+ @staticmethod
42
+ def make() -> PresetType:
43
+ config = gfm_like.make()
44
+ config["options"]["tasklists"] = True
45
+ config["options"]["tasklists_editable"] = False
46
+ config["options"]["alerts"] = True
47
+ config["options"]["strikethrough_single_tilde"] = True
48
+ return config
venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (2.4 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/commonmark.cpython-312.pyc ADDED
Binary file (1.14 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/default.cpython-312.pyc ADDED
Binary file (657 Bytes). View file
 
venv/lib/python3.12/site-packages/markdown_it/presets/__pycache__/zero.cpython-312.pyc ADDED
Binary file (918 Bytes). View file
 
venv/lib/python3.12/site-packages/markdown_it/presets/commonmark.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Commonmark default options.
2
+
3
+ This differs to presets.default,
4
+ primarily in that it allows HTML and does not enable components:
5
+
6
+ - block: table
7
+ - inline: strikethrough
8
+ """
9
+
10
+ from ..utils import PresetType
11
+
12
+
13
+ def make() -> PresetType:
14
+ return {
15
+ "options": {
16
+ "maxNesting": 20, # Internal protection, recursion limit
17
+ "html": True, # Enable HTML tags in source,
18
+ # this is just a shorthand for .enable(["html_inline", "html_block"])
19
+ # used by the linkify rule:
20
+ "linkify": False, # autoconvert URL-like texts to links
21
+ # used by the replacements and smartquotes rules
22
+ # Enable some language-neutral replacements + quotes beautification
23
+ "typographer": False,
24
+ # used by the smartquotes rule:
25
+ # Double + single quotes replacement pairs, when typographer enabled,
26
+ # and smartquotes on. Could be either a String or an Array.
27
+ #
28
+ # For example, you can use '«»„“' for Russian, '„“‚‘' for German,
29
+ # and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
30
+ "quotes": "\u201c\u201d\u2018\u2019", # /* “”‘’ */
31
+ # Renderer specific; these options are used directly in the HTML renderer
32
+ "xhtmlOut": True, # Use '/' to close single tags (<br />)
33
+ "breaks": False, # Convert '\n' in paragraphs into <br>
34
+ "langPrefix": "language-", # CSS language prefix for fenced blocks
35
+ # Highlighter function. Should return escaped HTML,
36
+ # or '' if the source string is not changed and should be escaped externally.
37
+ # If result starts with <pre... internal wrapper is skipped.
38
+ #
39
+ # function (/*str, lang, attrs*/) { return ''; }
40
+ #
41
+ "highlight": None,
42
+ },
43
+ "components": {
44
+ "core": {"rules": ["normalize", "block", "inline", "text_join"]},
45
+ "block": {
46
+ "rules": [
47
+ "blockquote",
48
+ "code",
49
+ "fence",
50
+ "heading",
51
+ "hr",
52
+ "html_block",
53
+ "lheading",
54
+ "list",
55
+ "reference",
56
+ "paragraph",
57
+ ]
58
+ },
59
+ "inline": {
60
+ "rules": [
61
+ "autolink",
62
+ "backticks",
63
+ "emphasis",
64
+ "entity",
65
+ "escape",
66
+ "html_inline",
67
+ "image",
68
+ "link",
69
+ "newline",
70
+ "text",
71
+ ],
72
+ "rules2": ["balance_pairs", "emphasis", "fragments_join"],
73
+ },
74
+ },
75
+ }
venv/lib/python3.12/site-packages/markdown_it/presets/default.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """markdown-it default options."""
2
+
3
+ from ..utils import PresetType
4
+
5
+
6
+ def make() -> PresetType:
7
+ return {
8
+ "options": {
9
+ "maxNesting": 100, # Internal protection, recursion limit
10
+ "html": False, # Enable HTML tags in source
11
+ # this is just a shorthand for .disable(["html_inline", "html_block"])
12
+ # used by the linkify rule:
13
+ "linkify": False, # autoconvert URL-like texts to links
14
+ # used by the replacements and smartquotes rules:
15
+ # Enable some language-neutral replacements + quotes beautification
16
+ "typographer": False,
17
+ # used by the smartquotes rule:
18
+ # Double + single quotes replacement pairs, when typographer enabled,
19
+ # and smartquotes on. Could be either a String or an Array.
20
+ # For example, you can use '«»„“' for Russian, '„“‚‘' for German,
21
+ # and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
22
+ "quotes": "\u201c\u201d\u2018\u2019", # /* “”‘’ */
23
+ # Renderer specific; these options are used directly in the HTML renderer
24
+ "xhtmlOut": False, # Use '/' to close single tags (<br />)
25
+ "breaks": False, # Convert '\n' in paragraphs into <br>
26
+ "langPrefix": "language-", # CSS language prefix for fenced blocks
27
+ # Highlighter function. Should return escaped HTML,
28
+ # or '' if the source string is not changed and should be escaped externally.
29
+ # If result starts with <pre... internal wrapper is skipped.
30
+ #
31
+ # function (/*str, lang, attrs*/) { return ''; }
32
+ #
33
+ "highlight": None,
34
+ },
35
+ "components": {"core": {}, "block": {}, "inline": {}},
36
+ }
venv/lib/python3.12/site-packages/markdown_it/presets/zero.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ "Zero" preset, with nothing enabled. Useful for manual configuring of simple
3
+ modes. For example, to parse bold/italic only.
4
+ """
5
+
6
+ from ..utils import PresetType
7
+
8
+
9
+ def make() -> PresetType:
10
+ return {
11
+ "options": {
12
+ "maxNesting": 20, # Internal protection, recursion limit
13
+ "html": False, # Enable HTML tags in source
14
+ # this is just a shorthand for .disable(["html_inline", "html_block"])
15
+ # used by the linkify rule:
16
+ "linkify": False, # autoconvert URL-like texts to links
17
+ # used by the replacements and smartquotes rules:
18
+ # Enable some language-neutral replacements + quotes beautification
19
+ "typographer": False,
20
+ # used by the smartquotes rule:
21
+ # Double + single quotes replacement pairs, when typographer enabled,
22
+ # and smartquotes on. Could be either a String or an Array.
23
+ # For example, you can use '«»„“' for Russian, '„“‚‘' for German,
24
+ # and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
25
+ "quotes": "\u201c\u201d\u2018\u2019", # /* “”‘’ */
26
+ # Renderer specific; these options are used directly in the HTML renderer
27
+ "xhtmlOut": False, # Use '/' to close single tags (<br />)
28
+ "breaks": False, # Convert '\n' in paragraphs into <br>
29
+ "langPrefix": "language-", # CSS language prefix for fenced blocks
30
+ # Highlighter function. Should return escaped HTML,
31
+ # or '' if the source string is not changed and should be escaped externally.
32
+ # If result starts with <pre... internal wrapper is skipped.
33
+ # function (/*str, lang, attrs*/) { return ''; }
34
+ "highlight": None,
35
+ },
36
+ "components": {
37
+ "core": {"rules": ["normalize", "block", "inline", "text_join"]},
38
+ "block": {"rules": ["paragraph"]},
39
+ "inline": {
40
+ "rules": ["text"],
41
+ "rules2": ["balance_pairs", "fragments_join"],
42
+ },
43
+ },
44
+ }
venv/lib/python3.12/site-packages/markdown_it/py.typed ADDED
@@ -0,0 +1 @@
 
 
1
+ # Marker file for PEP 561
venv/lib/python3.12/site-packages/markdown_it/renderer.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ class Renderer
3
+
4
+ Generates HTML from parsed token stream. Each instance has independent
5
+ copy of rules. Those can be rewritten with ease. Also, you can add new
6
+ rules if you create plugin and adds new token types.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Sequence
12
+ import inspect
13
+ from typing import Any, ClassVar, Protocol
14
+
15
+ from .common.utils import escapeHtml, unescapeAll
16
+ from .token import Token
17
+ from .utils import EnvType, OptionsDict
18
+
19
+
20
+ class RendererProtocol(Protocol):
21
+ __output__: ClassVar[str]
22
+
23
+ def render(
24
+ self, tokens: Sequence[Token], options: OptionsDict, env: EnvType
25
+ ) -> Any: ...
26
+
27
+
28
+ class RendererHTML(RendererProtocol):
29
+ """Contains render rules for tokens. Can be updated and extended.
30
+
31
+ Example:
32
+
33
+ Each rule is called as independent static function with fixed signature:
34
+
35
+ ::
36
+
37
+ class Renderer:
38
+ def token_type_name(self, tokens, idx, options, env) {
39
+ # ...
40
+ return renderedHTML
41
+
42
+ ::
43
+
44
+ class CustomRenderer(RendererHTML):
45
+ def strong_open(self, tokens, idx, options, env):
46
+ return '<b>'
47
+ def strong_close(self, tokens, idx, options, env):
48
+ return '</b>'
49
+
50
+ md = MarkdownIt(renderer_cls=CustomRenderer)
51
+
52
+ result = md.render(...)
53
+
54
+ See https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.js
55
+ for more details and examples.
56
+ """
57
+
58
+ __output__ = "html"
59
+
60
+ def __init__(self, parser: Any = None):
61
+ self.rules = {
62
+ k: v
63
+ for k, v in inspect.getmembers(self, predicate=inspect.ismethod)
64
+ if not (k.startswith("render") or k.startswith("_"))
65
+ }
66
+
67
+ def render(
68
+ self, tokens: Sequence[Token], options: OptionsDict, env: EnvType
69
+ ) -> str:
70
+ """Takes token stream and generates HTML.
71
+
72
+ :param tokens: list on block tokens to render
73
+ :param options: params of parser instance
74
+ :param env: additional data from parsed input
75
+
76
+ """
77
+ result = ""
78
+
79
+ for i, token in enumerate(tokens):
80
+ if token.type == "inline":
81
+ if token.children:
82
+ result += self.renderInline(token.children, options, env)
83
+ elif token.type in self.rules:
84
+ result += self.rules[token.type](tokens, i, options, env)
85
+ else:
86
+ result += self.renderToken(tokens, i, options, env)
87
+
88
+ return result
89
+
90
+ def renderInline(
91
+ self, tokens: Sequence[Token], options: OptionsDict, env: EnvType
92
+ ) -> str:
93
+ """The same as ``render``, but for single token of `inline` type.
94
+
95
+ :param tokens: list on block tokens to render
96
+ :param options: params of parser instance
97
+ :param env: additional data from parsed input (references, for example)
98
+ """
99
+ result = ""
100
+
101
+ for i, token in enumerate(tokens):
102
+ if token.type in self.rules:
103
+ result += self.rules[token.type](tokens, i, options, env)
104
+ else:
105
+ result += self.renderToken(tokens, i, options, env)
106
+
107
+ return result
108
+
109
+ def renderToken(
110
+ self,
111
+ tokens: Sequence[Token],
112
+ idx: int,
113
+ options: OptionsDict,
114
+ env: EnvType,
115
+ ) -> str:
116
+ """Default token renderer.
117
+
118
+ Can be overridden by custom function
119
+
120
+ :param idx: token index to render
121
+ :param options: params of parser instance
122
+ """
123
+ result = ""
124
+ needLf = False
125
+ token = tokens[idx]
126
+
127
+ # Tight list paragraphs
128
+ if token.hidden:
129
+ return ""
130
+
131
+ # Insert a newline between hidden paragraph and subsequent opening
132
+ # block-level tag.
133
+ #
134
+ # For example, here we should insert a newline before blockquote:
135
+ # - a
136
+ # >
137
+ #
138
+ if token.block and token.nesting != -1 and idx and tokens[idx - 1].hidden:
139
+ result += "\n"
140
+
141
+ # Add token name, e.g. `<img`
142
+ result += ("</" if token.nesting == -1 else "<") + token.tag
143
+
144
+ # Encode attributes, e.g. `<img src="foo"`
145
+ result += self.renderAttrs(token)
146
+
147
+ # Add a slash for self-closing tags, e.g. `<img src="foo" /`
148
+ if token.nesting == 0 and options["xhtmlOut"]:
149
+ result += " /"
150
+
151
+ # Check if we need to add a newline after this tag
152
+ if token.block:
153
+ needLf = True
154
+
155
+ if token.nesting == 1 and (idx + 1 < len(tokens)):
156
+ nextToken = tokens[idx + 1]
157
+
158
+ if nextToken.type == "inline" or nextToken.hidden:
159
+ # Block-level tag containing an inline tag.
160
+ #
161
+ needLf = False
162
+
163
+ elif nextToken.nesting == -1 and nextToken.tag == token.tag:
164
+ # Opening tag + closing tag of the same type. E.g. `<li></li>`.
165
+ #
166
+ needLf = False
167
+
168
+ result += ">\n" if needLf else ">"
169
+
170
+ return result
171
+
172
+ @staticmethod
173
+ def renderAttrs(token: Token) -> str:
174
+ """Render token attributes to string."""
175
+ result = ""
176
+
177
+ for key, value in token.attrItems():
178
+ result += " " + escapeHtml(key) + '="' + escapeHtml(str(value)) + '"'
179
+
180
+ return result
181
+
182
+ def renderInlineAsText(
183
+ self,
184
+ tokens: Sequence[Token] | None,
185
+ options: OptionsDict,
186
+ env: EnvType,
187
+ ) -> str:
188
+ """Special kludge for image `alt` attributes to conform CommonMark spec.
189
+
190
+ Don't try to use it! Spec requires to show `alt` content with stripped markup,
191
+ instead of simple escaping.
192
+
193
+ :param tokens: list on block tokens to render
194
+ :param options: params of parser instance
195
+ :param env: additional data from parsed input
196
+ """
197
+ result = ""
198
+
199
+ for token in tokens or []:
200
+ if token.type == "text":
201
+ result += token.content
202
+ elif token.type == "image":
203
+ if token.children:
204
+ result += self.renderInlineAsText(token.children, options, env)
205
+ elif token.type == "softbreak":
206
+ result += "\n"
207
+
208
+ return result
209
+
210
+ ###################################################
211
+
212
+ def list_item_open(
213
+ self,
214
+ tokens: Sequence[Token],
215
+ idx: int,
216
+ options: OptionsDict,
217
+ env: EnvType,
218
+ ) -> str:
219
+ token = tokens[idx]
220
+ result = self.renderToken(tokens, idx, options, env)
221
+ if token.meta and "checked" in token.meta:
222
+ checked_attr = ' checked=""' if token.meta["checked"] else ""
223
+ disabled_attr = (
224
+ "" if options.get("tasklists_editable", False) else ' disabled=""'
225
+ )
226
+ result += (
227
+ '<input class="task-list-item-checkbox"'
228
+ f'{disabled_attr} type="checkbox"{checked_attr}> '
229
+ )
230
+ return result
231
+
232
+ def code_inline(
233
+ self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType
234
+ ) -> str:
235
+ token = tokens[idx]
236
+ return (
237
+ "<code"
238
+ + self.renderAttrs(token)
239
+ + ">"
240
+ + escapeHtml(tokens[idx].content)
241
+ + "</code>"
242
+ )
243
+
244
+ def code_block(
245
+ self,
246
+ tokens: Sequence[Token],
247
+ idx: int,
248
+ options: OptionsDict,
249
+ env: EnvType,
250
+ ) -> str:
251
+ token = tokens[idx]
252
+
253
+ return (
254
+ "<pre"
255
+ + self.renderAttrs(token)
256
+ + "><code>"
257
+ + escapeHtml(tokens[idx].content)
258
+ + "</code></pre>\n"
259
+ )
260
+
261
+ def fence(
262
+ self,
263
+ tokens: Sequence[Token],
264
+ idx: int,
265
+ options: OptionsDict,
266
+ env: EnvType,
267
+ ) -> str:
268
+ token = tokens[idx]
269
+ info = unescapeAll(token.info).strip() if token.info else ""
270
+ langName = ""
271
+ langAttrs = ""
272
+
273
+ if info:
274
+ arr = info.split(maxsplit=1)
275
+ langName = arr[0]
276
+ if len(arr) == 2:
277
+ langAttrs = arr[1]
278
+
279
+ if options.highlight:
280
+ highlighted = options.highlight(
281
+ token.content, langName, langAttrs
282
+ ) or escapeHtml(token.content)
283
+ else:
284
+ highlighted = escapeHtml(token.content)
285
+
286
+ if highlighted.startswith("<pre"):
287
+ return highlighted + "\n"
288
+
289
+ # If language exists, inject class gently, without modifying original token.
290
+ # May be, one day we will add .deepClone() for token and simplify this part, but
291
+ # now we prefer to keep things local.
292
+ if info:
293
+ # Fake token just to render attributes
294
+ tmpToken = Token(type="", tag="", nesting=0, attrs=token.attrs.copy())
295
+ tmpToken.attrJoin("class", options.langPrefix + langName)
296
+
297
+ return (
298
+ "<pre><code"
299
+ + self.renderAttrs(tmpToken)
300
+ + ">"
301
+ + highlighted
302
+ + "</code></pre>\n"
303
+ )
304
+
305
+ return (
306
+ "<pre><code"
307
+ + self.renderAttrs(token)
308
+ + ">"
309
+ + highlighted
310
+ + "</code></pre>\n"
311
+ )
312
+
313
+ def image(
314
+ self,
315
+ tokens: Sequence[Token],
316
+ idx: int,
317
+ options: OptionsDict,
318
+ env: EnvType,
319
+ ) -> str:
320
+ token = tokens[idx]
321
+
322
+ # "alt" attr MUST be set, even if empty. Because it's mandatory and
323
+ # should be placed on proper position for tests.
324
+ if token.children:
325
+ token.attrSet("alt", self.renderInlineAsText(token.children, options, env))
326
+ else:
327
+ token.attrSet("alt", "")
328
+
329
+ return self.renderToken(tokens, idx, options, env)
330
+
331
+ def hardbreak(
332
+ self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType
333
+ ) -> str:
334
+ return "<br />\n" if options.xhtmlOut else "<br>\n"
335
+
336
+ def softbreak(
337
+ self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType
338
+ ) -> str:
339
+ return (
340
+ ("<br />\n" if options.xhtmlOut else "<br>\n") if options.breaks else "\n"
341
+ )
342
+
343
+ def text(
344
+ self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType
345
+ ) -> str:
346
+ return escapeHtml(tokens[idx].content)
347
+
348
+ def html_block(
349
+ self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType
350
+ ) -> str:
351
+ return tokens[idx].content
352
+
353
+ def html_inline(
354
+ self, tokens: Sequence[Token], idx: int, options: OptionsDict, env: EnvType
355
+ ) -> str:
356
+ return tokens[idx].content
venv/lib/python3.12/site-packages/markdown_it/ruler.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ class Ruler
3
+
4
+ Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and
5
+ [[MarkdownIt#inline]] to manage sequences of functions (rules):
6
+
7
+ - keep rules in defined order
8
+ - assign the name to each rule
9
+ - enable/disable rules
10
+ - add/replace rules
11
+ - allow assign rules to additional named chains (in the same)
12
+ - caching lists of active rules
13
+
14
+ You will not need use this class directly until write plugins. For simple
15
+ rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and
16
+ [[MarkdownIt.use]].
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from collections.abc import Iterable
22
+ from dataclasses import dataclass, field
23
+ from typing import TYPE_CHECKING, Generic, TypedDict, TypeVar
24
+ import warnings
25
+
26
+ from .utils import EnvType
27
+
28
+ if TYPE_CHECKING:
29
+ from markdown_it import MarkdownIt
30
+
31
+
32
+ class StateBase:
33
+ def __init__(self, src: str, md: MarkdownIt, env: EnvType):
34
+ self.src = src
35
+ self.env = env
36
+ self.md = md
37
+
38
+ @property
39
+ def src(self) -> str:
40
+ return self._src
41
+
42
+ @src.setter
43
+ def src(self, value: str) -> None:
44
+ self._src = value
45
+ self._srcCharCode: tuple[int, ...] | None = None
46
+
47
+ @property
48
+ def srcCharCode(self) -> tuple[int, ...]:
49
+ warnings.warn(
50
+ "StateBase.srcCharCode is deprecated. Use StateBase.src instead.",
51
+ DeprecationWarning,
52
+ stacklevel=2,
53
+ )
54
+ if self._srcCharCode is None:
55
+ self._srcCharCode = tuple(ord(c) for c in self._src)
56
+ return self._srcCharCode
57
+
58
+
59
+ class RuleOptionsType(TypedDict, total=False):
60
+ alt: list[str]
61
+
62
+
63
+ RuleFuncTv = TypeVar("RuleFuncTv")
64
+ """A rule function, whose signature is dependent on the state type."""
65
+
66
+
67
+ @dataclass(slots=True)
68
+ class Rule(Generic[RuleFuncTv]):
69
+ name: str
70
+ enabled: bool
71
+ fn: RuleFuncTv = field(repr=False)
72
+ alt: list[str]
73
+
74
+
75
+ class Ruler(Generic[RuleFuncTv]):
76
+ def __init__(self) -> None:
77
+ # List of added rules.
78
+ self.__rules__: list[Rule[RuleFuncTv]] = []
79
+ # Cached rule chains.
80
+ # First level - chain name, '' for default.
81
+ # Second level - diginal anchor for fast filtering by charcodes.
82
+ self.__cache__: dict[str, list[RuleFuncTv]] | None = None
83
+
84
+ def __find__(self, name: str) -> int:
85
+ """Find rule index by name"""
86
+ for i, rule in enumerate(self.__rules__):
87
+ if rule.name == name:
88
+ return i
89
+ return -1
90
+
91
+ def __compile__(self) -> None:
92
+ """Build rules lookup cache"""
93
+ chains = {""}
94
+ # collect unique names
95
+ for rule in self.__rules__:
96
+ if not rule.enabled:
97
+ continue
98
+ for name in rule.alt:
99
+ chains.add(name)
100
+ self.__cache__ = {}
101
+ for chain in chains:
102
+ self.__cache__[chain] = []
103
+ for rule in self.__rules__:
104
+ if not rule.enabled:
105
+ continue
106
+ if chain and (chain not in rule.alt):
107
+ continue
108
+ self.__cache__[chain].append(rule.fn)
109
+
110
+ def at(
111
+ self, ruleName: str, fn: RuleFuncTv, options: RuleOptionsType | None = None
112
+ ) -> None:
113
+ """Replace rule by name with new function & options.
114
+
115
+ :param ruleName: rule name to replace.
116
+ :param fn: new rule function.
117
+ :param options: new rule options (not mandatory).
118
+ :raises: KeyError if name not found
119
+ """
120
+ index = self.__find__(ruleName)
121
+ options = options or {}
122
+ if index == -1:
123
+ raise KeyError(f"Parser rule not found: {ruleName}")
124
+ self.__rules__[index].fn = fn
125
+ self.__rules__[index].alt = options.get("alt", [])
126
+ self.__cache__ = None
127
+
128
+ def before(
129
+ self,
130
+ beforeName: str,
131
+ ruleName: str,
132
+ fn: RuleFuncTv,
133
+ options: RuleOptionsType | None = None,
134
+ ) -> None:
135
+ """Add new rule to chain before one with given name.
136
+
137
+ :param beforeName: new rule will be added before this one.
138
+ :param ruleName: new rule will be added before this one.
139
+ :param fn: new rule function.
140
+ :param options: new rule options (not mandatory).
141
+ :raises: KeyError if name not found
142
+ """
143
+ index = self.__find__(beforeName)
144
+ options = options or {}
145
+ if index == -1:
146
+ raise KeyError(f"Parser rule not found: {beforeName}")
147
+ self.__rules__.insert(
148
+ index, Rule[RuleFuncTv](ruleName, True, fn, options.get("alt", []))
149
+ )
150
+ self.__cache__ = None
151
+
152
+ def after(
153
+ self,
154
+ afterName: str,
155
+ ruleName: str,
156
+ fn: RuleFuncTv,
157
+ options: RuleOptionsType | None = None,
158
+ ) -> None:
159
+ """Add new rule to chain after one with given name.
160
+
161
+ :param afterName: new rule will be added after this one.
162
+ :param ruleName: new rule will be added after this one.
163
+ :param fn: new rule function.
164
+ :param options: new rule options (not mandatory).
165
+ :raises: KeyError if name not found
166
+ """
167
+ index = self.__find__(afterName)
168
+ options = options or {}
169
+ if index == -1:
170
+ raise KeyError(f"Parser rule not found: {afterName}")
171
+ self.__rules__.insert(
172
+ index + 1, Rule[RuleFuncTv](ruleName, True, fn, options.get("alt", []))
173
+ )
174
+ self.__cache__ = None
175
+
176
+ def push(
177
+ self, ruleName: str, fn: RuleFuncTv, options: RuleOptionsType | None = None
178
+ ) -> None:
179
+ """Push new rule to the end of chain.
180
+
181
+ :param ruleName: new rule will be added to the end of chain.
182
+ :param fn: new rule function.
183
+ :param options: new rule options (not mandatory).
184
+
185
+ """
186
+ self.__rules__.append(
187
+ Rule[RuleFuncTv](ruleName, True, fn, (options or {}).get("alt", []))
188
+ )
189
+ self.__cache__ = None
190
+
191
+ def enable(
192
+ self, names: str | Iterable[str], ignoreInvalid: bool = False
193
+ ) -> list[str]:
194
+ """Enable rules with given names.
195
+
196
+ :param names: name or list of rule names to enable.
197
+ :param ignoreInvalid: ignore errors when rule not found
198
+ :raises: KeyError if name not found and not ignoreInvalid
199
+ :return: list of found rule names
200
+ """
201
+ if isinstance(names, str):
202
+ names = [names]
203
+ result: list[str] = []
204
+ for name in names:
205
+ idx = self.__find__(name)
206
+ if (idx < 0) and ignoreInvalid:
207
+ continue
208
+ if (idx < 0) and not ignoreInvalid:
209
+ raise KeyError(f"Rules manager: invalid rule name {name}")
210
+ self.__rules__[idx].enabled = True
211
+ result.append(name)
212
+ self.__cache__ = None
213
+ return result
214
+
215
+ def enableOnly(
216
+ self, names: str | Iterable[str], ignoreInvalid: bool = False
217
+ ) -> list[str]:
218
+ """Enable rules with given names, and disable everything else.
219
+
220
+ :param names: name or list of rule names to enable.
221
+ :param ignoreInvalid: ignore errors when rule not found
222
+ :raises: KeyError if name not found and not ignoreInvalid
223
+ :return: list of found rule names
224
+ """
225
+ if isinstance(names, str):
226
+ names = [names]
227
+ for rule in self.__rules__:
228
+ rule.enabled = False
229
+ return self.enable(names, ignoreInvalid)
230
+
231
+ def disable(
232
+ self, names: str | Iterable[str], ignoreInvalid: bool = False
233
+ ) -> list[str]:
234
+ """Disable rules with given names.
235
+
236
+ :param names: name or list of rule names to enable.
237
+ :param ignoreInvalid: ignore errors when rule not found
238
+ :raises: KeyError if name not found and not ignoreInvalid
239
+ :return: list of found rule names
240
+ """
241
+ if isinstance(names, str):
242
+ names = [names]
243
+ result = []
244
+ for name in names:
245
+ idx = self.__find__(name)
246
+ if (idx < 0) and ignoreInvalid:
247
+ continue
248
+ if (idx < 0) and not ignoreInvalid:
249
+ raise KeyError(f"Rules manager: invalid rule name {name}")
250
+ self.__rules__[idx].enabled = False
251
+ result.append(name)
252
+ self.__cache__ = None
253
+ return result
254
+
255
+ def getRules(self, chainName: str = "") -> list[RuleFuncTv]:
256
+ """Return array of active functions (rules) for given chain name.
257
+ It analyzes rules configuration, compiles caches if not exists and returns result.
258
+
259
+ Default chain name is `''` (empty string). It can't be skipped.
260
+ That's done intentionally, to keep signature monomorphic for high speed.
261
+
262
+ """
263
+ if self.__cache__ is None:
264
+ self.__compile__()
265
+ assert self.__cache__ is not None
266
+ # Chain can be empty, if rules disabled. But we still have to return Array.
267
+ return self.__cache__.get(chainName, []) or []
268
+
269
+ def get_all_rules(self) -> list[str]:
270
+ """Return all available rule names."""
271
+ return [r.name for r in self.__rules__]
272
+
273
+ def get_active_rules(self) -> list[str]:
274
+ """Return the active rule names."""
275
+ return [r.name for r in self.__rules__ if r.enabled]
venv/lib/python3.12/site-packages/markdown_it/rules_block/__init__.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __all__ = (
2
+ "StateBlock",
3
+ "blockquote",
4
+ "code",
5
+ "fence",
6
+ "heading",
7
+ "hr",
8
+ "html_block",
9
+ "lheading",
10
+ "list_block",
11
+ "make_fence_rule",
12
+ "paragraph",
13
+ "reference",
14
+ "table",
15
+ )
16
+
17
+ from .blockquote import blockquote
18
+ from .code import code
19
+ from .fence import fence, make_fence_rule
20
+ from .heading import heading
21
+ from .hr import hr
22
+ from .html_block import html_block
23
+ from .lheading import lheading
24
+ from .list import list_block
25
+ from .paragraph import paragraph
26
+ from .reference import reference
27
+ from .state_block import StateBlock
28
+ from .table import table
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (698 Bytes). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/blockquote.cpython-312.pyc ADDED
Binary file (9.22 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/code.cpython-312.pyc ADDED
Binary file (1.37 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/fence.cpython-312.pyc ADDED
Binary file (4.6 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/heading.cpython-312.pyc ADDED
Binary file (2.61 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/hr.cpython-312.pyc ADDED
Binary file (1.74 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/html_block.cpython-312.pyc ADDED
Binary file (3.6 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/lheading.cpython-312.pyc ADDED
Binary file (2.92 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/list.cpython-312.pyc ADDED
Binary file (9.99 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/paragraph.cpython-312.pyc ADDED
Binary file (2.16 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/reference.cpython-312.pyc ADDED
Binary file (6.19 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/state_block.cpython-312.pyc ADDED
Binary file (9.19 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/__pycache__/table.cpython-312.pyc ADDED
Binary file (7.37 kB). View file
 
venv/lib/python3.12/site-packages/markdown_it/rules_block/blockquote.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Block quotes
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+
6
+ from ..common.utils import isStrSpace
7
+ from .state_block import StateBlock
8
+
9
+ LOGGER = logging.getLogger(__name__)
10
+
11
+
12
+ def blockquote(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
13
+ LOGGER.debug(
14
+ "entering blockquote: %s, %s, %s, %s", state, startLine, endLine, silent
15
+ )
16
+
17
+ oldLineMax = state.lineMax
18
+ pos = state.bMarks[startLine] + state.tShift[startLine]
19
+ max = state.eMarks[startLine]
20
+
21
+ if state.is_code_block(startLine):
22
+ return False
23
+
24
+ # check the block quote marker
25
+ try:
26
+ if state.src[pos] != ">":
27
+ return False
28
+ except IndexError:
29
+ return False
30
+ pos += 1
31
+
32
+ # we know that it's going to be a valid blockquote,
33
+ # so no point trying to find the end of it in silent mode
34
+ if silent:
35
+ return True
36
+
37
+ # set offset past spaces and ">"
38
+ initial = offset = state.sCount[startLine] + 1
39
+
40
+ try:
41
+ second_char: str | None = state.src[pos]
42
+ except IndexError:
43
+ second_char = None
44
+
45
+ # skip one optional space after '>'
46
+ if second_char == " ":
47
+ # ' > test '
48
+ # ^ -- position start of line here:
49
+ pos += 1
50
+ initial += 1
51
+ offset += 1
52
+ adjustTab = False
53
+ spaceAfterMarker = True
54
+ elif second_char == "\t":
55
+ spaceAfterMarker = True
56
+
57
+ if (state.bsCount[startLine] + offset) % 4 == 3:
58
+ # ' >\t test '
59
+ # ^ -- position start of line here (tab has width==1)
60
+ pos += 1
61
+ initial += 1
62
+ offset += 1
63
+ adjustTab = False
64
+ else:
65
+ # ' >\t test '
66
+ # ^ -- position start of line here + shift bsCount slightly
67
+ # to make extra space appear
68
+ adjustTab = True
69
+
70
+ else:
71
+ spaceAfterMarker = False
72
+
73
+ oldBMarks = [state.bMarks[startLine]]
74
+ state.bMarks[startLine] = pos
75
+
76
+ while pos < max:
77
+ ch = state.src[pos]
78
+
79
+ if isStrSpace(ch):
80
+ if ch == "\t":
81
+ offset += (
82
+ 4
83
+ - (offset + state.bsCount[startLine] + (1 if adjustTab else 0)) % 4
84
+ )
85
+ else:
86
+ offset += 1
87
+
88
+ else:
89
+ break
90
+
91
+ pos += 1
92
+
93
+ oldBSCount = [state.bsCount[startLine]]
94
+ state.bsCount[startLine] = (
95
+ state.sCount[startLine] + 1 + (1 if spaceAfterMarker else 0)
96
+ )
97
+
98
+ lastLineEmpty = pos >= max
99
+
100
+ oldSCount = [state.sCount[startLine]]
101
+ state.sCount[startLine] = offset - initial
102
+
103
+ oldTShift = [state.tShift[startLine]]
104
+ state.tShift[startLine] = pos - state.bMarks[startLine]
105
+
106
+ terminatorRules = state.md.block.ruler.getRules("blockquote")
107
+
108
+ oldParentType = state.parentType
109
+ state.parentType = "blockquote"
110
+
111
+ # Search the end of the block
112
+ #
113
+ # Block ends with either:
114
+ # 1. an empty line outside:
115
+ # ```
116
+ # > test
117
+ #
118
+ # ```
119
+ # 2. an empty line inside:
120
+ # ```
121
+ # >
122
+ # test
123
+ # ```
124
+ # 3. another tag:
125
+ # ```
126
+ # > test
127
+ # - - -
128
+ # ```
129
+
130
+ # for (nextLine = startLine + 1; nextLine < endLine; nextLine++) {
131
+ nextLine = startLine + 1
132
+ while nextLine < endLine:
133
+ # check if it's outdented, i.e. it's inside list item and indented
134
+ # less than said list item:
135
+ #
136
+ # ```
137
+ # 1. anything
138
+ # > current blockquote
139
+ # 2. checking this line
140
+ # ```
141
+ isOutdented = state.sCount[nextLine] < state.blkIndent
142
+
143
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
144
+ max = state.eMarks[nextLine]
145
+
146
+ if pos >= max:
147
+ # Case 1: line is not inside the blockquote, and this line is empty.
148
+ break
149
+
150
+ evaluatesTrue = state.src[pos] == ">" and not isOutdented
151
+ pos += 1
152
+ if evaluatesTrue:
153
+ # This line is inside the blockquote.
154
+
155
+ # set offset past spaces and ">"
156
+ initial = offset = state.sCount[nextLine] + 1
157
+
158
+ try:
159
+ next_char: str | None = state.src[pos]
160
+ except IndexError:
161
+ next_char = None
162
+
163
+ # skip one optional space after '>'
164
+ if next_char == " ":
165
+ # ' > test '
166
+ # ^ -- position start of line here:
167
+ pos += 1
168
+ initial += 1
169
+ offset += 1
170
+ adjustTab = False
171
+ spaceAfterMarker = True
172
+ elif next_char == "\t":
173
+ spaceAfterMarker = True
174
+
175
+ if (state.bsCount[nextLine] + offset) % 4 == 3:
176
+ # ' >\t test '
177
+ # ^ -- position start of line here (tab has width==1)
178
+ pos += 1
179
+ initial += 1
180
+ offset += 1
181
+ adjustTab = False
182
+ else:
183
+ # ' >\t test '
184
+ # ^ -- position start of line here + shift bsCount slightly
185
+ # to make extra space appear
186
+ adjustTab = True
187
+
188
+ else:
189
+ spaceAfterMarker = False
190
+
191
+ oldBMarks.append(state.bMarks[nextLine])
192
+ state.bMarks[nextLine] = pos
193
+
194
+ while pos < max:
195
+ ch = state.src[pos]
196
+
197
+ if isStrSpace(ch):
198
+ if ch == "\t":
199
+ offset += (
200
+ 4
201
+ - (
202
+ offset
203
+ + state.bsCount[nextLine]
204
+ + (1 if adjustTab else 0)
205
+ )
206
+ % 4
207
+ )
208
+ else:
209
+ offset += 1
210
+ else:
211
+ break
212
+
213
+ pos += 1
214
+
215
+ lastLineEmpty = pos >= max
216
+
217
+ oldBSCount.append(state.bsCount[nextLine])
218
+ state.bsCount[nextLine] = (
219
+ state.sCount[nextLine] + 1 + (1 if spaceAfterMarker else 0)
220
+ )
221
+
222
+ oldSCount.append(state.sCount[nextLine])
223
+ state.sCount[nextLine] = offset - initial
224
+
225
+ oldTShift.append(state.tShift[nextLine])
226
+ state.tShift[nextLine] = pos - state.bMarks[nextLine]
227
+
228
+ nextLine += 1
229
+ continue
230
+
231
+ # Case 2: line is not inside the blockquote, and the last line was empty.
232
+ if lastLineEmpty:
233
+ break
234
+
235
+ # Case 3: another tag found.
236
+ terminate = False
237
+
238
+ for terminatorRule in terminatorRules:
239
+ if terminatorRule(state, nextLine, endLine, True):
240
+ terminate = True
241
+ break
242
+
243
+ if terminate:
244
+ # Quirk to enforce "hard termination mode" for paragraphs;
245
+ # normally if you call `tokenize(state, startLine, nextLine)`,
246
+ # paragraphs will look below nextLine for paragraph continuation,
247
+ # but if blockquote is terminated by another tag, they shouldn't
248
+ state.lineMax = nextLine
249
+
250
+ if state.blkIndent != 0:
251
+ # state.blkIndent was non-zero, we now set it to zero,
252
+ # so we need to re-calculate all offsets to appear as
253
+ # if indent wasn't changed
254
+ oldBMarks.append(state.bMarks[nextLine])
255
+ oldBSCount.append(state.bsCount[nextLine])
256
+ oldTShift.append(state.tShift[nextLine])
257
+ oldSCount.append(state.sCount[nextLine])
258
+ state.sCount[nextLine] -= state.blkIndent
259
+
260
+ break
261
+
262
+ oldBMarks.append(state.bMarks[nextLine])
263
+ oldBSCount.append(state.bsCount[nextLine])
264
+ oldTShift.append(state.tShift[nextLine])
265
+ oldSCount.append(state.sCount[nextLine])
266
+
267
+ # A negative indentation means that this is a paragraph continuation
268
+ #
269
+ state.sCount[nextLine] = -1
270
+
271
+ nextLine += 1
272
+
273
+ oldIndent = state.blkIndent
274
+ state.blkIndent = 0
275
+
276
+ # Detect GitHub-style alert marker on the first content line.
277
+ # Note: `startLine` here refers to the first content line of the
278
+ # blockquote, after the `>` prefix has already been stripped by the
279
+ # blockquote parser above (bMarks/tShift adjusted to skip `> `).
280
+ alert_kind = None
281
+ if state.md.options.get("alerts", False) and nextLine > startLine:
282
+ alert_kind = _detect_alert(state, startLine)
283
+
284
+ lines = [startLine, 0]
285
+
286
+ if alert_kind is not None:
287
+ # Emit alert tokens instead of blockquote tokens
288
+ alert_lower = alert_kind.lower()
289
+ token = state.push("alert_open", "div", 1)
290
+ token.markup = ">"
291
+ token.attrSet("class", f"markdown-alert markdown-alert-{alert_lower}")
292
+ token.map = lines
293
+ token.info = alert_kind
294
+ token.meta = {"kind": alert_kind}
295
+
296
+ # Emit a title paragraph: <p class="markdown-alert-title">Kind</p>
297
+ token = state.push("alert_title_open", "p", 1)
298
+ token.attrSet("class", "markdown-alert-title")
299
+ title_token = state.push("inline", "", 0)
300
+ title_token.content = alert_kind.capitalize()
301
+ title_token.children = []
302
+ token = state.push("alert_title_close", "p", -1)
303
+
304
+ # Skip the marker line (startLine) and tokenize from startLine + 1.
305
+ contentStart = startLine + 1
306
+ if contentStart < nextLine:
307
+ # tokenize() updates state.line to nextLine as part of its
308
+ # contract, consistent with the blockquote code path below.
309
+ state.md.block.tokenize(state, contentStart, nextLine)
310
+ else:
311
+ state.line = nextLine
312
+
313
+ token = state.push("alert_close", "div", -1)
314
+ token.markup = ">"
315
+ else:
316
+ token = state.push("blockquote_open", "blockquote", 1)
317
+ token.markup = ">"
318
+ token.map = lines
319
+
320
+ state.md.block.tokenize(state, startLine, nextLine)
321
+
322
+ token = state.push("blockquote_close", "blockquote", -1)
323
+ token.markup = ">"
324
+
325
+ state.lineMax = oldLineMax
326
+ state.parentType = oldParentType
327
+ # Update the opening token map for both alert and blockquote containers.
328
+ lines[1] = state.line
329
+
330
+ # Restore original tShift; this might not be necessary since the parser
331
+ # has already been here, but just to make sure we can do that.
332
+ for i, item in enumerate(oldTShift):
333
+ state.bMarks[i + startLine] = oldBMarks[i]
334
+ state.tShift[i + startLine] = item
335
+ state.sCount[i + startLine] = oldSCount[i]
336
+ state.bsCount[i + startLine] = oldBSCount[i]
337
+
338
+ state.blkIndent = oldIndent
339
+
340
+ return True
341
+
342
+
343
+ _ALERT_TYPES = {"NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION"}
344
+
345
+
346
+ def _detect_alert(state: StateBlock, startLine: int) -> str | None:
347
+ """Detect ``[!TYPE]`` on *startLine* (after ``>`` prefix has been stripped).
348
+
349
+ Returns the alert type string (e.g. ``"NOTE"``) or ``None``.
350
+ """
351
+ pos = state.bMarks[startLine] + state.tShift[startLine]
352
+ maximum = state.eMarks[startLine]
353
+ src = state.src
354
+
355
+ # Trim trailing whitespace
356
+ while maximum > pos and src[maximum - 1] in (" ", "\t"):
357
+ maximum -= 1
358
+
359
+ if maximum - pos < 4:
360
+ return None
361
+ if src[pos] != "[" or src[pos + 1] != "!":
362
+ return None
363
+ if src[maximum - 1] != "]":
364
+ return None
365
+ type_str = src[pos + 2 : maximum - 1].upper()
366
+ if type_str not in _ALERT_TYPES:
367
+ return None
368
+ return type_str
venv/lib/python3.12/site-packages/markdown_it/rules_block/code.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Code block (4 spaces padded)."""
2
+
3
+ import logging
4
+
5
+ from .state_block import StateBlock
6
+
7
+ LOGGER = logging.getLogger(__name__)
8
+
9
+
10
+ def code(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
11
+ LOGGER.debug("entering code: %s, %s, %s, %s", state, startLine, endLine, silent)
12
+
13
+ if not state.is_code_block(startLine):
14
+ return False
15
+
16
+ last = nextLine = startLine + 1
17
+
18
+ while nextLine < endLine:
19
+ if state.isEmpty(nextLine):
20
+ nextLine += 1
21
+ continue
22
+
23
+ if state.is_code_block(nextLine):
24
+ nextLine += 1
25
+ last = nextLine
26
+ continue
27
+
28
+ break
29
+
30
+ state.line = last
31
+
32
+ token = state.push("code_block", "code", 0)
33
+ token.content = state.getLines(startLine, last, 4 + state.blkIndent, False) + "\n"
34
+ token.map = [startLine, state.line]
35
+
36
+ return True
venv/lib/python3.12/site-packages/markdown_it/rules_block/fence.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # fences (``` lang, ~~~ lang)
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Callable
5
+ import logging
6
+
7
+ from .state_block import StateBlock
8
+
9
+ LOGGER = logging.getLogger(__name__)
10
+
11
+
12
+ def make_fence_rule(
13
+ *,
14
+ markers: tuple[str, ...] = ("~", "`"),
15
+ token_type: str = "fence",
16
+ exact_match: bool = False,
17
+ disallow_marker_in_info: tuple[str, ...] = ("`",),
18
+ min_markers: int = 3,
19
+ ) -> Callable[[StateBlock, int, int, bool], bool]:
20
+ """Create a fence parsing rule with configurable options.
21
+
22
+ :param markers: Tuple of single characters that can be used as fence markers.
23
+ :param token_type: The token type name to emit (e.g. "fence", "colon_fence").
24
+ :param exact_match: If True, the closing fence must have exactly the same
25
+ number of marker characters as the opening fence (not "at least as many").
26
+ This enables nesting of fences with different marker counts.
27
+ :param disallow_marker_in_info: Tuple of marker characters that are not allowed
28
+ to appear in the info string. The check only applies when the actual opening
29
+ marker is in this tuple (e.g. a tilde fence is unaffected by ``"`"`` being
30
+ listed). Per CommonMark, backtick fences cannot have backticks in the info
31
+ string. Use ``()`` to disable this restriction.
32
+ :param min_markers: Minimum number of marker characters to form a fence.
33
+ :return: A block rule function with signature
34
+ ``(state, startLine, endLine, silent) -> bool``.
35
+ """
36
+
37
+ closing_matcher: Callable[[int, int], bool]
38
+ if exact_match:
39
+ # closing code fence must have exactly the same number of markers as the opening one
40
+ closing_matcher = lambda opening_len, closing_len: closing_len == opening_len # noqa: E731
41
+ else:
42
+ # closing code fence must be at least as long as the opening one
43
+ closing_matcher = lambda opening_len, closing_len: closing_len >= opening_len # noqa: E731
44
+
45
+ def _fence_rule(
46
+ state: StateBlock, startLine: int, endLine: int, silent: bool
47
+ ) -> bool:
48
+ LOGGER.debug(
49
+ "entering fence: %s, %s, %s, %s", state, startLine, endLine, silent
50
+ )
51
+
52
+ haveEndMarker = False
53
+ pos = state.bMarks[startLine] + state.tShift[startLine]
54
+ maximum = state.eMarks[startLine]
55
+
56
+ if state.is_code_block(startLine):
57
+ return False
58
+
59
+ if pos + min_markers > maximum:
60
+ return False
61
+
62
+ marker = state.src[pos]
63
+
64
+ if marker not in markers:
65
+ return False
66
+
67
+ # scan marker length
68
+ mem = pos
69
+ pos = state.skipCharsStr(pos, marker)
70
+
71
+ length = pos - mem
72
+
73
+ if length < min_markers:
74
+ return False
75
+
76
+ markup = state.src[mem:pos]
77
+ params = state.src[pos:maximum]
78
+
79
+ if marker in disallow_marker_in_info and marker in params:
80
+ return False
81
+
82
+ # Since start is found, we can report success here in validation mode
83
+ if silent:
84
+ return True
85
+
86
+ # search end of block
87
+ nextLine = startLine
88
+
89
+ while True:
90
+ nextLine += 1
91
+ if nextLine >= endLine:
92
+ # unclosed block should be autoclosed by end of document.
93
+ # also block seems to be autoclosed by end of parent
94
+ break
95
+
96
+ pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]
97
+ maximum = state.eMarks[nextLine]
98
+
99
+ if pos < maximum and state.sCount[nextLine] < state.blkIndent:
100
+ # non-empty line with negative indent should stop the list:
101
+ # - ```
102
+ # test
103
+ break
104
+
105
+ try:
106
+ if state.src[pos] != marker:
107
+ continue
108
+ except IndexError:
109
+ break
110
+
111
+ if state.is_code_block(nextLine):
112
+ continue
113
+
114
+ pos = state.skipCharsStr(pos, marker)
115
+
116
+ if not closing_matcher(length, pos - mem):
117
+ continue
118
+
119
+ # make sure tail has spaces only
120
+ pos = state.skipSpaces(pos)
121
+
122
+ if pos < maximum:
123
+ continue
124
+
125
+ haveEndMarker = True
126
+ # found!
127
+ break
128
+
129
+ # If a fence has heading spaces, they should be removed from its inner block
130
+ length = state.sCount[startLine]
131
+
132
+ state.line = nextLine + (1 if haveEndMarker else 0)
133
+
134
+ token = state.push(token_type, "code", 0)
135
+ token.info = params
136
+ token.content = state.getLines(startLine + 1, nextLine, length, True)
137
+ token.markup = markup
138
+ token.map = [startLine, state.line]
139
+
140
+ return True
141
+
142
+ return _fence_rule
143
+
144
+
145
+ #: The default fence rule (backtick and tilde markers, CommonMark compliant).
146
+ fence = make_fence_rule()
venv/lib/python3.12/site-packages/markdown_it/rules_block/heading.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Atex heading (#, ##, ...)"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ from ..common.utils import isStrSpace
8
+ from .state_block import StateBlock
9
+
10
+ LOGGER = logging.getLogger(__name__)
11
+
12
+
13
+ def heading(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
14
+ LOGGER.debug("entering heading: %s, %s, %s, %s", state, startLine, endLine, silent)
15
+
16
+ pos = state.bMarks[startLine] + state.tShift[startLine]
17
+ maximum = state.eMarks[startLine]
18
+
19
+ if state.is_code_block(startLine):
20
+ return False
21
+
22
+ ch: str | None = state.src[pos]
23
+
24
+ if ch != "#" or pos >= maximum:
25
+ return False
26
+
27
+ # count heading level
28
+ level = 1
29
+ pos += 1
30
+ try:
31
+ ch = state.src[pos]
32
+ except IndexError:
33
+ ch = None
34
+ while ch == "#" and pos < maximum and level <= 6:
35
+ level += 1
36
+ pos += 1
37
+ try:
38
+ ch = state.src[pos]
39
+ except IndexError:
40
+ ch = None
41
+
42
+ if level > 6 or (pos < maximum and not isStrSpace(ch)):
43
+ return False
44
+
45
+ if silent:
46
+ return True
47
+
48
+ # Let's cut tails like ' ### ' from the end of string
49
+
50
+ maximum = state.skipSpacesBack(maximum, pos)
51
+ tmp = state.skipCharsStrBack(maximum, "#", pos)
52
+ if tmp > pos and isStrSpace(state.src[tmp - 1]):
53
+ maximum = tmp
54
+
55
+ state.line = startLine + 1
56
+
57
+ token = state.push("heading_open", "h" + str(level), 1)
58
+ token.markup = "########"[:level]
59
+ token.map = [startLine, state.line]
60
+
61
+ token = state.push("inline", "", 0)
62
+ token.content = state.src[pos:maximum].strip()
63
+ token.map = [startLine, state.line]
64
+ token.children = []
65
+
66
+ token = state.push("heading_close", "h" + str(level), -1)
67
+ token.markup = "########"[:level]
68
+
69
+ return True
venv/lib/python3.12/site-packages/markdown_it/rules_block/hr.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Horizontal rule
2
+
3
+ At least 3 of these characters on a line * - _
4
+ """
5
+
6
+ import logging
7
+
8
+ from ..common.utils import isStrSpace
9
+ from .state_block import StateBlock
10
+
11
+ LOGGER = logging.getLogger(__name__)
12
+
13
+
14
+ def hr(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
15
+ LOGGER.debug("entering hr: %s, %s, %s, %s", state, startLine, endLine, silent)
16
+
17
+ pos = state.bMarks[startLine] + state.tShift[startLine]
18
+ maximum = state.eMarks[startLine]
19
+
20
+ if state.is_code_block(startLine):
21
+ return False
22
+
23
+ try:
24
+ marker = state.src[pos]
25
+ except IndexError:
26
+ return False
27
+ pos += 1
28
+
29
+ # Check hr marker
30
+ if marker not in ("*", "-", "_"):
31
+ return False
32
+
33
+ # markers can be mixed with spaces, but there should be at least 3 of them
34
+
35
+ cnt = 1
36
+ while pos < maximum:
37
+ ch = state.src[pos]
38
+ pos += 1
39
+ if ch != marker and not isStrSpace(ch):
40
+ return False
41
+ if ch == marker:
42
+ cnt += 1
43
+
44
+ if cnt < 3:
45
+ return False
46
+
47
+ if silent:
48
+ return True
49
+
50
+ state.line = startLine + 1
51
+
52
+ token = state.push("hr", "hr", 0)
53
+ token.map = [startLine, state.line]
54
+ token.markup = marker * (cnt + 1)
55
+
56
+ return True
venv/lib/python3.12/site-packages/markdown_it/rules_block/html_block.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HTML block
2
+ from __future__ import annotations
3
+
4
+ import logging
5
+ import re
6
+
7
+ from ..common.html_blocks import block_names
8
+ from ..common.html_re import HTML_OPEN_CLOSE_TAG_STR
9
+ from .state_block import StateBlock
10
+
11
+ LOGGER = logging.getLogger(__name__)
12
+
13
+ # An array of opening and corresponding closing sequences for html tags,
14
+ # last argument defines whether it can terminate a paragraph or not
15
+ HTML_SEQUENCES: list[tuple[re.Pattern[str], re.Pattern[str], bool]] = [
16
+ (
17
+ re.compile(r"^<(script|pre|style|textarea)(?=(\s|>|$))", re.IGNORECASE),
18
+ re.compile(r"<\/(script|pre|style|textarea)>", re.IGNORECASE),
19
+ True,
20
+ ),
21
+ (re.compile(r"^<!--"), re.compile(r"-->"), True),
22
+ (re.compile(r"^<\?"), re.compile(r"\?>"), True),
23
+ (re.compile(r"^<![A-Z]"), re.compile(r">"), True),
24
+ (re.compile(r"^<!\[CDATA\["), re.compile(r"\]\]>"), True),
25
+ (
26
+ re.compile("^</?(" + "|".join(block_names) + ")(?=(\\s|/?>|$))", re.IGNORECASE),
27
+ re.compile(r"^$"),
28
+ True,
29
+ ),
30
+ (re.compile(HTML_OPEN_CLOSE_TAG_STR + "\\s*$"), re.compile(r"^$"), False),
31
+ ]
32
+
33
+
34
+ def html_block(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
35
+ LOGGER.debug(
36
+ "entering html_block: %s, %s, %s, %s", state, startLine, endLine, silent
37
+ )
38
+ pos = state.bMarks[startLine] + state.tShift[startLine]
39
+ maximum = state.eMarks[startLine]
40
+
41
+ if state.is_code_block(startLine):
42
+ return False
43
+
44
+ if not state.md.options.get("html", None):
45
+ return False
46
+
47
+ if state.src[pos] != "<":
48
+ return False
49
+
50
+ lineText = state.src[pos:maximum]
51
+
52
+ html_seq = None
53
+ for HTML_SEQUENCE in HTML_SEQUENCES:
54
+ if HTML_SEQUENCE[0].search(lineText):
55
+ html_seq = HTML_SEQUENCE
56
+ break
57
+
58
+ if not html_seq:
59
+ return False
60
+
61
+ if silent:
62
+ # true if this sequence can be a terminator, false otherwise
63
+ return html_seq[2]
64
+
65
+ nextLine = startLine + 1
66
+
67
+ # If we are here - we detected HTML block.
68
+ # Let's roll down till block end.
69
+ if not html_seq[1].search(lineText):
70
+ while nextLine < endLine:
71
+ if state.sCount[nextLine] < state.blkIndent:
72
+ break
73
+
74
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
75
+ maximum = state.eMarks[nextLine]
76
+ lineText = state.src[pos:maximum]
77
+
78
+ if html_seq[1].search(lineText):
79
+ if len(lineText) != 0:
80
+ nextLine += 1
81
+ break
82
+ nextLine += 1
83
+
84
+ state.line = nextLine
85
+
86
+ token = state.push("html_block", "", 0)
87
+ token.map = [startLine, nextLine]
88
+ token.content = state.getLines(startLine, nextLine, state.blkIndent, True)
89
+
90
+ return True
venv/lib/python3.12/site-packages/markdown_it/rules_block/lheading.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # lheading (---, ==)
2
+ import logging
3
+
4
+ from .state_block import StateBlock
5
+
6
+ LOGGER = logging.getLogger(__name__)
7
+
8
+
9
+ def lheading(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
10
+ LOGGER.debug("entering lheading: %s, %s, %s, %s", state, startLine, endLine, silent)
11
+
12
+ level = None
13
+ nextLine = startLine + 1
14
+ ruler = state.md.block.ruler
15
+ terminatorRules = ruler.getRules("paragraph")
16
+
17
+ if state.is_code_block(startLine):
18
+ return False
19
+
20
+ oldParentType = state.parentType
21
+ state.parentType = "paragraph" # use paragraph to match terminatorRules
22
+
23
+ # jump line-by-line until empty one or EOF
24
+ while nextLine < endLine and not state.isEmpty(nextLine):
25
+ # this would be a code block normally, but after paragraph
26
+ # it's considered a lazy continuation regardless of what's there
27
+ if state.sCount[nextLine] - state.blkIndent > 3:
28
+ nextLine += 1
29
+ continue
30
+
31
+ # Check for underline in setext header
32
+ if state.sCount[nextLine] >= state.blkIndent:
33
+ pos = state.bMarks[nextLine] + state.tShift[nextLine]
34
+ maximum = state.eMarks[nextLine]
35
+
36
+ if pos < maximum:
37
+ marker = state.src[pos]
38
+
39
+ if marker in ("-", "="):
40
+ pos = state.skipCharsStr(pos, marker)
41
+ pos = state.skipSpaces(pos)
42
+
43
+ # /* = */
44
+ if pos >= maximum:
45
+ level = 1 if marker == "=" else 2
46
+ break
47
+
48
+ # quirk for blockquotes, this line should already be checked by that rule
49
+ if state.sCount[nextLine] < 0:
50
+ nextLine += 1
51
+ continue
52
+
53
+ # Some tags can terminate paragraph without empty line.
54
+ terminate = False
55
+ for terminatorRule in terminatorRules:
56
+ if terminatorRule(state, nextLine, endLine, True):
57
+ terminate = True
58
+ break
59
+ if terminate:
60
+ break
61
+
62
+ nextLine += 1
63
+
64
+ if not level:
65
+ # Didn't find valid underline
66
+ return False
67
+
68
+ content = state.getLines(startLine, nextLine, state.blkIndent, False).strip()
69
+
70
+ state.line = nextLine + 1
71
+
72
+ token = state.push("heading_open", "h" + str(level), 1)
73
+ token.markup = marker
74
+ token.map = [startLine, state.line]
75
+
76
+ token = state.push("inline", "", 0)
77
+ token.content = content
78
+ token.map = [startLine, state.line - 1]
79
+ token.children = []
80
+
81
+ token = state.push("heading_close", "h" + str(level), -1)
82
+ token.markup = marker
83
+
84
+ state.parentType = oldParentType
85
+
86
+ return True