upgrade mistune to 3.1.3

This commit is contained in:
Kenneth Kehl
2025-03-26 09:16:07 -07:00
parent 96c033cd6f
commit 0108bcf91f
7 changed files with 407 additions and 632 deletions

View File

@@ -1,308 +1,220 @@
import mistune
from notifications_utils import MAGIC_SEQUENCE, magic_sequence_regex
from notifications_utils.formatters import create_sanitised_html_for_url
import re
from itertools import count
import mistune
from ordered_set import OrderedSet
from notifications_utils import MAGIC_SEQUENCE, magic_sequence_regex
from notifications_utils.formatters import create_sanitised_html_for_url
LINK_STYLE = "word-wrap: break-word; color: #1D70B8;"
mistune._block_quote_leading_pattern = re.compile(r"^ *\^ ?", flags=re.M)
mistune.BlockGrammar.block_quote = re.compile(r"^( *\^[^\n]+(\n[^\n]+)*\n*)+")
mistune.BlockGrammar.list_block = re.compile(
r"^( *)([•*-]|\d+\.)[\s\S]+?"
r"(?:"
r"\n+(?=\1?(?:[-*_] *){3,}(?:\n+|$))" # hrule
r"|\n+(?=%s)" # def links
r"|\n+(?=%s)" # def footnotes
r"|\n{2,}"
r"(?! )"
r"(?!\1(?:[•*-]|\d+\.) )\n*"
r"|"
r"\s*$)"
% (
mistune._pure_pattern(mistune.BlockGrammar.def_links),
mistune._pure_pattern(mistune.BlockGrammar.def_footnotes),
)
)
mistune.BlockGrammar.list_item = re.compile(
r"^(( *)(?:[•*-]|\d+\.)[^\n]*" r"(?:\n(?!\2(?:[•*-]|\d+\.))[^\n]*)*)", flags=re.M
)
mistune.BlockGrammar.list_bullet = re.compile(r"^ *(?:[•*-]|\d+\.)")
mistune.InlineGrammar.url = re.compile(r"""^(https?:\/\/[^\s<]+[^<.,:"')\]\s])""")
mistune.InlineLexer.default_rules = list(
OrderedSet(mistune.InlineLexer.default_rules)
- set(
(
"emphasis",
"double_emphasis",
"strikethrough",
"code",
class EmailRenderer(mistune.HTMLRenderer):
def heading(self, text, level):
if level == 1:
return (
'<h2 style="Margin: 0 0 20px 0; padding: 0; '
'font-size: 27px; line-heigh: 35px; font-weight: bold; color: #0B0C0C;">'
f"{text}</h2>"
)
return self.paragraph(text)
def paragraph(self, text):
if text.strip():
return (
'<p style="Margin: 0 0 20px 0; font-size: 19px; '
'line-height: 25px; color: #0B0C0C;">' + text + '</p>'
)
def emphasis(self, text):
return f"*{text}*"
def block_quote(self, text):
return (
'<blockquote style="Margin: 0 0 20px 0; border-left: 10px solid #B1B4B6; '
'padding: 15 px 0 0.1px 15 px; font-size: 19px; line-height: 25px;">'
f"{text}</blockquote>"
)
)
)
mistune.InlineLexer.inline_html_rules = list(
set(mistune.InlineLexer.inline_html_rules)
- set(
(
"emphasis",
"double_emphasis",
"strikethrough",
"code",
def thematic_break(self):
return '<hr style="border: 0; height: 1px; background: #B1B4B6; Margin: 30px 0 30px 0;">'
def codespan(self, text):
return (
f"`{text}`"
)
)
)
def linebreak(self):
return "<br />"
def list(self, text, ordered, level=None, start=None, **kwargs):
tag = "ol" if ordered else "ul"
style = (
'list-style-type: decimal;' if ordered else 'list-style-type: disc;'
)
return (
'<table role="presentation" style="padding 0 0 20px 0;"><tr<td style="font-family: Helvetica, Arial, sans-serif;">'
f'<{tag} style="Margin: 0 0 0 20px; padding: 0; {style}">{text}</{tag}>'
'</td></tr></table'
)
class NotifyLetterMarkdownPreviewRenderer(mistune.Renderer):
# TODO if we start removing the dead code detected by
# the vulture tool (such as the parameter 'language' here)
# it will break all the tests. Need to do some massive
# cleanup apparently, although it's not clear why vulture
# only recently started detecting this.
def block_code(self, code, language=None): # noqa
return code
def list_item(self, text, level=None):
return (
'<li style="Margin: 5px 0 5px; padding: 0 0 0 5px; font-size: 19px;'
'line-height: 25px; color: #0B0C0C;">' + text.strip() + '</li>'
)
def link(self, link=None, text=None, title=None, url=None, **kwargs):
href = url or (link if link and link.startswith("http://", "https://") else "")
display_text = text or link or href or ""
title_attr = f' title="{title}"' if title else ""
return f'<a style="{LINK_STYLE}" href="{href}"{title_attr}>{display_text}</a>'
def autolink(self, link, is_email=False):
return create_sanitised_html_for_url(link, style=LINK_STYLE)
def image(self, src, alt="", title=None, url=None):
return ""
def strikethrough(self, text):
return (
'<p style="Margin: 0 0 20px 0; font-size: 19px; line-height: 25px; color: #0B0C0C;">'
f"~~{text}~~"
'</p>'
)
class PlainTextRenderer(mistune.HTMLRenderer):
COLUMN_WIDTH = 65
def heading(self, text, level):
if level == 1:
return f"\n\n\n{text}\n{'-' * self.COLUMN_WIDTH}"
return self.paragraph(text)
def paragraph(self, text):
if text.strip():
return f"\n\n{text}"
return ""
def thematic_break(self):
return f"\n\n{'=' * self.COLUMN_WIDTH}\n"
def heading(self, text, level):
print(f"TEXT {text} LEVEL {level}")
if level == 1:
return f"\n\n\n{text}\n{'-' * self.COLUMN_WIDTH}"
return self.paragraph(text)
def block_quote(self, text):
return text
def header(self, text, level, raw=None): # noqa
if level == 1:
return super().header(text, 2)
def linebreak(self):
return "\n"
def list(self, text, ordered, level=None, **kwargs):
return f"\n{text}"
def list_item(self, text, level=None):
return f"\n{text.strip()}"
def link(self, link=None, text=None, title=None, url=None, **kwargs):
display_text = text or link or url or ""
href = url or link or ""
output = display_text
if title:
output += f" ({title})"
if href:
output += f": {href}"
return output
def autolink(self, link, is_email=False):
return link
def image(self, src, alt="", title=None, url=None):
return ""
def emphasis(self, text):
return f"*{text}*"
def strong(self, text):
return f"**{text}**"
def codespan(self, text):
return f"`{text}`"
def strikethrough(self, text):
return f"~~{text}~~"
class PreheaderRenderer(PlainTextRenderer):
def heading(self, text, level):
return self.paragraph(text)
def hrule(self):
return '<div class="page-break">&nbsp;</div>'
def thematic_break(self):
return ""
def link(self, link, text=None, title=None):
return text or link
def image(self, src, alt="", title=None, url=None):
return ""
class LetterPreviewRenderer(mistune.HTMLRenderer):
def heading(self, text, level):
if level == 1:
return super().heading(text, 2)
return self.paragraph(text)
def paragraph(self, text):
if text.strip():
return "<p>{}</p>".format(text)
return f"<p>{text}</p>"
return ""
def table(self, header, body):
return ""
def link(self, link, text=None, title=None, url=None):
href = url
display_text = text or link
print(f"LINKE {link} URL {url} HREF {href}")
return f"{display_text}: <strong>{href.replace('http://', '').replace('https://', '')}</strong>"
#return f"{text}: {link}"
def autolink(self, link, is_email=False):
return "<strong>{}</strong>".format(
link.replace("http://", "").replace("https://", "")
)
return f"<strong>{link.replace('http://', '')}.replace(https://', '')</strong>"
def image(self, src, title, alt_text): # noqa
def thematic_break(self):
return '<div class="page-break">&nbsp;</div>'
def image(self, src, alt="", title=None, **kwargs):
return ""
def block_quote(self, text):
return text
def list_item(self, text, level=None):
return f"<li>{text.strip()}</li>\n"
def emphasis(self, text):
return f"*{text}*"
def strong(self, text):
return f"**{text}**"
def codespan(self, text):
return f"`{text}`"
def linebreak(self):
return "<br>"
def newline(self):
return self.linebreak()
def list_item(self, text):
return "<li>{}</li>\n".format(text.strip())
def link(self, link, title, content):
return "{}: {}".format(content, self.autolink(link))
def footnote_ref(self, key, index):
return ""
def footnote_item(self, key, text):
return text
def footnotes(self, text):
return text
return "<br>"
class NotifyEmailMarkdownRenderer(NotifyLetterMarkdownPreviewRenderer):
def header(self, text, level, raw=None): # noqa
if level == 1:
return (
'<h2 style="Margin: 0 0 20px 0; padding: 0; '
'font-size: 27px; line-height: 35px; font-weight: bold; color: #0B0C0C;">'
"{}"
"</h2>"
).format(text)
return self.paragraph(text)
def hrule(self):
return '<hr style="border: 0; height: 1px; background: #B1B4B6; Margin: 30px 0 30px 0;">'
def linebreak(self):
return "<br />"
def list(self, body, ordered=True):
return (
(
'<table role="presentation" style="padding: 0 0 20px 0;">'
"<tr>"
'<td style="font-family: Helvetica, Arial, sans-serif;">'
'<ol style="Margin: 0 0 0 20px; padding: 0; list-style-type: decimal;">'
"{}"
"</ol>"
"</td>"
"</tr>"
"</table>"
).format(body)
if ordered
else (
'<table role="presentation" style="padding: 0 0 20px 0;">'
"<tr>"
'<td style="font-family: Helvetica, Arial, sans-serif;">'
'<ul style="Margin: 0 0 0 20px; padding: 0; list-style-type: disc;">'
"{}"
"</ul>"
"</td>"
"</tr>"
"</table>"
).format(body)
)
def list_item(self, text):
return (
'<li style="Margin: 5px 0 5px; padding: 0 0 0 5px; font-size: 19px;'
'line-height: 25px; color: #0B0C0C;">'
"{}"
"</li>"
).format(text.strip())
def paragraph(self, text):
if text.strip():
return (
'<p style="Margin: 0 0 20px 0; font-size: 19px; line-height: 25px; color: #0B0C0C;">{}</p>'
).format(text)
return ""
def block_quote(self, text):
return (
"<blockquote "
'style="Margin: 0 0 20px 0; border-left: 10px solid #B1B4B6;'
'padding: 15px 0 0.1px 15px; font-size: 19px; line-height: 25px;"'
">"
"{}"
"</blockquote>"
).format(text)
def link(self, link, title, content):
return ('<a style="{}"{}{}>{}</a>').format(
LINK_STYLE,
' href="{}"'.format(link),
' title="{}"'.format(title) if title else "",
content,
)
def autolink(self, link, is_email=False):
if is_email:
return link
return create_sanitised_html_for_url(link, style=LINK_STYLE)
class NotifyPlainTextEmailMarkdownRenderer(NotifyEmailMarkdownRenderer):
COLUMN_WIDTH = 65
def header(self, text, level, raw=None): # noqa
if level == 1:
return "".join(
(
self.linebreak() * 3,
text,
self.linebreak(),
"-" * self.COLUMN_WIDTH,
)
)
return self.paragraph(text)
def hrule(self):
return self.paragraph("=" * self.COLUMN_WIDTH)
def linebreak(self):
return "\n"
def list(self, body, ordered=True):
def _get_list_marker():
decimal = count(1)
return lambda _: "{}.".format(next(decimal)) if ordered else ""
return "".join(
(
self.linebreak(),
re.sub(
magic_sequence_regex,
_get_list_marker(),
body,
),
)
)
def list_item(self, text):
return "".join(
(
self.linebreak(),
MAGIC_SEQUENCE,
" ",
text.strip(),
)
)
def paragraph(self, text):
if text.strip():
return "".join(
(
self.linebreak() * 2,
text,
)
)
return ""
def block_quote(self, text):
return text
def link(self, link, title, content):
return "".join(
(
content,
" ({})".format(title) if title else "",
": ",
link,
)
)
def autolink(self, link, is_email=False): # noqa
return link
class NotifyEmailPreheaderMarkdownRenderer(NotifyPlainTextEmailMarkdownRenderer):
def header(self, text, level, raw=None): # noqa
return self.paragraph(text)
def hrule(self):
return ""
def link(self, link, title, content):
return "".join(
(
content,
" ({})".format(title) if title else "",
)
)
notify_email_markdown = mistune.Markdown(
renderer=NotifyEmailMarkdownRenderer(),
hard_wrap=True,
use_xhtml=False,
)
notify_plain_text_email_markdown = mistune.Markdown(
renderer=NotifyPlainTextEmailMarkdownRenderer(),
hard_wrap=True,
)
notify_email_preheader_markdown = mistune.Markdown(
renderer=NotifyEmailPreheaderMarkdownRenderer(),
hard_wrap=True,
)
notify_letter_preview_markdown = mistune.Markdown(
renderer=NotifyLetterMarkdownPreviewRenderer(),
hard_wrap=True,
use_xhtml=False,
)
notify_email_markdown = mistune.create_markdown(renderer=EmailRenderer())
notify_letter_preview_markdown = mistune.create_markdown(renderer=LetterPreviewRenderer())
notify_email_preheader_markdown = mistune.create_markdown(renderer=PreheaderRenderer())
notify_plain_text_email_markdown=mistune.create_markdown(renderer=PlainTextRenderer())