Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/mistune/util.py: 100%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import re
2import html
3from typing import Callable, Match, cast
4from urllib.parse import quote
6_expand_tab_re = re.compile(r"^( {0,3})\t", flags=re.M)
7_replace_charref = cast(Callable[[Match[str]], str], getattr(html, "_replace_charref"))
10def expand_leading_tab(text: str, width: int = 4) -> str:
11 def repl(m: Match[str]) -> str:
12 s = m.group(1)
13 return s + " " * (width - len(s))
15 return _expand_tab_re.sub(repl, text)
18def expand_tab(text: str, space: str = " ") -> str:
19 repl = r"\1" + space
20 return _expand_tab_re.sub(repl, text)
23def escape(s: str, quote: bool = True) -> str:
24 """Escape characters of ``&<>``. If quote=True, ``"`` will be
25 converted to ``"e;``."""
26 s = s.replace("&", "&")
27 s = s.replace("<", "<")
28 s = s.replace(">", ">")
29 if quote:
30 s = s.replace('"', """)
31 return s
34def escape_url(link: str) -> str:
35 """Escape URL for safety."""
36 safe = (
37 ":/?#@" # gen-delims - '[]' (rfc3986)
38 "!$&()*+,;=" # sub-delims - "'" (rfc3986)
39 "%" # leave already-encoded octets alone
40 )
41 return quote(unescape(link), safe=safe)
44def safe_entity(s: str) -> str:
45 """Escape characters for safety."""
46 return escape(unescape(s))
49def unikey(s: str) -> str:
50 """Generate a unique key for links and footnotes."""
51 key = " ".join(s.split()).strip()
52 return key.lower().upper()
55_charref_re = re.compile(
56 r"&(#[0-9]{1,7};"
57 r"|#[xX][0-9a-fA-F]+;"
58 r"|[^\t\n\f <&#;]{1,32};)"
59)
62def unescape(s: str) -> str:
63 """
64 Copy from `html.unescape`, but `_charref` is different. CommonMark
65 does not accept entity references without a trailing semicolon
66 """
67 if "&" not in s:
68 return s
69 return _charref_re.sub(_replace_charref, s)
72_striptags_re = re.compile(r"(<!--.*?-->|<[^>]*>)")
73_strip_image_re = re.compile(r"<img\b[^>]*\balt=(\"([^\"]*)\"|'([^']*)')[^>]*>")
76def striptags(s: str) -> str:
77 s = _strip_image_re.sub(lambda m: m.group(2) or m.group(3) or "", s)
78 return _striptags_re.sub("", s)
81def strip_end(src: str) -> str:
82 r"""Strip trailing whitespace after the final line break.
84 This used to be implemented as ``re.sub(r"\n\s+$", "\n", src)``.
85 For a long run of blank lines followed by a non-whitespace continuation,
86 the regex retries ``\s+`` from every preceding newline and becomes
87 quadratic. Scanning the suffix once keeps the same behavior in linear
88 time.
89 """
90 end = len(src)
91 while end and src[end - 1].isspace():
92 end -= 1
94 newline = src.find("\n", end)
95 if newline >= 0:
96 return src[:newline] + "\n"
97 return src