text: import re
  from functools import lru_cache, partial
  
  import bleach
  import mistune
  from bleach.linkifier import LinkifyFilter
  
  LINK_REL = "nofollow noopener"
  
  MARKDOWN_TAGS = [
      "a",
      "blockquote",
      "code",
      "em",
      "hr",
      "h1",
      "h2",
      "h3",
      "h4",
      "h5",
      "h6",
      "img",
      "li",
      "ol",
      "p",
      "pre",
      "strong",
      "ul",
  ]
  ALLOWED_TAGS = set(bleach.ALLOWED_TAGS + MARKDOWN_TAGS)
  
  
  def _filter_link_attributes(_tag, name, value):
      if name in ["href", "title"]:
          return True
  
      if name == "target" and value == "_blank":
          return True
  
      if name == "rel" and value == LINK_REL:
          return True
  
      return False
  
  
  MARKDOWN_ATTRIBUTES = {"a": _filter_link_attributes, "img": ["alt", "src", "title"]}
  
  ALLOWED_ATTRIBUTES = bleach.ALLOWED_ATTRIBUTES.copy()
  ALLOWED_ATTRIBUTES.update(MARKDOWN_ATTRIBUTES)
  
  
  class MathMarkdown(mistune.Markdown):
      def output_block_math(self):
          return self.renderer.block_math(self.token["text"])
  
  
  class MathInlineLexer(mistune.InlineLexer):
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
          self.rules.inline_math = re.compile(r"\\\((.*?)\\\)", re.DOTALL)
          self.default_rules.insert(0, "inline_math")
  
      def output_inline_math(self, match):
          return self.renderer.inline_math(match.group(1))
  
  
  class MathBlockLexer(mistune.BlockLexer):
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
          self.rules.block_math = re.compile(r"^\$\$(.*?)\$\$", re.DOTALL)
          self.default_rules.insert(0, "block_math")
  
      def parse_block_math(self, match):
          self.tokens.append({"type": "block_math", "text": match.group(1)})
  
  
  class MathRenderer(mistune.Renderer):
      def block_math(self, text):  # pylint: disable=no-self-use
          return f"<p>$${text}$$</p>\n"
  
      def inline_math(self, text):  # pylint: disable=no-self-use
          return f"\\({text}\\)"
  
  
  def render(text):
      if text is not None:
          return _sanitize(_get_markdown()(text))
  
      return None
  
  
  def _sanitize(text):
      return _get_cleaner().clean(text)
  
  
  def _linkify_target_blank(attrs, new=False):  # pylint: disable=unused-argument
      # FIXME: when bleach>2.0.0 is released we can use
      # bleach.callbacks.target_blank instead of this function. We have our own
      # copy to work around a bug in 2.0.0:
      #
      #   https://github.com/mozilla/bleach/commit/b23c74c1ca5ffcbd308df93e79487fa92a6eb4a7
      #
      href_key = (None, "href")
  
      if href_key not in attrs:
          return attrs
  
      if attrs[href_key].startswith("mailto:"):
          return attrs
  
      attrs[(None, "target")] = "_blank"
      return attrs
  
  
  def _linkify_rel(attrs, new=False):  # pylint: disable=unused-argument
      href_key = (None, "href")
  
      if href_key not in attrs:
          return attrs
  
      if attrs[href_key].startswith("mailto:"):
          return attrs
  
      attrs[(None, "rel")] = LINK_REL
      return attrs
  
  
  @lru_cache(maxsize=None)
  def _get_cleaner():
      linkify_filter = partial(
          LinkifyFilter, callbacks=[_linkify_target_blank, _linkify_rel]
      )
      cleaner = bleach.Cleaner(
          tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES, filters=[linkify_filter]
      )
      return cleaner
  
  
  @lru_cache(maxsize=None)
  def _get_markdown():
      return MathMarkdown(
          renderer=MathRenderer(),
          inline=MathInlineLexer,
          block=MathBlockLexer,
          escape=True,
      )
  