diff --git a/.gitignore b/.gitignore
index 6de88e389..456d05c3c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -64,6 +64,7 @@ target/
.python-version
# virtualenv
+.venv/
venv/
ENV/
diff --git a/.spell-dict b/.spell-dict
index 91cd7bd10..13cdea5b1 100644
--- a/.spell-dict
+++ b/.spell-dict
@@ -69,6 +69,7 @@ md
merchantability
metadata
MkDocs
+mkdocstrings
multi
MultiMarkdown
munge
@@ -102,6 +103,7 @@ refactor
refactored
refactors
registerExtension
+resettable
richeland
RSS
rST
@@ -126,6 +128,7 @@ stdout
Stelios
Stienstra
subclasses
+submodules
SuperFences
svn
Swartz
diff --git a/docs/changelog.md b/docs/changelog.md
index d233abfda..21c464dd2 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -27,6 +27,8 @@ links to the header, placing them after all other header content.
* Refactor changelog to use the format defined at
Defined Value:
+ {%+ filter highlight(language="python", inline=False) %} +{{ attribute.source }} + {% endfilter %} + {% endif %} +{% endblock contents %} diff --git a/docs/templates/python/nature/class.html b/docs/templates/python/nature/class.html new file mode 100644 index 000000000..7193c3419 --- /dev/null +++ b/docs/templates/python/nature/class.html @@ -0,0 +1,6 @@ +{% extends "_base/class.html" %} + +{% block heading scoped %} + ‹› + {{ super() }} +{% endblock heading %} diff --git a/docs/templates/python/nature/docstring/admonition.html b/docs/templates/python/nature/docstring/admonition.html new file mode 100644 index 000000000..14ec034aa --- /dev/null +++ b/docs/templates/python/nature/docstring/admonition.html @@ -0,0 +1,5 @@ +{{ log.debug("Rendering admonition") }} +{{ section.title|convert_markdown(heading_level, html_id, strip_paragraph=True) }}
+ {{ section.value.contents|convert_markdown(heading_level, html_id) }} +` element containing the matching text. """
+ """ Return a `` element containing the escaped matching text. """
def __init__(self, pattern):
InlineProcessor.__init__(self, pattern)
self.ESCAPED_BSLASH = '{}{}{}'.format(util.STX, ord('\\'), util.ETX)
self.tag = 'code'
+ """ The tag of the rendered element. """
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element | str, int, int]:
+ """
+ If the match contains `group(3)` of a pattern, then return a `code`
+ [`Element`][xml.etree.ElementTree.Element] which contains HTML escaped text (with
+ [`code_escape`][markdown.util.code_escape]) as an [`AtomicString`][markdown.util.AtomicString].
+
+ If the match does not contain `group(3)` then return the text of `group(1)` backslash escaped.
+
+ """
if m.group(3):
el = etree.Element(self.tag)
el.text = util.AtomicString(util.code_escape(m.group(3).strip()))
@@ -387,7 +456,12 @@ class DoubleTagPattern(SimpleTagPattern): # pragma: no cover
Useful for strong emphasis etc.
"""
- def handleMatch(self, m):
+ def handleMatch(self, m: Match) -> etree.Element:
+ """
+ Return [`Element`][xml.etree.ElementTree.Element] in following format:
+ `group(3) group(4)` where `group(4)` is optional.
+
+ """
tag1, tag2 = self.tag.split(",")
el1 = etree.Element(tag1)
el2 = etree.SubElement(el1, tag2)
@@ -403,7 +477,12 @@ class DoubleTagInlineProcessor(SimpleTagInlineProcessor):
Useful for strong emphasis etc.
"""
- def handleMatch(self, m, data): # pragma: no cover
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element, int, int]: # pragma: no cover
+ """
+ Return [`Element`][xml.etree.ElementTree.Element] in following format:
+ `group(2) group(3)` where `group(3)` is optional.
+
+ """
tag1, tag2 = self.tag.split(",")
el1 = etree.Element(tag1)
el2 = etree.SubElement(el1, tag2)
@@ -415,7 +494,8 @@ def handleMatch(self, m, data): # pragma: no cover
class HtmlInlineProcessor(InlineProcessor):
""" Store raw inline html and return a placeholder. """
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[str, int, int]:
+ """ Store the text of `group(1)` of a pattern and return a placeholder string. """
rawhtml = self.backslash_unescape(self.unescape(m.group(1)))
place_holder = self.md.htmlStash.store(rawhtml)
return place_holder, m.start(0), m.end(0)
@@ -461,6 +541,7 @@ class AsteriskProcessor(InlineProcessor):
EmStrongItem(re.compile(STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'),
EmStrongItem(re.compile(EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em')
]
+ """ The various strong and emphasis patterns handled by this processor. """
def build_single(self, m, tag, idx):
"""Return single tag."""
@@ -570,7 +651,7 @@ def build_element(self, m, builder, tags, index):
else:
return self.build_single(m, tags, index)
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element, int, int]:
"""Parse patterns."""
el = None
@@ -597,6 +678,7 @@ class UnderscoreProcessor(AsteriskProcessor):
EmStrongItem(re.compile(SMART_STRONG_RE, re.DOTALL | re.UNICODE), 'single', 'strong'),
EmStrongItem(re.compile(SMART_EMPHASIS_RE, re.DOTALL | re.UNICODE), 'single', 'em')
]
+ """ The various strong and emphasis patterns handled by this processor. """
class LinkInlineProcessor(InlineProcessor):
@@ -604,7 +686,8 @@ class LinkInlineProcessor(InlineProcessor):
RE_LINK = re.compile(r'''\(\s*(?:(<[^<>]*>)\s*(?:('[^']*'|"[^"]*")\s*)?\))?''', re.DOTALL | re.UNICODE)
RE_TITLE_CLEAN = re.compile(r'\s')
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element | None, int | None, int | None]:
+ """ Return an `a` [`Element`][xml.etree.ElementTree.Element] or `(None, None, None)`. """
text, index, handled = self.getText(data, m.end(0))
if not handled:
@@ -763,7 +846,8 @@ def getText(self, data, index):
class ImageInlineProcessor(LinkInlineProcessor):
""" Return a `img` element from the given match. """
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element | None, int | None, int | None]:
+ """ Return an `img` [`Element`][xml.etree.ElementTree.Element] or `(None, None, None)`. """
text, index, handled = self.getText(data, m.end(0))
if not handled:
return None, None, None
@@ -789,7 +873,11 @@ class ReferenceInlineProcessor(LinkInlineProcessor):
RE_LINK = re.compile(r'\s?\[([^\]]*)\]', re.DOTALL | re.UNICODE)
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element | None, int | None, int | None]:
+ """
+ Return [`Element`][xml.etree.ElementTree.Element] returned by `makeTag` method or `(None, None, None)`.
+
+ """
text, index, handled = self.getText(data, m.end(0))
if not handled:
return None, None, None
@@ -809,9 +897,9 @@ def handleMatch(self, m, data):
def evalId(self, data, index, text):
"""
- Evaluate the id portion of [ref][id].
+ Evaluate the id portion of `[ref][id]`.
- If [ref][] use [ref].
+ If `[ref][]` use `[ref]`.
"""
m = self.RE_LINK.match(data, pos=index)
if not m:
@@ -823,7 +911,8 @@ def evalId(self, data, index, text):
id = text.lower()
return id, end, True
- def makeTag(self, href, title, text):
+ def makeTag(self, href: str, title: str, text: str) -> etree.Element:
+ """ Return an `a` [`Element`][xml.etree.ElementTree.Element]. """
el = etree.Element('a')
el.set('href', href)
@@ -835,16 +924,17 @@ def makeTag(self, href, title, text):
class ShortReferenceInlineProcessor(ReferenceInlineProcessor):
- """Short form of reference: [google]. """
+ """Short form of reference: `[google]`. """
def evalId(self, data, index, text):
- """Evaluate the id from of [ref] """
+ """Evaluate the id of `[ref]`. """
return text.lower(), index, True
class ImageReferenceInlineProcessor(ReferenceInlineProcessor):
""" Match to a stored reference and return `img` element. """
- def makeTag(self, href, title, text):
+ def makeTag(self, href: str, title: str, text: str) -> etree.Element:
+ """ Return an `img` [`Element`][xml.etree.ElementTree.Element]. """
el = etree.Element("img")
el.set("src", href)
if title:
@@ -856,14 +946,15 @@ def makeTag(self, href, title, text):
class ShortImageReferenceInlineProcessor(ImageReferenceInlineProcessor):
""" Short form of image reference: `![ref]`. """
def evalId(self, data, index, text):
- """Evaluate the id from of [ref] """
+ """Evaluate the id of `[ref]`. """
return text.lower(), index, True
class AutolinkInlineProcessor(InlineProcessor):
""" Return a link Element given an auto-link (``). """
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element, int, int]:
+ """ Return an `a` [`Element`][xml.etree.ElementTree.Element] of `group(1)`. """
el = etree.Element("a")
el.set('href', self.unescape(m.group(1)))
el.text = util.AtomicString(m.group(1))
@@ -874,7 +965,8 @@ class AutomailInlineProcessor(InlineProcessor):
"""
Return a `mailto` link Element given an auto-mail link (``).
"""
- def handleMatch(self, m, data):
+ def handleMatch(self, m: Match, data: str) -> tuple[etree.Element, int, int]:
+ """ Return an [`Element`][xml.etree.ElementTree.Element] containing a `mailto` link of `group(1)`. """
el = etree.Element('a')
email = self.unescape(m.group(1))
if email.startswith("mailto:"):
diff --git a/markdown/postprocessors.py b/markdown/postprocessors.py
index 0e89d406f..4a18e18ee 100644
--- a/markdown/postprocessors.py
+++ b/markdown/postprocessors.py
@@ -1,38 +1,43 @@
-"""
-Python Markdown
+# Python Markdown
-A Python implementation of John Gruber's Markdown.
+# A Python implementation of John Gruber's Markdown.
-Documentation: https://python-markdown.github.io/
-GitHub: https://github.com/Python-Markdown/markdown/
-PyPI: https://pypi.org/project/Markdown/
+# Documentation: https://python-markdown.github.io/
+# GitHub: https://github.com/Python-Markdown/markdown/
+# PyPI: https://pypi.org/project/Markdown/
-Started by Manfred Stienstra (http://www.dwerg.net/).
-Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
-Currently maintained by Waylan Limberg (https://github.com/waylan),
-Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+# Started by Manfred Stienstra (http://www.dwerg.net/).
+# Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+# Currently maintained by Waylan Limberg (https://github.com/waylan),
+# Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
+# Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
+# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+# Copyright 2004 Manfred Stienstra (the original version)
-License: BSD (see LICENSE.md for details).
+# License: BSD (see LICENSE.md for details).
-POST-PROCESSORS
-=============================================================================
+"""
-Markdown also allows post-processors, which are similar to preprocessors in
-that they need to implement a "run" method. However, they are run after core
-processing.
+Post-processors run on the text of the entire document after is has been serialized into a string.
+Postprocessors should be used to work with the text just before output. Usually, they are used add
+back sections that were extracted in a preprocessor, fix up outgoing encodings, or wrap the whole
+document.
"""
+from __future__ import annotations
+
from collections import OrderedDict
+from typing import TYPE_CHECKING, Any
from . import util
import re
+if TYPE_CHECKING: # pragma: no cover
+ from markdown import Markdown
-def build_postprocessors(md, **kwargs):
+
+def build_postprocessors(md: Markdown, **kwargs: Any) -> util.Registry:
""" Build the default postprocessors for Markdown. """
postprocessors = util.Registry()
postprocessors.register(RawHtmlPostprocessor(md), 'raw_html', 30)
@@ -44,16 +49,16 @@ class Postprocessor(util.Processor):
"""
Postprocessors are run after the ElementTree it converted back into text.
- Each Postprocessor implements a "run" method that takes a pointer to a
+ Each Postprocessor implements a `run` method that takes a pointer to a
text string, modifies it as necessary and returns a text string.
- Postprocessors must extend markdown.Postprocessor.
+ Postprocessors must extend `Postprocessor`.
"""
- def run(self, text):
+ def run(self, text: str) -> str:
"""
- Subclasses of Postprocessor should implement a `run` method, which
+ Subclasses of `Postprocessor` should implement a `run` method, which
takes the html document as a single text string and returns a
(possibly modified) string.
@@ -66,7 +71,7 @@ class RawHtmlPostprocessor(Postprocessor):
BLOCK_LEVEL_REGEX = re.compile(r'^\<\/?([^ >]+)')
- def run(self, text):
+ def run(self, text: str):
""" Iterate over html stash and restore html. """
replacements = OrderedDict()
for i in range(self.md.htmlStash.html_counter):
@@ -99,7 +104,8 @@ def substitute_match(m):
else:
return self.run(processed_text)
- def isblocklevel(self, html):
+ def isblocklevel(self, html: str) -> bool:
+ """ Check is block of HTML is block-level. """
m = self.BLOCK_LEVEL_REGEX.match(html)
if m:
if m.group(1)[0] in ('!', '?', '@', '%'):
@@ -108,7 +114,7 @@ def isblocklevel(self, html):
return self.md.is_block_level(m.group(1))
return False
- def stash_to_string(self, text):
+ def stash_to_string(self, text: str) -> str:
""" Convert a stashed object to a string. """
return str(text)
@@ -122,11 +128,11 @@ def run(self, text):
@util.deprecated(
- "This class will be removed in the future; "
- "use 'treeprocessors.UnescapeTreeprocessor' instead."
+ "This class is deprecated and will be removed in the future; "
+ "use [`UnescapeTreeprocessor`][markdown.treeprocessors.UnescapeTreeprocessor] instead."
)
class UnescapePostprocessor(Postprocessor):
- """ Restore escaped chars """
+ """ Restore escaped chars. """
RE = re.compile(r'{}(\d+){}'.format(util.STX, util.ETX))
diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py
index e1023c59a..8787e56bd 100644
--- a/markdown/preprocessors.py
+++ b/markdown/preprocessors.py
@@ -1,37 +1,41 @@
-"""
-Python Markdown
-
-A Python implementation of John Gruber's Markdown.
+# Python Markdown
-Documentation: https://python-markdown.github.io/
-GitHub: https://github.com/Python-Markdown/markdown/
-PyPI: https://pypi.org/project/Markdown/
+# A Python implementation of John Gruber's Markdown.
-Started by Manfred Stienstra (http://www.dwerg.net/).
-Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
-Currently maintained by Waylan Limberg (https://github.com/waylan),
-Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+# Documentation: https://python-markdown.github.io/
+# GitHub: https://github.com/Python-Markdown/markdown/
+# PyPI: https://pypi.org/project/Markdown/
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
+# Started by Manfred Stienstra (http://www.dwerg.net/).
+# Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+# Currently maintained by Waylan Limberg (https://github.com/waylan),
+# Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-License: BSD (see LICENSE.md for details).
+# Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
+# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+# Copyright 2004 Manfred Stienstra (the original version)
-PRE-PROCESSORS
-=============================================================================
+# License: BSD (see LICENSE.md for details).
-Preprocessors work on source text before we start doing anything too
-complicated.
"""
+Preprocessors work on source text before it is broken down into its individual parts.
+This is an excellent place to clean up bad characters or to extract portions for later
+processing that the parser may otherwise choke on.
+"""
+
+from __future__ import annotations
+from typing import TYPE_CHECKING, Any
from . import util
from .htmlparser import HTMLExtractor
import re
+if TYPE_CHECKING: # pragma: no cover
+ from markdown import Markdown
-def build_preprocessors(md, **kwargs):
- """ Build the default set of preprocessors used by Markdown. """
+
+def build_preprocessors(md: Markdown, **kwargs: Any) -> util.Registry:
+ """ Build and return the default set of preprocessors used by Markdown. """
preprocessors = util.Registry()
preprocessors.register(NormalizeWhitespace(md), 'normalize_whitespace', 30)
preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20)
@@ -42,16 +46,16 @@ class Preprocessor(util.Processor):
"""
Preprocessors are run after the text is broken into lines.
- Each preprocessor implements a "run" method that takes a pointer to a
+ Each preprocessor implements a `run` method that takes a pointer to a
list of lines of the document, modifies it as necessary and returns
either the same pointer or a pointer to a new list.
- Preprocessors must extend markdown.Preprocessor.
+ Preprocessors must extend `Preprocessor`.
"""
- def run(self, lines):
+ def run(self, lines: list[str]) -> list[str]:
"""
- Each subclass of Preprocessor should override the `run` method, which
+ Each subclass of `Preprocessor` should override the `run` method, which
takes the document as a list of strings split by newlines and returns
the (possibly modified) list of lines.
@@ -62,7 +66,7 @@ def run(self, lines):
class NormalizeWhitespace(Preprocessor):
""" Normalize whitespace for consistent parsing. """
- def run(self, lines):
+ def run(self, lines: list[str]) -> list[str]:
source = '\n'.join(lines)
source = source.replace(util.STX, "").replace(util.ETX, "")
source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n"
@@ -72,9 +76,14 @@ def run(self, lines):
class HtmlBlockPreprocessor(Preprocessor):
- """Remove html blocks from the text and store them for later retrieval."""
+ """
+ Remove html blocks from the text and store them for later retrieval.
+
+ The raw HTML is stored in the [`htmlStash`][markdown.util.HtmlStash] of the
+ [`Markdown`][markdown.Markdown] instance.
+ """
- def run(self, lines):
+ def run(self, lines: list[str]) -> list[str]:
source = '\n'.join(lines)
parser = HTMLExtractor(self.md)
parser.feed(source)
diff --git a/markdown/serializers.py b/markdown/serializers.py
index b5dcd11d0..5a8818e28 100644
--- a/markdown/serializers.py
+++ b/markdown/serializers.py
@@ -34,8 +34,19 @@
# OF THIS SOFTWARE.
# --------------------------------------------------------------------
+"""
+Python-Markdown provides two serializers which render [`ElementTree.Element`][xml.etree.ElementTree.Element]
+objects to a string of HTML. Both functions wrap the same underlying code with only a few minor
+differences as outlined below:
+
+1. Empty (self-closing) tags are rendered as `` for HTML and as ` ` for XHTML.
+2. Boolean attributes are rendered as `attrname` for HTML and as `attrname="attrname"` for XHTML.
+"""
+
+from __future__ import annotations
+
from xml.etree.ElementTree import ProcessingInstruction
-from xml.etree.ElementTree import Comment, ElementTree, QName, HTML_EMPTY
+from xml.etree.ElementTree import Comment, ElementTree, Element, QName, HTML_EMPTY
import re
__all__ = ['to_html_string', 'to_xhtml_string']
@@ -171,9 +182,12 @@ def _write_html(root, format="html"):
# --------------------------------------------------------------------
# public functions
-def to_html_string(element):
+
+def to_html_string(element: Element) -> str:
+ """ Serialize element and its children to a string of HTML5. """
return _write_html(ElementTree(element).getroot(), format="html")
-def to_xhtml_string(element):
+def to_xhtml_string(element: Element) -> str:
+ """ Serialize element and its children to a string of XHTML. """
return _write_html(ElementTree(element).getroot(), format="xhtml")
diff --git a/markdown/test_tools.py b/markdown/test_tools.py
index c4db6b22c..895e44ec5 100644
--- a/markdown/test_tools.py
+++ b/markdown/test_tools.py
@@ -1,28 +1,31 @@
-"""
-Python Markdown
+# Python Markdown
-A Python implementation of John Gruber's Markdown.
+# A Python implementation of John Gruber's Markdown.
-Documentation: https://python-markdown.github.io/
-GitHub: https://github.com/Python-Markdown/markdown/
-PyPI: https://pypi.org/project/Markdown/
+# Documentation: https://python-markdown.github.io/
+# GitHub: https://github.com/Python-Markdown/markdown/
+# PyPI: https://pypi.org/project/Markdown/
-Started by Manfred Stienstra (http://www.dwerg.net/).
-Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
-Currently maintained by Waylan Limberg (https://github.com/waylan),
-Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+# Started by Manfred Stienstra (http://www.dwerg.net/).
+# Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+# Currently maintained by Waylan Limberg (https://github.com/waylan),
+# Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
+# Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
+# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+# Copyright 2004 Manfred Stienstra (the original version)
-License: BSD (see LICENSE.md for details).
-"""
+# License: BSD (see LICENSE.md for details).
+
+""" A collection of tools for testing the Markdown code base and extensions. """
+
+from __future__ import annotations
import os
import sys
import unittest
import textwrap
+from typing import Any
from . import markdown, Markdown, util
try:
@@ -35,9 +38,9 @@
class TestCase(unittest.TestCase):
"""
- A `unittest.TestCase` subclass with helpers for testing Markdown output.
+ A [`unittest.TestCase`][] subclass with helpers for testing Markdown output.
- Define `default_kwargs` as a dict of keywords to pass to Markdown for each
+ Define `default_kwargs` as a `dict` of keywords to pass to Markdown for each
test. The defaults can be overridden on individual tests.
The `assertMarkdownRenders` method accepts the source text, the expected
@@ -52,13 +55,14 @@ class TestCase(unittest.TestCase):
In all other respects, behaves as `unittest.TestCase`.
"""
- default_kwargs = {}
+ default_kwargs: dict[str, Any] = {}
+ """ Default options to pass to Markdown for each test. """
def assertMarkdownRenders(self, source, expected, expected_attrs=None, **kwargs):
"""
Test that source Markdown text renders to expected output with given keywords.
- `expected_attrs` accepts a dict. Each key should be the name of an attribute
+ `expected_attrs` accepts a `dict`. Each key should be the name of an attribute
on the `Markdown` instance and the value should be the expected value after
the source text is parsed by Markdown. After the expected output is tested,
the expected value for each attribute is compared against the actual
@@ -93,10 +97,12 @@ class recursionlimit:
Example usage:
- with recursionlimit(20):
- # test code here
+ ``` python
+ with recursionlimit(20):
+ # test code here
+ ```
- See https://stackoverflow.com/a/50120316/866026
+ See .
"""
def __init__(self, limit):
@@ -116,12 +122,12 @@ def __exit__(self, type, value, tb):
class Kwargs(dict):
- """ A dict like class for holding keyword arguments. """
+ """ A `dict` like class for holding keyword arguments. """
pass
def _normalize_whitespace(text):
- """ Normalize whitespace for a string of html using `tidylib`. """
+ """ Normalize whitespace for a string of HTML using `tidylib`. """
output, errors = tidylib.tidy_fragment(text, options={
'drop_empty_paras': 0,
'fix_backslash': 0,
@@ -189,22 +195,20 @@ def test(self):
class LegacyTestCase(unittest.TestCase, metaclass=LegacyTestMeta):
"""
- A `unittest.TestCase` subclass for running Markdown's legacy file-based tests.
+ A [`unittest.TestCase`][] subclass for running Markdown's legacy file-based tests.
A subclass should define various properties which point to a directory of
text-based test files and define various behaviors/defaults for those tests.
The following properties are supported:
- `location`: A path to the directory of test files. An absolute path is preferred.
- `exclude`: A list of tests to exclude. Each test name should comprise the filename
- without an extension.
- `normalize`: A boolean value indicating if the HTML should be normalized.
- Default: `False`.
- `input_ext`: A string containing the file extension of input files. Default: `.txt`.
- `output_ext`: A string containing the file extension of expected output files.
- Default: `html`.
- `default_kwargs`: A `Kwargs` instance which stores the default set of keyword
- arguments for all test files in the directory.
+ Attributes:
+ location (str): A path to the directory of test files. An absolute path is preferred.
+ exclude (list[str]): A list of tests to exclude. Each test name should comprise the filename
+ without an extension.
+ normalize (bool): A boolean value indicating if the HTML should be normalized. Default: `False`.
+ input_ext (str): A string containing the file extension of input files. Default: `.txt`.
+ output_ext (str): A string containing the file extension of expected output files. Default: `html`.
+ default_kwargs (Kwargs[str, Any]): The default set of keyword arguments for all test files in the directory.
In addition, properties can be defined for each individual set of test files within
the directory. The property should be given the name of the file without the file
diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py
index c779d4d7b..fceb18814 100644
--- a/markdown/treeprocessors.py
+++ b/markdown/treeprocessors.py
@@ -1,31 +1,42 @@
-"""
-Python Markdown
+# Python Markdown
+
+# A Python implementation of John Gruber's Markdown.
-A Python implementation of John Gruber's Markdown.
+# Documentation: https://python-markdown.github.io/
+# GitHub: https://github.com/Python-Markdown/markdown/
+# PyPI: https://pypi.org/project/Markdown/
-Documentation: https://python-markdown.github.io/
-GitHub: https://github.com/Python-Markdown/markdown/
-PyPI: https://pypi.org/project/Markdown/
+# Started by Manfred Stienstra (http://www.dwerg.net/).
+# Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+# Currently maintained by Waylan Limberg (https://github.com/waylan),
+# Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Started by Manfred Stienstra (http://www.dwerg.net/).
-Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
-Currently maintained by Waylan Limberg (https://github.com/waylan),
-Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+# Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
+# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+# Copyright 2004 Manfred Stienstra (the original version)
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
+# License: BSD (see LICENSE.md for details).
-License: BSD (see LICENSE.md for details).
"""
+Tree processors manipulate the tree created by block processors. They can even create an entirely
+new `ElementTree` object. This is an excellent place for creating summaries, adding collected
+references, or last minute adjustments.
+
+"""
+
+from __future__ import annotations
import re
import xml.etree.ElementTree as etree
+from typing import TYPE_CHECKING, Sequence, Any
from . import util
from . import inlinepatterns
+if TYPE_CHECKING: # pragma: no cover
+ from markdown import Markdown
-def build_treeprocessors(md, **kwargs):
+
+def build_treeprocessors(md: Markdown, **kwargs: Any) -> util.Registry:
""" Build the default `treeprocessors` for Markdown. """
treeprocessors = util.Registry()
treeprocessors.register(InlineProcessor(md), 'inline', 20)
@@ -34,8 +45,8 @@ def build_treeprocessors(md, **kwargs):
return treeprocessors
-def isString(s):
- """ Check if it's string """
+def isString(s: Any) -> bool:
+ """ Return `True` if object is a string but not an [`AtomicString`][markdown.util.AtomicString]. """
if not isinstance(s, util.AtomicString):
return isinstance(s, str)
return False
@@ -46,17 +57,16 @@ class Treeprocessor(util.Processor):
`Treeprocessor`s are run on the `ElementTree` object before serialization.
Each `Treeprocessor` implements a `run` method that takes a pointer to an
- `ElementTree`, modifies it as necessary and returns an `ElementTree`
- object.
+ `Element` and modifies it as necessary.
`Treeprocessors` must extend `markdown.Treeprocessor`.
"""
- def run(self, root):
+ def run(self, root: etree.Element) -> etree.Element | None:
"""
Subclasses of `Treeprocessor` should implement a `run` method, which
- takes a root `ElementTree`. This method can return another `ElementTree`
- object, and the existing root `ElementTree `will be replaced, or it can
+ takes a root `Element`. This method can return another `Element`
+ object, and the existing root `Element` will be replaced, or it can
modify the current tree and return `None`.
"""
pass # pragma: no cover
@@ -83,16 +93,16 @@ def __makePlaceholder(self, type):
hash = util.INLINE_PLACEHOLDER % id
return hash, id
- def __findPlaceholder(self, data, index):
+ def __findPlaceholder(self, data: str, index: int) -> tuple[str | None, int]:
"""
- Extract id from data string, start from index
-
- Keyword arguments:
+ Extract id from data string, start from index.
- * `data`: string
- * `index`: index, from which we start search
+ Arguments:
+ data: String.
+ index: Index, from which we start search.
- Returns: placeholder id and string index, after the found placeholder.
+ Returns:
+ Placeholder id and string index, after the found placeholder.
"""
m = self.__placeholder_re.search(data, index)
@@ -102,22 +112,21 @@ def __findPlaceholder(self, data, index):
return None, index + 1
def __stashNode(self, node, type):
- """ Add node to stash """
+ """ Add node to stash. """
placeholder, id = self.__makePlaceholder(type)
self.stashed_nodes[id] = node
return placeholder
- def __handleInline(self, data, patternIndex=0):
+ def __handleInline(self, data: str, patternIndex: int = 0) -> str:
"""
- Process string with inline patterns and replace it
- with placeholders
-
- Keyword arguments:
+ Process string with inline patterns and replace it with placeholders.
- * `data`: A line of Markdown text
- * `patternIndex`: The index of the `inlinePattern` to start with
+ Arguments:
+ data: A line of Markdown text.
+ patternIndex: The index of the `inlinePattern` to start with.
- Returns: String with placeholders.
+ Returns:
+ String with placeholders.
"""
if not isinstance(data, util.AtomicString):
@@ -131,18 +140,15 @@ def __handleInline(self, data, patternIndex=0):
patternIndex += 1
return data
- def __processElementText(self, node, subnode, isText=True):
+ def __processElementText(self, node: etree.Element, subnode: etree.Element, isText: bool = True):
"""
Process placeholders in `Element.text` or `Element.tail`
of Elements popped from `self.stashed_nodes`.
- Keywords arguments:
-
- * `node`: parent node
- * `subnode`: processing node
- * `isText`: boolean variable, True - it's text, False - it's a tail
-
- Returns: None
+ Arguments:
+ node: Parent node.
+ subnode: Processing node.
+ isText: Boolean variable, True - it's text, False - it's a tail.
"""
if isText:
@@ -163,16 +169,17 @@ def __processElementText(self, node, subnode, isText=True):
for newChild in childResult:
node.insert(pos, newChild[0])
- def __processPlaceholders(self, data, parent, isText=True):
+ def __processPlaceholders(self, data: str, parent: etree.Element, isText: bool = True) -> list[etree.ElementTree]:
"""
Process string with placeholders and generate `ElementTree` tree.
- Keyword arguments:
-
- * `data`: string with placeholders instead of `ElementTree` elements.
- * `parent`: Element, which contains processing inline data
+ Arguments:
+ data: String with placeholders instead of `ElementTree` elements.
+ parent: Element, which contains processing inline data.
+ isText: Boolean variable, True - it's text, False - it's a tail.
- Returns: list with `ElementTree` elements with applied inline patterns.
+ Returns:
+ List with `ElementTree` elements with applied inline patterns.
"""
def linkText(text):
@@ -238,19 +245,19 @@ def linkText(text):
return result
- def __applyPattern(self, pattern, data, patternIndex, startIndex=0):
+ def __applyPattern(self, pattern: str, data: str, patternIndex: int, startIndex: int = 0) -> tuple[str, bool, int]:
"""
Check if the line fits the pattern, create the necessary
elements, add it to `stashed_nodes`.
- Keyword arguments:
-
- * `data`: the text to be processed
- * `pattern`: the pattern to be checked
- * `patternIndex`: index of current pattern
- * `startIndex`: string index, from which we start searching
+ Arguments:
+ data: The text to be processed.
+ pattern: The pattern to be checked.
+ patternIndex: Index of current pattern.
+ startIndex: String index, from which we start searching.
- Returns: String with placeholders instead of `ElementTree` elements.
+ Returns:
+ String with placeholders instead of `ElementTree` elements.
"""
new_style = isinstance(pattern, inlinepatterns.InlineProcessor)
@@ -322,22 +329,22 @@ def __build_ancestors(self, parent, parents):
ancestors.reverse()
parents.extend(ancestors)
- def run(self, tree, ancestors=None):
+ def run(self, tree: etree.Element, ancestors: Sequence[str] | None = None) -> etree.Element:
"""Apply inline patterns to a parsed Markdown tree.
- Iterate over `ElementTree`, find elements with inline tag, apply inline
- patterns and append newly created Elements to tree. If you don't
- want to process your data with inline patterns, instead of normal
- string, use subclass `AtomicString`:
+ Iterate over `Element`, find elements with inline tag, apply inline
+ patterns and append newly created Elements to tree. To avoid further
+ processing of string with inline patterns, instead of normal string,
+ use subclass [`AtomicString`][markdown.util.AtomicString]:
- node.text = markdown.AtomicString("This will not be processed.")
+ node.text = markdown.util.AtomicString("This will not be processed.")
Arguments:
+ tree: `Element` object, representing Markdown tree.
+ ancestors: List of parent tag names that precede the tree node (if needed).
- * `tree`: `ElementTree` object, representing Markdown tree.
- * `ancestors`: List of parent tag names that precede the tree node (if needed).
-
- Returns: `ElementTree` object with applied inline patterns.
+ Returns:
+ An element tree object with applied inline patterns.
"""
self.stashed_nodes = {}
@@ -411,8 +418,8 @@ def _prettifyETree(self, elem):
if not elem.tail or not elem.tail.strip():
elem.tail = i
- def run(self, root):
- """ Add line breaks to `ElementTree` root object. """
+ def run(self, root: etree.Element) -> None:
+ """ Add line breaks to `Element` object and its children. """
self._prettifyETree(root)
# Do `
`'s separately as they are often in the middle of
diff --git a/markdown/util.py b/markdown/util.py
index 09b8cf619..1ee602973 100644
--- a/markdown/util.py
+++ b/markdown/util.py
@@ -1,30 +1,39 @@
-"""
-Python Markdown
+# Python Markdown
+
+# A Python implementation of John Gruber's Markdown.
-A Python implementation of John Gruber's Markdown.
+# Documentation: https://python-markdown.github.io/
+# GitHub: https://github.com/Python-Markdown/markdown/
+# PyPI: https://pypi.org/project/Markdown/
-Documentation: https://python-markdown.github.io/
-GitHub: https://github.com/Python-Markdown/markdown/
-PyPI: https://pypi.org/project/Markdown/
+# Started by Manfred Stienstra (http://www.dwerg.net/).
+# Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+# Currently maintained by Waylan Limberg (https://github.com/waylan),
+# Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Started by Manfred Stienstra (http://www.dwerg.net/).
-Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
-Currently maintained by Waylan Limberg (https://github.com/waylan),
-Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+# Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
+# Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+# Copyright 2004 Manfred Stienstra (the original version)
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
+# License: BSD (see LICENSE.md for details).
-License: BSD (see LICENSE.md for details).
+"""
+This module contains various contacts, classes and functions which get referenced and used
+throughout the code base.
"""
+from __future__ import annotations
+
import re
import sys
import warnings
from collections import namedtuple
from functools import wraps, lru_cache
from itertools import count
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING: # pragma: no cover
+ from markdown import Markdown
"""
@@ -33,7 +42,7 @@
"""
-BLOCK_LEVEL_ELEMENTS = [
+BLOCK_LEVEL_ELEMENTS: list[str] = [
# Elements which are invalid to wrap in a `` tag.
# See https://w3c.github.io/html/grouping-content.html#the-p-element
'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl',
@@ -45,23 +54,35 @@
'math', 'map', 'noscript', 'output', 'object', 'option', 'progress', 'script',
'style', 'summary', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'video'
]
+"""
+List of HTML tags which get treated as block-level elements. Same as the `block_level_elements`
+attribute of the [`Markdown`][markdown.Markdown] class. Generally one should use the
+attribute on the class. This remains for compatibility with older extensions.
+"""
# Placeholders
-STX = '\u0002' # Use `STX` ("Start of text") for start-of-placeholder
-ETX = '\u0003' # Use `ETX` ("End of text") for end-of-placeholder
+STX = '\u0002'
+""" "Start of Text" marker for placeholder templates. """
+ETX = '\u0003'
+""" "End of Text" marker for placeholder templates. """
INLINE_PLACEHOLDER_PREFIX = STX+"klzzwxh:"
+""" Prefix for inline placeholder template. """
INLINE_PLACEHOLDER = INLINE_PLACEHOLDER_PREFIX + "%s" + ETX
+""" Placeholder template for stashed inline text. """
INLINE_PLACEHOLDER_RE = re.compile(INLINE_PLACEHOLDER % r'([0-9]+)')
+""" Regular Expression which matches inline placeholders. """
AMP_SUBSTITUTE = STX+"amp"+ETX
+""" Placeholder template for HTML entities. """
HTML_PLACEHOLDER = STX + "wzxhzdk:%s" + ETX
+""" Placeholder template for raw HTML. """
HTML_PLACEHOLDER_RE = re.compile(HTML_PLACEHOLDER % r'([0-9]+)')
+""" Regular expression which matches HTML placeholders. """
TAG_PLACEHOLDER = STX + "hzzhzkh:%s" + ETX
+""" Placeholder template for tags. """
-"""
-Constants you probably do not need to change
------------------------------------------------------------------------------
-"""
+# Constants you probably do not need to change
+# -----------------------------------------------------------------------------
RTL_BIDI_RANGES = (
('\u0590', '\u07FF'),
@@ -72,14 +93,13 @@
)
-"""
-AUXILIARY GLOBAL FUNCTIONS
-=============================================================================
-"""
+# AUXILIARY GLOBAL FUNCTIONS
+# =============================================================================
@lru_cache(maxsize=None)
def get_installed_extensions():
+ """ Return all entry_points in the `markdown.extensions` group. """
if sys.version_info >= (3, 10):
from importlib import metadata
else: # ` bool | None:
+ """Parses a string representing a boolean value. If parsing was successful,
+ returns `True` or `False`. If `preserve_none=True`, returns `True`, `False`,
or `None`. If parsing was not successful, raises `ValueError`, or, if
`fail_on_errors=False`, returns `None`."""
if not isinstance(value, str):
@@ -129,8 +152,8 @@ def parseBoolValue(value, fail_on_errors=True, preserve_none=False):
raise ValueError('Cannot parse bool value: %r' % value)
-def code_escape(text):
- """Escape code."""
+def code_escape(text: str) -> str:
+ """HTML escape a string of code."""
if "&" in text:
text = text.replace("&", "&")
if "<" in text:
@@ -156,10 +179,8 @@ def nearing_recursion_limit():
return sys.getrecursionlimit() - _get_stack_depth() < 100
-"""
-MISC AUXILIARY CLASSES
-=============================================================================
-"""
+# MISC AUXILIARY CLASSES
+# =============================================================================
class AtomicString(str):
@@ -168,7 +189,16 @@ class AtomicString(str):
class Processor:
- def __init__(self, md=None):
+ """ The base class for all processors.
+
+ Attributes:
+ Processor.md: The `Markdown` instance passed in an initialization.
+
+ Arguments:
+ md: The `Markdown` instance this processor is a part of.
+
+ """
+ def __init__(self, md: Markdown = None):
self.md = md
@@ -185,17 +215,17 @@ def __init__(self):
self.tag_counter = 0
self.tag_data = [] # list of dictionaries in the order tags appear
- def store(self, html):
+ def store(self, html: str) -> str:
"""
Saves an HTML segment for later reinsertion. Returns a
placeholder string that needs to be inserted into the
document.
Keyword arguments:
+ html: An html segment.
- * `html`: an html segment
-
- Returns : a placeholder string
+ Returns:
+ A placeholder string.
"""
self.rawHtmlBlocks.append(html)
@@ -204,13 +234,14 @@ def store(self, html):
return placeholder
def reset(self):
+ """ Clear the stash. """
self.html_counter = 0
self.rawHtmlBlocks = []
def get_placeholder(self, key):
return HTML_PLACEHOLDER % key
- def store_tag(self, tag, attrs, left_index, right_index):
+ def store_tag(self, tag, attrs, left_index, right_index) -> str:
"""Store tag data and return a placeholder."""
self.tag_data.append({'tag': tag, 'attrs': attrs,
'left_index': left_index,
@@ -299,7 +330,7 @@ def __len__(self):
def __repr__(self):
return '<{}({})>'.format(self.__class__.__name__, list(self))
- def get_index_for_name(self, name):
+ def get_index_for_name(self, name) -> int:
"""
Return the index of the given name.
"""
@@ -310,15 +341,14 @@ def get_index_for_name(self, name):
)
raise ValueError('No item named "{}" exists.'.format(name))
- def register(self, item, name, priority):
+ def register(self, item: Any, name: str, priority: int):
"""
Add an item to the registry with the given name and priority.
- Parameters:
-
- * `item`: The item being registered.
- * `name`: A string used to reference the item.
- * `priority`: An integer or float used to sort against all items.
+ Arguments:
+ item: The item being registered.
+ name: A string used to reference the item.
+ priority: An integer or float used to sort against all items.
If an item is registered with a "name" which already exists, the
existing item is replaced with the new item. Treat carefully as the
@@ -337,7 +367,7 @@ def deregister(self, name, strict=True):
"""
Remove an item from the registry.
- Set `strict=False` to fail silently.
+ Set `strict=False` to fail silently. Otherwise a [`ValueError`][] is raised for an unknown `name`.
"""
try:
index = self.get_index_for_name(name)
diff --git a/mkdocs.yml b/mkdocs.yml
index 9cb1511d6..1db47dc59 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -5,6 +5,7 @@ site_author: "The Python-Markdown Project"
copyright: "Copyright © 2010-2023"
use_directory_urls: true
+watch: [markdown]
theme:
name: nature
@@ -14,6 +15,7 @@ theme:
extra_css:
- custom.css
+ - mkdocstrings.css
nav:
- Python-Markdown: index.md
@@ -42,6 +44,7 @@ nav:
- WikiLinks: extensions/wikilinks.md
- Extension API: extensions/api.md
- Test Tools: test_tools.md
+ - API Reference: reference/
- Contributing to Python-Markdown: contributing.md
- Changelog: changelog.md
- Authors: authors.md
@@ -59,3 +62,51 @@ markdown_extensions:
- mdx_gh_links:
user: Python-Markdown
repo: markdown
+
+plugins:
+- gen-files:
+ scripts:
+ - scripts/gen_ref_nav.py
+- literate-nav:
+ nav_file: SUMMARY.md
+- section-index
+- mkdocstrings:
+ custom_templates: docs/templates
+ handlers:
+ python:
+ import:
+ - https://docs.python.org/3/objects.inv
+ options:
+ annotations_path: brief
+ docstring_options:
+ ignore_init_summary: true
+ docstring_style: google
+ docstring_section_style: list
+ extensions:
+ - scripts/griffe_extensions.py:DeprecatedExtension
+ - scripts/griffe_extensions.py:PriorityTableExtension:
+ paths:
+ - markdown.preprocessors.build_preprocessors
+ - markdown.blockprocessors.build_block_parser
+ - markdown.treeprocessors.build_treeprocessors
+ - markdown.inlinepatterns.build_inlinepatterns
+ - markdown.postprocessors.build_postprocessors
+ filters: ["!^_"]
+ group_by_category: false
+ heading_level: 1
+ inherited_members: true
+ members_order: source
+ merge_init_into_class: true
+ separate_signature: false
+ show_root_heading: true
+ show_object_full_path: true
+ show_signature_annotations: true
+ show_source: false
+ show_symbol_type_heading: true
+ show_symbol_type_toc: true
+ signature_crossrefs: false
+ summary: true
+ source:
+ repo: https://github.com/Python-Markdown/markdown
+ tag: !!python/name:markdown.__version__
+ title: "View source code on GitHub."
diff --git a/pyproject.toml b/pyproject.toml
index c75a0edb2..8c9e9bcfe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,8 +54,12 @@ testing = [
]
docs = [
'mkdocs>=1.5',
- 'mkdocs-nature>=0.5',
- 'mdx_gh_links>=0.2'
+ 'mkdocs-nature>=0.6',
+ 'mdx_gh_links>=0.2',
+ "mkdocstrings[python]",
+ "mkdocs-gen-files",
+ "mkdocs-section-index",
+ "mkdocs-literate-nav",
]
[project.urls]
diff --git a/scripts/gen_ref_nav.py b/scripts/gen_ref_nav.py
new file mode 100755
index 000000000..2ecbd5767
--- /dev/null
+++ b/scripts/gen_ref_nav.py
@@ -0,0 +1,60 @@
+"""Generate the code reference pages and navigation."""
+
+import textwrap
+import yaml
+from pathlib import Path
+
+import mkdocs_gen_files
+
+nav = mkdocs_gen_files.Nav()
+
+per_module_options = {
+ "markdown": {"summary": {"attributes": True, "functions": True, "classes": True}}
+}
+
+modules = [
+ Path("markdown", "__init__.py"),
+ Path("markdown", "preprocessors.py"),
+ Path("markdown", "blockparser.py"),
+ Path("markdown", "blockprocessors.py"),
+ Path("markdown", "treeprocessors.py"),
+ Path("markdown", "inlinepatterns.py"),
+ Path("markdown", "postprocessors.py"),
+ Path("markdown", "serializers.py"),
+ Path("markdown", "util.py"),
+ Path("markdown", "htmlparser.py"),
+ Path("markdown", "test_tools.py"),
+ *sorted(Path("markdown", "extensions").rglob("*.py")),
+]
+
+for path in modules:
+ module_path = path.with_suffix("")
+ doc_path = path.with_suffix(".md")
+ full_doc_path = Path("reference", doc_path)
+
+ parts = tuple(module_path.parts)
+
+ if parts[-1] == "__init__":
+ parts = parts[:-1]
+ doc_path = doc_path.with_name("index.md")
+ full_doc_path = full_doc_path.with_name("index.md")
+ elif parts[-1].startswith("_"):
+ continue
+
+ nav_parts = [f"{part}" for part in parts]
+ nav[nav_parts] = doc_path.as_posix()
+
+ with mkdocs_gen_files.open(full_doc_path, "w") as fd:
+ ident = ".".join(parts)
+ fd.write(f"::: {ident}")
+ if ident in per_module_options:
+ yaml_options = yaml.dump({"options": per_module_options[ident]})
+ fd.write(f"\n{textwrap.indent(yaml_options, prefix=' ')}")
+ elif ident.startswith("markdown.extensions."):
+ yaml_options = yaml.dump({"options": {"inherited_members": False}})
+ fd.write(f"\n{textwrap.indent(yaml_options, prefix=' ')}")
+
+ mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path)
+
+with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
+ nav_file.writelines(nav.build_literate_nav())
diff --git a/scripts/griffe_extensions.py b/scripts/griffe_extensions.py
new file mode 100644
index 000000000..551687fde
--- /dev/null
+++ b/scripts/griffe_extensions.py
@@ -0,0 +1,97 @@
+"""Griffe extensions."""
+
+from __future__ import annotations
+
+import ast
+from typing import TYPE_CHECKING
+import textwrap
+
+from griffe import Docstring, Extension
+from griffe.docstrings.dataclasses import DocstringSectionAdmonition, DocstringSectionText
+
+if TYPE_CHECKING:
+ from griffe import Class, Function, ObjectNode
+
+
+def _deprecated(obj: Class | Function) -> str | None:
+ for decorator in obj.decorators:
+ if decorator.callable_path == "markdown.util.deprecated":
+ return ast.literal_eval(str(decorator.value.arguments[0]))
+ return None
+
+
+class DeprecatedExtension(Extension):
+ """Griffe extension for `@markdown.util.deprecated` decorator support."""
+
+ def _insert_message(self, obj: Function | Class, message: str) -> None:
+ if not obj.docstring:
+ obj.docstring = Docstring("", parent=obj)
+ sections = obj.docstring.parsed
+ sections.insert(0, DocstringSectionAdmonition(kind="warning", text=message, title="Deprecated"))
+
+ def on_class_instance(self, node: ast.AST | ObjectNode, cls: Class) -> None: # noqa: ARG002
+ """Add section to docstrings of deprecated classes."""
+ if message := _deprecated(cls):
+ self._insert_message(cls, message)
+ cls.labels.add("deprecated")
+
+ def on_function_instance(self, node: ast.AST | ObjectNode, func: Function) -> None: # noqa: ARG002
+ """Add section to docstrings of deprecated functions."""
+ if message := _deprecated(func):
+ self._insert_message(func, message)
+ func.labels.add("deprecated")
+
+
+class PriorityTableExtension(Extension):
+ """ Griffe extension to insert a table of processor priority in specified functions. """
+
+ def __init__(self, paths: list[str] | None = None) -> None:
+ super().__init__()
+ self.paths = paths
+
+ def linked_obj(self, value: str, path: str) -> str:
+ """ Wrap object name in reference link. """
+ return f'[`{value}`][{path}.{value}]'
+
+ def on_function_instance(self, node: ast.AST | ObjectNode, func: Function) -> None: # noqa: ARG002
+ """Add table to specified function docstrings."""
+ if self.paths and func.path not in self.paths:
+ return # skip objects that were not selected
+
+ # Table header
+ data = [
+ 'Class Instance | Name | Priority',
+ '-------------- | ---- | :------:'
+ ]
+
+ # Extract table body from source code of function.
+ for obj in node.body:
+ # Extract the arguments passed to `util.Registry.register`.
+ if isinstance(obj, ast.Expr) and isinstance(obj.value, ast.Call) and obj.value.func.attr == 'register':
+ _args = obj.value.args
+ cls = self.linked_obj(_args[0].func.id, func.path.rsplit('.', 1)[0])
+ name = _args[1].value
+ priority = str(_args[2].value)
+ if func.name == ('build_inlinepatterns'):
+ # Include Pattern: first arg passed to class
+ if isinstance(_args[0].args[0], ast.Constant):
+ # Pattern is a string
+ value = f'`"{_args[0].args[0].value}"`'
+ else:
+ # Pattern is a variable
+ value = self.linked_obj(_args[0].args[0].id, func.path.rsplit('.', 1)[0])
+ cls = f'{cls}({value})'
+ data.append(f'{cls} | `{name}` | `{priority}`')
+
+ table = '\n'.join(data)
+ body = (
+ f"Return a [`{func.returns.canonical_name}`][{func.returns.canonical_path}] instance which contains "
+ "the following collection of classes with their assigned names and priorities.\n\n"
+ f"{table}"
+ )
+
+ # Add to docstring.
+ if not func.docstring:
+ func.docstring = Docstring("", parent=func)
+ sections = func.docstring.parsed
+ sections.append(DocstringSectionText(body, title="Priority Table"))
diff --git a/tests/__init__.py b/tests/__init__.py
index 564ba3be4..19aa92b87 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_apis.py b/tests/test_apis.py
index 82bc3b8c5..1305c5476 100644
--- a/tests/test_apis.py
+++ b/tests/test_apis.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_extensions.py b/tests/test_extensions.py
index 305ea8347..a9e789f19 100644
--- a/tests/test_extensions.py
+++ b/tests/test_extensions.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_legacy.py b/tests/test_legacy.py
index 295588b0a..df4da4d71 100644
--- a/tests/test_legacy.py
+++ b/tests/test_legacy.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/__init__.py b/tests/test_syntax/__init__.py
index 564ba3be4..19aa92b87 100644
--- a/tests/test_syntax/__init__.py
+++ b/tests/test_syntax/__init__.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/__init__.py b/tests/test_syntax/blocks/__init__.py
index 564ba3be4..19aa92b87 100644
--- a/tests/test_syntax/blocks/__init__.py
+++ b/tests/test_syntax/blocks/__init__.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/test_blockquotes.py b/tests/test_syntax/blocks/test_blockquotes.py
index 3422f9f76..9a114644a 100644
--- a/tests/test_syntax/blocks/test_blockquotes.py
+++ b/tests/test_syntax/blocks/test_blockquotes.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/test_code_blocks.py b/tests/test_syntax/blocks/test_code_blocks.py
index 54b686069..dbbb9c79a 100644
--- a/tests/test_syntax/blocks/test_code_blocks.py
+++ b/tests/test_syntax/blocks/test_code_blocks.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/test_headers.py b/tests/test_syntax/blocks/test_headers.py
index ca065a56c..da2ca14ff 100644
--- a/tests/test_syntax/blocks/test_headers.py
+++ b/tests/test_syntax/blocks/test_headers.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/test_hr.py b/tests/test_syntax/blocks/test_hr.py
index 85a51b32f..a8753977b 100644
--- a/tests/test_syntax/blocks/test_hr.py
+++ b/tests/test_syntax/blocks/test_hr.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/test_html_blocks.py b/tests/test_syntax/blocks/test_html_blocks.py
index 8623b3085..22b949834 100644
--- a/tests/test_syntax/blocks/test_html_blocks.py
+++ b/tests/test_syntax/blocks/test_html_blocks.py
@@ -13,7 +13,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/blocks/test_paragraphs.py b/tests/test_syntax/blocks/test_paragraphs.py
index 9b7ba0357..f09731e72 100644
--- a/tests/test_syntax/blocks/test_paragraphs.py
+++ b/tests/test_syntax/blocks/test_paragraphs.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/__init__.py b/tests/test_syntax/extensions/__init__.py
index 564ba3be4..19aa92b87 100644
--- a/tests/test_syntax/extensions/__init__.py
+++ b/tests/test_syntax/extensions/__init__.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/test_abbr.py b/tests/test_syntax/extensions/test_abbr.py
index 64388c2d8..fbb25ffb2 100644
--- a/tests/test_syntax/extensions/test_abbr.py
+++ b/tests/test_syntax/extensions/test_abbr.py
@@ -13,7 +13,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/test_attr_list.py b/tests/test_syntax/extensions/test_attr_list.py
index f6a4fe65b..ba8b23693 100644
--- a/tests/test_syntax/extensions/test_attr_list.py
+++ b/tests/test_syntax/extensions/test_attr_list.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/test_footnotes.py b/tests/test_syntax/extensions/test_footnotes.py
index 9fe786bad..6f504e39c 100644
--- a/tests/test_syntax/extensions/test_footnotes.py
+++ b/tests/test_syntax/extensions/test_footnotes.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/test_legacy_attrs.py b/tests/test_syntax/extensions/test_legacy_attrs.py
index b4ba5e715..44e8a6159 100644
--- a/tests/test_syntax/extensions/test_legacy_attrs.py
+++ b/tests/test_syntax/extensions/test_legacy_attrs.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/test_legacy_em.py b/tests/test_syntax/extensions/test_legacy_em.py
index c5daf1cf8..ad2862e5e 100644
--- a/tests/test_syntax/extensions/test_legacy_em.py
+++ b/tests/test_syntax/extensions/test_legacy_em.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/extensions/test_md_in_html.py b/tests/test_syntax/extensions/test_md_in_html.py
index 3de49b072..c439a03b2 100644
--- a/tests/test_syntax/extensions/test_md_in_html.py
+++ b/tests/test_syntax/extensions/test_md_in_html.py
@@ -13,7 +13,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
@@ -1208,7 +1208,7 @@ def test_md1_nested_footnote_ref(self):
def load_tests(loader, tests, pattern):
- ''' Ensure `TestHTMLBlocks` doesn't get run twice by excluding it here. '''
+ """ Ensure `TestHTMLBlocks` doesn't get run twice by excluding it here. """
suite = TestSuite()
for test_class in [TestDefaultwMdInHTML, TestMdInHTML, TestMarkdownInHTMLPostProcessor]:
tests = loader.loadTestsFromTestCase(test_class)
diff --git a/tests/test_syntax/extensions/test_tables.py b/tests/test_syntax/extensions/test_tables.py
index 6a1a0d4fc..4e33d1aa3 100644
--- a/tests/test_syntax/extensions/test_tables.py
+++ b/tests/test_syntax/extensions/test_tables.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/inline/__init__.py b/tests/test_syntax/inline/__init__.py
index 564ba3be4..19aa92b87 100644
--- a/tests/test_syntax/inline/__init__.py
+++ b/tests/test_syntax/inline/__init__.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/inline/test_entities.py b/tests/test_syntax/inline/test_entities.py
index 34cc2e725..4262bce1b 100644
--- a/tests/test_syntax/inline/test_entities.py
+++ b/tests/test_syntax/inline/test_entities.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/inline/test_images.py b/tests/test_syntax/inline/test_images.py
index c9c7cb88d..379cc597a 100644
--- a/tests/test_syntax/inline/test_images.py
+++ b/tests/test_syntax/inline/test_images.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/inline/test_links.py b/tests/test_syntax/inline/test_links.py
index 04587560d..702756894 100644
--- a/tests/test_syntax/inline/test_links.py
+++ b/tests/test_syntax/inline/test_links.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
diff --git a/tests/test_syntax/inline/test_raw_html.py b/tests/test_syntax/inline/test_raw_html.py
index f75a616d6..f165f750a 100644
--- a/tests/test_syntax/inline/test_raw_html.py
+++ b/tests/test_syntax/inline/test_raw_html.py
@@ -12,7 +12,7 @@
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2023 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)