Repository: mkdocs/mkdocs
Pull request #3297 (2023-07-18 22:13:36)

Regression fixes

- Always set `inclusion_level` so static-i18n plugin keeps working
- Fix unescaping of Markdown titles
- Add a test for relative link without dot in filename

Found in https://github.com/mkdocs/regressions/actions/runs/5591321689


Patch:
diff --git a/mkdocs/structure/files.py b/mkdocs/structure/files.py
index a9f7bfd0..ce154437 100644
--- a/mkdocs/structure/files.py
+++ b/mkdocs/structure/files.py
@@ -190,7 +190,7 @@ class File:
     url: str
     """The URI of the destination file relative to the destination directory as a string."""
 
-    inclusion: InclusionLevel
+    inclusion: InclusionLevel = InclusionLevel.UNDEFINED
     """Whether the file will be excluded from the built site."""
 
     @property
diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py
index 7b84e1fc..34c0bc76 100644
--- a/mkdocs/structure/pages.py
+++ b/mkdocs/structure/pages.py
@@ -4,13 +4,11 @@ import copy
 import logging
 import posixpath
 import warnings
-from typing import TYPE_CHECKING, Any, Callable, Iterator, MutableMapping
+from typing import TYPE_CHECKING, Any, Callable, Iterator, MutableMapping, Sequence
 from urllib.parse import unquote as urlunquote
 from urllib.parse import urljoin, urlsplit, urlunsplit
 
 import markdown
-import markdown.extensions
-import markdown.postprocessors
 import markdown.treeprocessors
 from markdown.util import AMP_SUBSTITUTE
 
@@ -22,16 +20,12 @@ from mkdocs.utils import _removesuffix, get_build_date, get_markdown_title, meta
 if TYPE_CHECKING:
     from xml.etree import ElementTree as etree
 
+    import markdown.postprocessors
+
     from mkdocs.config.defaults import MkDocsConfig
     from mkdocs.structure.files import File, Files
     from mkdocs.structure.toc import TableOfContents
 
-_unescape: Callable[[str], str]
-try:
-    _unescape = markdown.treeprocessors.UnescapeTreeprocessor().unescape  # type: ignore
-except AttributeError:
-    _unescape = markdown.postprocessors.UnescapePostprocessor().run
-
 
 log = logging.getLogger(__name__)
 
@@ -437,6 +431,7 @@ class _RelativePathTreeprocessor(markdown.treeprocessors.Treeprocessor):
 
 class _ExtractTitleTreeprocessor(markdown.treeprocessors.Treeprocessor):
     title: str | None = None
+    postprocessors: Sequence[markdown.postprocessors.Postprocessor] = ()
 
     def run(self, root: etree.Element) -> etree.Element:
         for el in root:
@@ -446,13 +441,18 @@ class _ExtractTitleTreeprocessor(markdown.treeprocessors.Treeprocessor):
                     el = copy.copy(el)
                     del el[-1]
                 # Extract the text only, recursively.
-                self.title = _unescape(''.join(el.itertext()))
+                title = ''.join(el.itertext())
+                # Unescape per Markdown implementation details.
+                for pp in self.postprocessors:
+                    title = pp.run(title)
+                self.title = title
             break
         return root
 
     def _register(self, md: markdown.Markdown) -> None:
+        self.postprocessors = tuple(md.postprocessors)
         md.treeprocessors.register(
             self,
             "mkdocs_extract_title",
-            priority=1,  # Close to the end.
+            priority=-1,  # After the end.
         )
diff --git a/pyproject.toml b/pyproject.toml
index 3c412846..14e030bd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -151,7 +151,6 @@ dependencies = [
     "types-PyYAML",
     "types-setuptools",
     "typing-extensions",
-    "click <8.1.4",
 ]
 [tool.hatch.envs.types.scripts]
 check = "mypy mkdocs"


Test patch:
diff --git a/mkdocs/tests/structure/page_tests.py b/mkdocs/tests/structure/page_tests.py
index d196cffa..0f8d1f17 100644
--- a/mkdocs/tests/structure/page_tests.py
+++ b/mkdocs/tests/structure/page_tests.py
@@ -352,7 +352,7 @@ class PageTests(unittest.TestCase):
 
     _FORMATTING_CONTENT = dedent(
         '''
-        # Hello *beautiful* `world`
+        # \\*Hello --- *beautiful* `world`
 
         Hi.
         '''
@@ -361,11 +361,12 @@ class PageTests(unittest.TestCase):
     @tempdir(files={'testing_formatting.md': _FORMATTING_CONTENT})
     def test_page_title_from_markdown_strip_formatting(self, docs_dir):
         cfg = load_config()
+        cfg.markdown_extensions.append('smarty')
         fl = File('testing_formatting.md', docs_dir, docs_dir, use_directory_urls=True)
         pg = Page(None, fl, cfg)
         pg.read_source(cfg)
         pg.render(cfg, fl)
-        self.assertEqual(pg.title, 'Hello beautiful world')
+        self.assertEqual(pg.title, '*Hello &mdash; beautiful world')
 
     _ATTRLIST_CONTENT = dedent(
         '''
@@ -819,6 +820,23 @@ class RelativePathExtensionTests(unittest.TestCase):
             '<a href="sub2/non-index.html">link</a>',
         )
 
+    def test_relative_doc_link_without_extension(self):
+        self.assertEqual(
+            self.get_rendered_result(
+                use_directory_urls=False,
+                content='[link](bar/Dockerfile)',
+                files=['foo/bar.md', 'foo/bar/Dockerfile'],
+            ),
+            '<a href="bar/Dockerfile">link</a>',
+        )
+        self.assertEqual(
+            self.get_rendered_result(
+                content='[link](bar/Dockerfile)',
+                files=['foo/bar.md', 'foo/bar/Dockerfile'],
+            ),
+            '<a href="Dockerfile">link</a>',
+        )
+
     def test_relative_html_link_with_encoded_space(self):
         self.assertEqual(
             self.get_rendered_result(