Skip to content

Commit 4bf5ec0

Browse files
committed
fix: improve thread safety for markdown rendering in email notifications
1 parent a581156 commit 4bf5ec0

1 file changed

Lines changed: 58 additions & 19 deletions

File tree

notifications_utils/formatters.py

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import html
22
import re
33
import string
4+
import threading
45
import urllib
56
from itertools import count
6-
from typing import List
7+
from typing import Callable, List
78

89
import bleach
910
import mistune
@@ -619,24 +620,62 @@ def link(self, link, title, content):
619620
)
620621

621622

622-
notify_email_markdown = mistune.Markdown(
623-
renderer=NotifyEmailMarkdownRenderer(),
624-
hard_wrap=True,
625-
use_xhtml=False,
626-
)
627-
notify_plain_text_email_markdown = mistune.Markdown(
628-
renderer=NotifyPlainTextEmailMarkdownRenderer(),
629-
hard_wrap=True,
630-
)
631-
notify_email_preheader_markdown = mistune.Markdown(
632-
renderer=NotifyEmailPreheaderMarkdownRenderer(),
633-
hard_wrap=True,
634-
)
635-
notify_letter_preview_markdown = mistune.Markdown(
636-
renderer=NotifyLetterMarkdownPreviewRenderer(),
637-
hard_wrap=True,
638-
use_xhtml=False,
639-
)
623+
_markdown_local = threading.local()
624+
625+
626+
def _get_thread_local_markdown(name: str, factory: Callable[[], mistune.Markdown]) -> mistune.Markdown:
627+
# Mistune 0.8 markdown instances keep mutable parse state and are not thread-safe.
628+
parser = getattr(_markdown_local, name, None)
629+
if parser is None:
630+
parser = factory()
631+
setattr(_markdown_local, name, parser)
632+
return parser
633+
634+
635+
def _build_notify_email_markdown() -> mistune.Markdown:
636+
return mistune.Markdown(
637+
renderer=NotifyEmailMarkdownRenderer(),
638+
hard_wrap=True,
639+
use_xhtml=False,
640+
)
641+
642+
643+
def _build_notify_plain_text_email_markdown() -> mistune.Markdown:
644+
return mistune.Markdown(
645+
renderer=NotifyPlainTextEmailMarkdownRenderer(),
646+
hard_wrap=True,
647+
)
648+
649+
650+
def _build_notify_email_preheader_markdown() -> mistune.Markdown:
651+
return mistune.Markdown(
652+
renderer=NotifyEmailPreheaderMarkdownRenderer(),
653+
hard_wrap=True,
654+
)
655+
656+
657+
def _build_notify_letter_preview_markdown() -> mistune.Markdown:
658+
return mistune.Markdown(
659+
renderer=NotifyLetterMarkdownPreviewRenderer(),
660+
hard_wrap=True,
661+
use_xhtml=False,
662+
)
663+
664+
665+
def notify_email_markdown(text):
666+
return _get_thread_local_markdown("notify_email_markdown", _build_notify_email_markdown)(text)
667+
668+
669+
def notify_plain_text_email_markdown(text):
670+
return _get_thread_local_markdown("notify_plain_text_email_markdown", _build_notify_plain_text_email_markdown)(text)
671+
672+
673+
def notify_email_preheader_markdown(text):
674+
return _get_thread_local_markdown("notify_email_preheader_markdown", _build_notify_email_preheader_markdown)(text)
675+
676+
677+
def notify_letter_preview_markdown(text):
678+
return _get_thread_local_markdown("notify_letter_preview_markdown", _build_notify_letter_preview_markdown)(text)
640679

641680

642681
def escape_lang_tags(_content: str) -> str:

0 commit comments

Comments
 (0)