From ba98661b50e2cde19d8696d6c8ceecdbb49ce83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 14 Mar 2025 14:39:54 +0100 Subject: [PATCH 01/51] deps: Remove unused typing-extensions dependency --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c3087f61..1e104425 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,6 @@ dependencies = [ "mkdocs-autorefs>=1.4", "pymdown-extensions>=6.3", "importlib-metadata>=4.6; python_version < '3.10'", - "typing-extensions>=4.1; python_version < '3.10'", ] [project.optional-dependencies] From 94645796ecbb48a4a4c654d219df2e588d59f7bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 14 Mar 2025 14:40:23 +0100 Subject: [PATCH 02/51] tests: Remove old skip conditions --- tests/test_extension.py | 1 - tests/test_inventory.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/test_extension.py b/tests/test_extension.py index b7c1c742..aed0a1f1 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -60,7 +60,6 @@ def test_reference_inside_autodoc(ext_markdown: Markdown) -> None: assert re.search(r"Link to <.*something\.Else.*>something\.Else<.*>\.", output) -@pytest.mark.skipif(sys.version_info < (3, 8), reason="typing.Literal requires Python 3.8") def test_quote_inside_annotation(ext_markdown: Markdown) -> None: """Assert that inline highlighting doesn't double-escape HTML.""" output = ext_markdown.convert("::: tests.fixtures.string_annotation.Foo") diff --git a/tests/test_inventory.py b/tests/test_inventory.py index eb008661..9589d528 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -36,7 +36,6 @@ def test_sphinx_load_inventory_file(our_inv: Inventory) -> None: assert item.name in sphinx_inv[f"{item.domain}:{item.role}"] -@pytest.mark.skipif(sys.version_info < (3, 7), reason="using plugins that require Python 3.7") def test_sphinx_load_mkdocstrings_inventory_file() -> None: """Perform the 'live' inventory load test on mkdocstrings own inventory.""" mkdocs_config = load_config() From 983b3cd8c4e1de249ef3ebec2678e295e6c6b966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 14 Mar 2025 14:40:45 +0100 Subject: [PATCH 03/51] chore: Mark legacy stuff with Yore comments --- pyproject.toml | 2 ++ src/mkdocstrings/_internal/handlers/base.py | 3 ++- src/mkdocstrings/_internal/loggers.py | 3 ++- src/mkdocstrings/extension.py | 2 ++ src/mkdocstrings/handlers/__init__.py | 2 ++ src/mkdocstrings/handlers/base.py | 2 ++ src/mkdocstrings/handlers/rendering.py | 2 ++ src/mkdocstrings/inventory.py | 2 ++ src/mkdocstrings/loggers.py | 2 ++ src/mkdocstrings/plugin.py | 2 ++ 10 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1e104425..29aff0d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,12 +31,14 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ + # YORE: Bump 1: Replace `2.11.1` with `3.1` within line. "Jinja2>=2.11.1", "Markdown>=3.6", "MarkupSafe>=1.1", "mkdocs>=1.6", "mkdocs-autorefs>=1.4", "pymdown-extensions>=6.3", + # YORE: EOL 3.9: Remove line. "importlib-metadata>=4.6; python_version < '3.10'", ] diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 7784f007..f19d9094 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -33,7 +33,8 @@ from mkdocstrings._internal.inventory import Inventory from mkdocstrings._internal.loggers import get_logger, get_template_logger -# TODO: remove once support for Python 3.9 is dropped + +# YORE: EOL 3.9: Replace block with line 4. if sys.version_info < (3, 10): from importlib_metadata import entry_points else: diff --git a/src/mkdocstrings/_internal/loggers.py b/src/mkdocstrings/_internal/loggers.py index d56d09c3..6c6304c3 100644 --- a/src/mkdocstrings/_internal/loggers.py +++ b/src/mkdocstrings/_internal/loggers.py @@ -7,9 +7,10 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Callable +# YORE: Bump 1: Replace block with line 2. try: from jinja2 import pass_context -except ImportError: # TODO: remove once Jinja2 < 3.1 is dropped +except ImportError: from jinja2 import contextfunction as pass_context # type: ignore[attr-defined,no-redef] if TYPE_CHECKING: diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py index 15a84cc8..c7943652 100644 --- a/src/mkdocstrings/extension.py +++ b/src/mkdocstrings/extension.py @@ -1,5 +1,7 @@ """Deprecated. Import from `mkdocstrings` directly.""" +# YORE: Bump 1: Remove file. + import warnings from typing import Any diff --git a/src/mkdocstrings/handlers/__init__.py b/src/mkdocstrings/handlers/__init__.py index af032e98..b684324a 100644 --- a/src/mkdocstrings/handlers/__init__.py +++ b/src/mkdocstrings/handlers/__init__.py @@ -1 +1,3 @@ """Deprecated. Import from `mkdocstrings` directly.""" + +# YORE: Bump 1: Remove file. diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py index 82ee3edb..c55a50ba 100644 --- a/src/mkdocstrings/handlers/base.py +++ b/src/mkdocstrings/handlers/base.py @@ -1,5 +1,7 @@ """Deprecated. Import from `mkdocstrings` directly.""" +# YORE: Bump 1: Remove file. + import warnings from typing import Any diff --git a/src/mkdocstrings/handlers/rendering.py b/src/mkdocstrings/handlers/rendering.py index 940f3a9c..f3f04eea 100644 --- a/src/mkdocstrings/handlers/rendering.py +++ b/src/mkdocstrings/handlers/rendering.py @@ -1,5 +1,7 @@ """Deprecated. Import from `mkdocstrings` directly.""" +# YORE: Bump 1: Remove file. + import warnings from typing import Any diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py index b5c8adea..7192acff 100644 --- a/src/mkdocstrings/inventory.py +++ b/src/mkdocstrings/inventory.py @@ -1,5 +1,7 @@ """Deprecated. Import from `mkdocstrings` directly.""" +# YORE: Bump 1: Remove file. + import warnings from typing import Any diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py index ce805362..25545ca5 100644 --- a/src/mkdocstrings/loggers.py +++ b/src/mkdocstrings/loggers.py @@ -1,5 +1,7 @@ """Deprecated. Import from `mkdocstrings` directly.""" +# YORE: Bump 1: Remove file. + import warnings from typing import Any diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py index b4edf945..dbb6abf9 100644 --- a/src/mkdocstrings/plugin.py +++ b/src/mkdocstrings/plugin.py @@ -1,5 +1,7 @@ """Deprecated. Import from `mkdocstrings` directly.""" +# YORE: Bump 1: Remove file. + import warnings from typing import Any From ccf65c1166103cf705f30b27b8c913863a372da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 28 Mar 2025 16:06:34 +0100 Subject: [PATCH 04/51] docs: Remove 'sponsors only' labels --- docs/usage/handlers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index b9a01f68..0d375a95 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -4,12 +4,12 @@ A handler is what makes it possible to collect and render documentation for a pa ## Available handlers -- [C](https://mkdocstrings.github.io/c/){ .external } [:octicons-heart-fill-24:{ .heart .pulse title="Sponsors only" }](../insiders/index.md) +- [C](https://mkdocstrings.github.io/c/){ .external } - [Crystal](https://mkdocstrings.github.io/crystal/){ .external } - [Python](https://mkdocstrings.github.io/python/){ .external } - [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/){ .external } - [Shell](https://mkdocstrings.github.io/shell/){ .external } -- [TypeScript](https://mkdocstrings.github.io/typescript/){ .external } [:octicons-heart-fill-24:{ .heart .pulse title="Sponsors only" }](../insiders/index.md) +- [TypeScript](https://mkdocstrings.github.io/typescript/){ .external } - [VBA](https://pypi.org/project/mkdocstrings-vba/){ .external } ## About the Python handlers From 0bc4799b0e38bf7378d0e57428d52fdd1e7ac1ab Mon Sep 17 00:00:00 2001 From: Josh Mitchell Date: Thu, 27 Mar 2025 17:27:17 +1100 Subject: [PATCH 05/51] style: Format and configure for Ruff >= 0.10.0 Ruff 0.10.0 introduces the S704 lint, which triggers when a non- literal string is passed to `markupsafe.Markup()`. This triggered 5 times in the codebase. Only one of these errors was trivially fixable, and the fix caused tests to fail because the "fix" introduced escapes to already correct markup. This commit therefore configures Ruff to ignore this lint and does not fix any code that triggers it. Other changes are due to other formatting and linting changes from recent releases of Ruff. Ruff 0.10.0: https://github.com/astral-sh/ruff/releases/tag/0.10.0 Lint S704: https://docs.astral.sh/ruff/rules/unsafe-markup-use/ --- config/ruff.toml | 1 + scripts/insiders.py | 2 +- src/mkdocstrings/_internal/handlers/base.py | 5 ++--- tests/test_extension.py | 1 - tests/test_inventory.py | 1 - 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/config/ruff.toml b/config/ruff.toml index 655a158c..65416253 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -43,6 +43,7 @@ ignore = [ "PLR0913", # Too many arguments to function call "PLR0915", # Too many statements "SLF001", # Private member accessed + "S704", # Unsafe use of `markupsafe.Markup` "TRY003", # Avoid specifying long messages outside the exception class ] diff --git a/scripts/insiders.py b/scripts/insiders.py index 6535a31e..4cd438d4 100644 --- a/scripts/insiders.py +++ b/scripts/insiders.py @@ -168,6 +168,6 @@ def load_json(url: str) -> str | list | dict: ongoing_goals = [goal for goal in goals.values() if not goal.complete] unreleased_features = sorted( (ft for ft in feature_list(ongoing_goals) if ft.since), - key=lambda ft: cast(date, ft.since), + key=lambda ft: cast("date", ft.since), reverse=True, ) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index f19d9094..cb1fffb3 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -17,7 +17,6 @@ from jinja2 import Environment, FileSystemLoader from markdown import Markdown -from markdown.extensions.toc import TocTreeprocessor from markupsafe import Markup from mkdocs.utils.cache import download_and_cache_url from mkdocs_autorefs import AutorefsInlineProcessor, BacklinksTreeProcessor @@ -33,7 +32,6 @@ from mkdocstrings._internal.inventory import Inventory from mkdocstrings._internal.loggers import get_logger, get_template_logger - # YORE: EOL 3.9: Replace block with line 4. if sys.version_info < (3, 10): from importlib_metadata import entry_points @@ -44,6 +42,7 @@ from collections.abc import Iterable, Iterator, Mapping, Sequence from markdown import Extension + from markdown.extensions.toc import TocTreeprocessor from mkdocs_autorefs import AutorefsHookInterface, Backlink _logger = get_logger(__name__) @@ -494,7 +493,7 @@ def do_heading( el = Element(f"h{heading_level}", attributes) el.append(Element("mkdocstrings-placeholder")) # Tell the inner 'toc' extension to make its additions if configured so. - toc = cast(TocTreeprocessor, self.md.treeprocessors["toc"]) + toc = cast("TocTreeprocessor", self.md.treeprocessors["toc"]) if toc.use_anchors: toc.add_anchor(el, attributes["id"]) if toc.use_permalinks: diff --git a/tests/test_extension.py b/tests/test_extension.py index aed0a1f1..dd3d7028 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -3,7 +3,6 @@ from __future__ import annotations import re -import sys from textwrap import dedent from typing import TYPE_CHECKING diff --git a/tests/test_inventory.py b/tests/test_inventory.py index 9589d528..8c2333d3 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -2,7 +2,6 @@ from __future__ import annotations -import sys from io import BytesIO from os.path import join From 81caff5ff76f1a6606da9d2980e81ae9d2e02246 Mon Sep 17 00:00:00 2001 From: Josh Mitchell Date: Thu, 27 Mar 2025 17:38:04 +1100 Subject: [PATCH 06/51] fix: Ignore invalid inventory lines Previously, inventory items whose `dispname` value contains multiple lines would prevent mkdocstrings from loading the whole inventory file. This change makes mkdocstrings ignore invalid lines in inventories so that the rest of the inventory can still be loaded. This continuation line behavior can be seen in the wild in the OpenEye toolkits inventory file and a few Open Force Field inventory files. These projects' inventories cannot be used with mkdocstrings because of the raised error. Note that in Sphinx too, these inventory files are read succesfully, and the continuation lines are discarded, truncating the display name. In theory, a continuation line that by chance did parse correctly would be interpreted by both packages as a new inventory item. OpenEye Toolkits inventory file: https://docs.eyesopen.com/toolkits/python/objects.inv Open Force Field Toolkit inventory file: https://docs.openforcefield.org/projects/toolkit/en/stable/objects.inv BespokeFit inventory file: https://docs.openforcefield.org/projects/bespokefit/en/stable/objects.inv --- src/mkdocstrings/_internal/inventory.py | 18 +++++++++++--- tests/test_inventory.py | 33 +++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/mkdocstrings/_internal/inventory.py b/src/mkdocstrings/_internal/inventory.py index 471e3633..241bbb12 100644 --- a/src/mkdocstrings/_internal/inventory.py +++ b/src/mkdocstrings/_internal/inventory.py @@ -8,7 +8,7 @@ import re import zlib from textwrap import dedent -from typing import TYPE_CHECKING, BinaryIO +from typing import TYPE_CHECKING, BinaryIO, Literal, overload if TYPE_CHECKING: from collections.abc import Collection @@ -66,11 +66,21 @@ def format_sphinx(self) -> str: sphinx_item_regex = re.compile(r"^(.+?)\s+(\S+):(\S+)\s+(-?\d+)\s+(\S+)\s*(.*)$") """Regex to parse a Sphinx v2 inventory line.""" + @overload @classmethod - def parse_sphinx(cls, line: str) -> InventoryItem: + def parse_sphinx(cls, line: str, *, return_none: Literal[False]) -> InventoryItem: ... + + @overload + @classmethod + def parse_sphinx(cls, line: str, *, return_none: Literal[True]) -> InventoryItem | None: ... + + @classmethod + def parse_sphinx(cls, line: str, *, return_none: bool = False) -> InventoryItem | None: """Parse a line from a Sphinx v2 inventory file and return an `InventoryItem` from it.""" match = cls.sphinx_item_regex.search(line) if not match: + if return_none: + return None raise ValueError(line) name, domain, role, priority, uri, dispname = match.groups() if uri.endswith("$"): @@ -167,7 +177,9 @@ def parse_sphinx(cls, in_file: BinaryIO, *, domain_filter: Collection[str] = ()) for _ in range(4): in_file.readline() lines = zlib.decompress(in_file.read()).splitlines() - items = [InventoryItem.parse_sphinx(line.decode("utf8")) for line in lines] + items: list[InventoryItem] = [ + item for line in lines if (item := InventoryItem.parse_sphinx(line.decode("utf8"), return_none=True)) + ] if domain_filter: items = [item for item in items if item.domain in domain_filter] return cls(items) diff --git a/tests/test_inventory.py b/tests/test_inventory.py index 8c2333d3..ab61e599 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -11,8 +11,6 @@ from mkdocstrings import Inventory, InventoryItem -sphinx = pytest.importorskip("sphinx.util.inventory", reason="Sphinx is not installed") - @pytest.mark.parametrize( "our_inv", @@ -21,10 +19,13 @@ Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url")]), Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#object_path")]), Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#other_anchor")]), + Inventory([InventoryItem(name="o", domain="py", role="obj", uri="u#o", dispname="first line\nsecond line")]), ], ) def test_sphinx_load_inventory_file(our_inv: Inventory) -> None: """Perform the 'live' inventory load test.""" + sphinx = pytest.importorskip("sphinx.util.inventory", reason="Sphinx is not installed") + buffer = BytesIO(our_inv.format_sphinx()) sphinx_inv = sphinx.InventoryFile.load(buffer, "", join) @@ -37,6 +38,8 @@ def test_sphinx_load_inventory_file(our_inv: Inventory) -> None: def test_sphinx_load_mkdocstrings_inventory_file() -> None: """Perform the 'live' inventory load test on mkdocstrings own inventory.""" + sphinx = pytest.importorskip("sphinx.util.inventory", reason="Sphinx is not installed") + mkdocs_config = load_config() mkdocs_config["plugins"].run_event("startup", command="build", dirty=False) try: @@ -53,3 +56,29 @@ def test_sphinx_load_mkdocstrings_inventory_file() -> None: for item in own_inv.values(): assert item.name in sphinx_inv[f"{item.domain}:{item.role}"] + + +@pytest.mark.parametrize( + "our_inv", + [ + Inventory(), + Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url")]), + Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#object_path")]), + Inventory([InventoryItem(name="object_path", domain="py", role="obj", uri="page_url#other_anchor")]), + Inventory([InventoryItem(name="o", domain="py", role="obj", uri="u#o", dispname="first line\nsecond line")]), + ], +) +def test_mkdocstrings_roundtrip_inventory_file(our_inv: Inventory) -> None: + """Save some inventory files, then load them in again.""" + buffer = BytesIO(our_inv.format_sphinx()) + round_tripped = Inventory.parse_sphinx(buffer) + + assert our_inv.keys() == round_tripped.keys() + for key, value in our_inv.items(): + round_tripped_item = round_tripped[key] + assert round_tripped_item.name == value.name + assert round_tripped_item.domain == value.domain + assert round_tripped_item.role == value.role + assert round_tripped_item.uri == value.uri + assert round_tripped_item.priority == value.priority + assert round_tripped_item.dispname == value.dispname.splitlines()[0] From 1a980402c39728ce265d8998b396c34bf76a113d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 31 Mar 2025 10:30:52 +0200 Subject: [PATCH 07/51] refactor: Rename loggers to "mkdocstrings" --- src/mkdocstrings/_internal/download.py | 2 +- src/mkdocstrings/_internal/extension.py | 2 +- src/mkdocstrings/_internal/handlers/base.py | 2 +- src/mkdocstrings/_internal/plugin.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mkdocstrings/_internal/download.py b/src/mkdocstrings/_internal/download.py index 2beb053a..ffe25e6b 100644 --- a/src/mkdocstrings/_internal/download.py +++ b/src/mkdocstrings/_internal/download.py @@ -9,7 +9,7 @@ from mkdocstrings._internal.loggers import get_logger -_logger = get_logger(__name__) +_logger = get_logger("mkdocstrings") # Regex pattern for an environment variable in the form ${ENV_VAR}. _ENV_VAR_PATTERN = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 182fc563..83421ff8 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -44,7 +44,7 @@ from mkdocs_autorefs import AutorefsPlugin -_logger = get_logger(__name__) +_logger = get_logger("mkdocstrings") class AutoDocProcessor(BlockProcessor): diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index cb1fffb3..3d5852c5 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -45,7 +45,7 @@ from markdown.extensions.toc import TocTreeprocessor from mkdocs_autorefs import AutorefsHookInterface, Backlink -_logger = get_logger(__name__) +_logger = get_logger("mkdocstrings") CollectorItem = Any """The type of the item returned by the `collect` method of a handler.""" diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index d7adf1c6..afc94490 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -35,7 +35,7 @@ from mkdocs.structure.files import Files -_logger = get_logger(__name__) +_logger = get_logger("mkdocstrings") class PluginConfig(Config): From df4e7c81bd5b2a8931c676adf2d916d06531a987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 31 Mar 2025 10:32:52 +0200 Subject: [PATCH 08/51] chore: Prepare release 0.29.1 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 826873b0..ed973133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.29.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.29.1) - 2025-03-31 + +[Compare with 0.29.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.0...0.29.1) + +### Dependencies + +- Remove unused typing-extensions dependency ([ba98661](https://github.com/mkdocstrings/mkdocstrings/commit/ba98661b50e2cde19d8696d6c8ceecdbb49ce83f) by Timothée Mazzucotelli). + +### Bug Fixes + +- Ignore invalid inventory lines ([81caff5](https://github.com/mkdocstrings/mkdocstrings/commit/81caff5ff76f1a6606da9d2980e81ae9d2e02246) by Josh Mitchell). [PR-748](https://github.com/mkdocstrings/mkdocstrings/pull/748) + +### Code Refactoring + +- Rename loggers to "mkdocstrings" ([1a98040](https://github.com/mkdocstrings/mkdocstrings/commit/1a980402c39728ce265d8998b396c34bf76a113d) by Timothée Mazzucotelli). + ## [0.29.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.29.0) - 2025-03-10 [Compare with 0.28.3](https://github.com/mkdocstrings/mkdocstrings/compare/0.28.3...0.29.0) From d5bf4e1ed0370853f968b210ad77913faf106eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 1 Apr 2025 14:05:31 +0200 Subject: [PATCH 09/51] docs: Update link to YAML idiosyncrasies --- docs/usage/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/index.md b/docs/usage/index.md index ea9716cc..d208b4b0 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -16,7 +16,7 @@ The syntax is as follows: > Here are some resources that other users found useful to better > understand YAML's peculiarities. > -> - [YAML idiosyncrasies](https://docs.saltproject.io/en/3000/topics/troubleshooting/yaml_idiosyncrasies.html) +> - [YAML idiosyncrasies](https://salt-zh.readthedocs.io/en/latest/topics/troubleshooting/yaml_idiosyncrasies.html) > - [YAML multiline](https://yaml-multiline.info/) The `identifier` is a string identifying the object you want to document. From b1da3d02c4f432f603cbb0004bb35099327706db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 30 Jun 2025 22:37:58 +0200 Subject: [PATCH 10/51] ci: Ignore Ruff warnings --- duties.py | 2 +- src/mkdocstrings/_internal/handlers/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/duties.py b/duties.py index 6ee9b08d..b75d8b55 100644 --- a/duties.py +++ b/duties.py @@ -238,7 +238,7 @@ def coverage(ctx: Context) -> None: @duty -def test(ctx: Context, *cli_args: str, match: str = "") -> None: +def test(ctx: Context, *cli_args: str, match: str = "") -> None: # noqa: PT028 """Run the test suite. Parameters: diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 3d5852c5..c87f473c 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -359,7 +359,7 @@ def get_templates_dir(self, handler: str | None = None) -> Path: """ handler = handler or self.name try: - import mkdocstrings_handlers + import mkdocstrings_handlers # noqa: PLC0415 except ModuleNotFoundError as error: raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error From 51f217f38af9475415f758866697158d9010967d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 30 Jun 2025 22:47:11 +0200 Subject: [PATCH 11/51] chore: Template upgrade --- .copier-answers.yml | 2 +- docs/css/mkdocstrings.css | 45 ++++++++++++++++++++++ docs/reference/{mkdocstrings.md => api.md} | 0 mkdocs.yml | 29 +++++++------- pyproject.toml | 4 +- tests/test_api.py | 6 ++- tests/test_extension.py | 1 - 7 files changed, 69 insertions(+), 18 deletions(-) rename docs/reference/{mkdocstrings.md => api.md} (100%) diff --git a/.copier-answers.yml b/.copier-answers.yml index bda5cd0c..fa6c4f9c 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.7.1 +_commit: 1.8.4 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css index 55321aa3..3447549c 100644 --- a/docs/css/mkdocstrings.css +++ b/docs/css/mkdocstrings.css @@ -35,3 +35,48 @@ td code { li.md-nav__item:has(> a[href*="("]) { display: none; } + +/* Tree-like output for backlinks. */ +.doc-backlink-list { + --tree-clr: var(--md-default-fg-color); + --tree-font-size: 1rem; + --tree-item-height: 1; + --tree-offset: 1rem; + --tree-thickness: 1px; + --tree-style: solid; + display: grid; + list-style: none !important; +} + +.doc-backlink-list li > span:first-child { + text-indent: .3rem; +} +.doc-backlink-list li { + padding-inline-start: var(--tree-offset); + border-left: var(--tree-thickness) var(--tree-style) var(--tree-clr); + position: relative; + margin-left: 0 !important; + + &:last-child { + border-color: transparent; + } + &::before{ + content: ''; + position: absolute; + top: calc(var(--tree-item-height) / 2 * -1 * var(--tree-font-size) + var(--tree-thickness)); + left: calc(var(--tree-thickness) * -1); + width: calc(var(--tree-offset) + var(--tree-thickness) * 2); + height: calc(var(--tree-item-height) * var(--tree-font-size)); + border-left: var(--tree-thickness) var(--tree-style) var(--tree-clr); + border-bottom: var(--tree-thickness) var(--tree-style) var(--tree-clr); + } + &::after{ + content: ''; + position: absolute; + border-radius: 50%; + background-color: var(--tree-clr); + top: calc(var(--tree-item-height) / 2 * 1rem); + left: var(--tree-offset) ; + translate: calc(var(--tree-thickness) * -1) calc(var(--tree-thickness) * -1); + } +} diff --git a/docs/reference/mkdocstrings.md b/docs/reference/api.md similarity index 100% rename from docs/reference/mkdocstrings.md rename to docs/reference/api.md diff --git a/mkdocs.yml b/mkdocs.yml index 828a81a4..dc53327f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,7 +34,7 @@ nav: - Guides: - Recipes: recipes.md - Troubleshooting: troubleshooting.md -- API reference: reference/mkdocstrings.md +- API reference: reference/api.md - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md @@ -143,6 +143,7 @@ plugins: - https://markupsafe.palletsprojects.com/en/stable/objects.inv paths: [src] options: + backlinks: tree docstring_options: ignore_init_summary: true docstring_section_style: list @@ -161,16 +162,17 @@ plugins: signature_crossrefs: true summary: true - llmstxt: - files: - - output: llms-full.txt - inputs: + full_output: llms-full.txt + sections: + Usage: - index.md - usage/index.md - usage/handlers.md - usage/theming.md - recipes.md - troubleshooting.md - - reference/**.md + API: + - reference/api.md - git-revision-date-localized: enabled: !ENV [DEPLOY, false] enable_creation_date: true @@ -179,14 +181,15 @@ plugins: redirect_maps: theming.md: usage/theming.md handlers/overview.md: usage/handlers.md - reference/index.md: reference/mkdocstrings.md#mkdocstrings - reference/extension.md: reference/mkdocstrings.md#mkdocstrings.extension - reference/handlers/index.md: reference/mkdocstrings.md#mkdocstrings.handlers - reference/handlers/base.md: reference/mkdocstrings.md#mkdocstrings.handlers.base - reference/handlers/rendering.md: reference/mkdocstrings.md#mkdocstrings.handlers.rendering - reference/inventory.md: reference/mkdocstrings.md#mkdocstrings.inventory - reference/loggers.md: reference/mkdocstrings.md#mkdocstrings.loggers - reference/plugin.md: reference/mkdocstrings.md#mkdocstrings.plugin + reference/mkdocstrings.md: reference/api.md + reference/index.md: reference/api.md#mkdocstrings + reference/extension.md: reference/api.md#mkdocstrings.extension + reference/handlers/index.md: reference/api.md#mkdocstrings.handlers + reference/handlers/base.md: reference/api.md#mkdocstrings.handlers.base + reference/handlers/rendering.md: reference/api.md#mkdocstrings.handlers.rendering + reference/inventory.md: reference/api.md#mkdocstrings.inventory + reference/loggers.md: reference/api.md#mkdocstrings.loggers + reference/plugin.md: reference/api.md#mkdocstrings.plugin - minify: minify_html: !ENV [DEPLOY, false] - group: diff --git a/pyproject.toml b/pyproject.toml index 29aff0d9..4db21462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ getter = "scripts.get_version:get_version" [tool.pdm.build] # Include as much as possible in the source distribution, to help redistributors. -excludes = ["**/.pytest_cache"] +excludes = ["**/.pytest_cache", "**/.mypy_cache"] source-includes = [ "config", "docs", @@ -112,7 +112,7 @@ ci = [ "mkdocs>=1.6", "mkdocs-coverage>=1.0", "mkdocs-git-revision-date-localized-plugin>=1.2", - "mkdocs-llmstxt>=0.1", + "mkdocs-llmstxt>=0.2", "mkdocs-material>=9.5", "mkdocs-minify-plugin>=0.8", "mkdocs-redirects>=1.2.1", diff --git a/tests/test_api.py b/tests/test_api.py index 4b13aac7..57f0ce20 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -155,7 +155,11 @@ def test_inventory_matches_api( public_api_paths = {obj.path for obj in public_objects} public_api_paths.add("mkdocstrings") for item in inventory.values(): - if item.domain == "py" and "(" not in item.name: + if ( + item.domain == "py" + and "(" not in item.name + and (item.name == "mkdocstrings" or item.name.startswith("mkdocstrings.")) + ): obj = loader.modules_collection[item.name] # YORE: Bump 1: Remove block. if any(obj.path.startswith(f"mkdocstrings.{module}") for module in deprecated_modules): diff --git a/tests/test_extension.py b/tests/test_extension.py index dd3d7028..c283f9c0 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -198,7 +198,6 @@ def test_removing_duplicated_headings(ext_markdown: Markdown) -> None: assert output.count(">Heading one<") == 1 assert output.count(">Heading two<") == 1 assert output.count(">Heading three<") == 1 - assert output.count('class="mkdocstrings') == 0 def _assert_contains_in_order(items: list[str], string: str) -> None: From 2b4ed541bc707e55d959092d950ebeecc4fbd136 Mon Sep 17 00:00:00 2001 From: Nyuan Zhang Date: Fri, 11 Jul 2025 00:19:09 +0800 Subject: [PATCH 12/51] feat: Add I18N support (translations) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-645: https://github.com/mkdocstrings/mkdocstrings/pull/645 Co-authored-by: Timothée Mazzucotelli --- docs/schema.json | 6 ++++++ docs/usage/index.md | 12 ++++++++++++ src/mkdocstrings/_internal/extension.py | 8 +++++++- src/mkdocstrings/_internal/handlers/base.py | 18 +++++++++++++++--- src/mkdocstrings/_internal/plugin.py | 15 ++++++++++++++- tests/test_plugin.py | 2 ++ 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/docs/schema.json b/docs/schema.json index bd646f88..66197827 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -28,6 +28,12 @@ "type": "string", "default": "python" }, + "locale": { + "title": "The locale to use for translations.", + "markdownDescription": "https://mkdocstrings.github.io/usage/#global-options", + "type": "string", + "default": null + }, "enable_inventory": { "title": "Whether to enable inventory file generation.", "markdownDescription": "https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories", diff --git a/docs/usage/index.md b/docs/usage/index.md index d208b4b0..64588fdf 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -113,6 +113,7 @@ The above is equivalent to: - `handlers`: The handlers' global configuration. - `enable_inventory`: Whether to enable inventory file generation. See [Cross-references to other projects / inventories](#cross-references-to-other-projects-inventories) +- `locale`: The locale used for translations. See [Internationalization](#internationalization-i18n). - `enabled` **(New in version 0.20)**: Whether to enable the plugin. Defaults to `true`. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below). @@ -124,6 +125,7 @@ The above is equivalent to: enabled: !ENV [ENABLE_MKDOCSTRINGS, true] custom_templates: templates default_handler: python + locale: en handlers: python: options: @@ -141,6 +143,16 @@ The above is equivalent to: Some handlers accept additional global configuration. Check the documentation for your handler of interest in [Handlers](handlers.md). +## Internationalization (I18N) + +Some handlers support multiple languages. + +If the handler supports localization, the locale it uses is determined by the following order of precedence: + +- `locale` in [global options](#global-options) +- `theme.language`: used by the [MkDocs Material theme](https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/) +- `theme.locale` in [MkDocs configuration](https://www.mkdocs.org/user-guide/configuration/#theme) + ## Cross-references Cross-references are written as Markdown *reference-style* links: diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 83421ff8..f67b5cfc 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -23,6 +23,8 @@ from __future__ import annotations import re +from functools import partial +from inspect import signature from typing import TYPE_CHECKING, Any from warnings import warn from xml.etree.ElementTree import Element @@ -197,8 +199,12 @@ def _process_block( self._updated_envs.add(handler_name) _logger.debug("Rendering templates") + if "locale" in signature(handler.render).parameters: + render = partial(handler.render, locale=self._handlers._locale) + else: + render = handler.render # type: ignore[assignment] try: - rendered = handler.render(data, options) + rendered = render(data, options) except TemplateNotFound as exc: _logger.error( # noqa: TRY400 "Template '%s' not found for '%s' handler and theme '%s'.", diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index c87f473c..b5641959 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -319,20 +319,29 @@ def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem: """ raise NotImplementedError - def render(self, data: CollectorItem, options: HandlerOptions) -> str: + def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str: """Render a template using provided data and configuration options. Arguments: data: The collected data to render. options: The final configuration options. + locale: The locale to use for translations, if any. Returns: The rendered template as HTML. """ raise NotImplementedError - def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]]) -> str: # noqa: ARG002 - """Render backlinks.""" + def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str: # noqa: ARG002 + """Render backlinks. + + Parameters: + backlinks: A mapping of identifiers to backlinks. + locale: The locale to use for translations, if any. + + Returns: + The rendered backlinks as HTML. + """ return "" def teardown(self) -> None: @@ -578,6 +587,7 @@ def __init__( custom_templates: str | None = None, mdx: Sequence[str | Extension] | None = None, mdx_config: Mapping[str, Any] | None = None, + locale: str = "en", tool_config: Any, ) -> None: """Initialize the object. @@ -591,6 +601,7 @@ def __init__( custom_templates: The path to custom templates. mdx: A list of Markdown extensions to use. mdx_config: Configuration for the Markdown extensions. + locale: The locale to use for translations. tool_config: Tool configuration to pass down to handlers. """ self._theme = theme @@ -600,6 +611,7 @@ def __init__( self._mdx = mdx or [] self._mdx_config = mdx_config or {} self._handlers: dict[str, BaseHandler] = {} + self._locale = locale self._tool_config = tool_config self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version) diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index afc94490..2af14a7a 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -15,6 +15,8 @@ import os import re +from functools import partial +from inspect import signature from re import Match from typing import TYPE_CHECKING, Any from warnings import catch_warnings, simplefilter @@ -73,6 +75,8 @@ class PluginConfig(Config): """Whether to enable object inventory creation.""" enabled = opt.Type(bool, default=True) """Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything.""" + locale = opt.Optional(opt.Type(str)) + """The locale to use for translations.""" class MkdocstringsPlugin(BasePlugin[PluginConfig]): @@ -131,6 +135,9 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: return config _logger.debug("Adding extension to the list") + locale = self.config.locale or config.theme.get("language") or config.theme.get("locale") or "en" + locale = str(locale).replace("_", "-") + handlers = Handlers( default=self.config.default_handler, handlers_config=self.config.handlers, @@ -140,6 +147,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: mdx_config=config.mdx_configs, inventory_project=config.site_name, inventory_version="0.0.0", # TODO: Find a way to get actual version. + locale=locale, tool_config=config, ) @@ -234,7 +242,12 @@ def repl(match: Match) -> str: if not backlinks: return "" - return handler.render_backlinks(backlinks) + if "locale" in signature(handler.render_backlinks).parameters: + render_backlinks = partial(handler.render_backlinks, locale=self.handlers._locale) + else: + render_backlinks = handler.render_backlinks # type: ignore[assignment] + + return render_backlinks(backlinks) for file in files: if file.page and file.page.content: diff --git a/tests/test_plugin.py b/tests/test_plugin.py index af8a5594..acb9556c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -57,6 +57,7 @@ def test_plugin_default_config(tmp_path: Path) -> None: "custom_templates": None, "enable_inventory": None, "enabled": True, + "locale": None, } @@ -77,4 +78,5 @@ def test_plugin_config_custom_templates(tmp_path: Path) -> None: "custom_templates": str(template_dir), "enable_inventory": None, "enabled": True, + "locale": None, } From f856160b03b2c27e1d75fdf4f315c273cb9d9247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 21 Jul 2025 20:49:21 +0200 Subject: [PATCH 13/51] feat: Add `data-skip-inventory` boolean attribute for elements to skip registration in local inventory Issue-671: https://github.com/mkdocstrings/mkdocstrings/issues/671 PR-774: https://github.com/mkdocstrings/mkdocstrings/pull/774 --- src/mkdocstrings/_internal/extension.py | 9 +++++++++ src/mkdocstrings/_internal/handlers/base.py | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index f67b5cfc..00b112d3 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -251,6 +251,15 @@ def _process_headings(self, handler: BaseHandler, element: Element) -> None: for heading in headings: rendered_id = heading.attrib["id"] + + skip_inventory = "data-skip-inventory" in heading.attrib + if skip_inventory: + _logger.debug( + "Skipping heading with id %r because data-skip-inventory is present", + rendered_id, + ) + continue + # The title is registered to be used as tooltip by autorefs. self._autorefs.register_anchor(page, rendered_id, title=heading.text, primary=True) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index b5641959..60910436 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -459,6 +459,7 @@ def do_heading( role: str | None = None, hidden: bool = False, toc_label: str | None = None, + skip_inventory: bool = False, **attributes: str, ) -> Markup: """Render an HTML heading and register it for the table of contents. For use inside templates. @@ -469,6 +470,7 @@ def do_heading( role: An optional role for the object bound to this heading. hidden: If True, only register it for the table of contents, don't render anything. toc_label: The title to use in the table of contents ('data-toc-label' attribute). + skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute). **attributes: Any extra HTML attributes of the heading. Returns: @@ -488,6 +490,8 @@ def do_heading( if toc_label is None: toc_label = content.unescape() if isinstance(content, Markup) else content el.set("data-toc-label", toc_label) + if skip_inventory: + el.set("data-skip-inventory", "true") if role: el.set("data-role", role) if content: From 2be445f054c1191d308af7ee0cc881e359f5a4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 23 Jul 2025 01:48:33 +0200 Subject: [PATCH 14/51] chore: Prepare release 0.30.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed973133..658ae74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.30.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.0) - 2025-07-23 + +[Compare with 0.29.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.1...0.30.0) + +### Features + +- Add `data-skip-inventory` boolean attribute for elements to skip registration in local inventory ([f856160](https://github.com/mkdocstrings/mkdocstrings/commit/f856160b03b2c27e1d75fdf4f315c273cb9d9247) by Bartosz Sławecki). [Issue-671](https://github.com/mkdocstrings/mkdocstrings/issues/671), [PR-774](https://github.com/mkdocstrings/mkdocstrings/pull/774) +- Add I18N support (translations) ([2b4ed54](https://github.com/mkdocstrings/mkdocstrings/commit/2b4ed541bc707e55d959092d950ebeecc4fbd136) by Nyuan Zhang). [PR-645](https://github.com/mkdocstrings/mkdocstrings/pull/645), Co-authored-by: Timothée Mazzucotelli + ## [0.29.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.29.1) - 2025-03-31 [Compare with 0.29.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.0...0.29.1) From 85f4479690f9845a2d3d6d228176f2cc391106d8 Mon Sep 17 00:00:00 2001 From: Deven Mistry <31466137+deven367@users.noreply.github.com> Date: Wed, 30 Jul 2025 11:16:18 -0400 Subject: [PATCH 15/51] docs: Fix broken NVidia link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8d4b8bb0..d46f9bf7 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdo [IBM](https://ds4sd.github.io/docling/api_reference/document_converter/), [Jitsi](https://jitsi.github.io/jiwer/reference/alignment/), [Microsoft](https://microsoft.github.io/presidio/api/analyzer_python/), -[NVIDIA](https://nvidia.github.io/bionemo-framework/API_reference/bionemo/core/api/), +[NVIDIA](https://nvidia.github.io/bionemo-framework/main/references/API_reference/bionemo/core/api/), [Prefect](https://docs.prefect.io/2.10.12/api-ref/prefect/agent/), [Pydantic](https://docs.pydantic.dev/dev-v2/api/main/), [Textual](https://textual.textualize.io/api/app/), From 572677173c2d31e5779f6313e48e90cf265b9c62 Mon Sep 17 00:00:00 2001 From: Mark Shui Hu Date: Sun, 24 Aug 2025 17:29:27 +0200 Subject: [PATCH 16/51] doc: Add links to MATLAB handler PR-789: https://github.com/mkdocstrings/mkdocstrings/pull/789 --- README.md | 1 + docs/usage/handlers.md | 1 + mkdocs.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/README.md b/README.md index d46f9bf7..cb6887e6 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdo [C](https://mkdocstrings.github.io/c/), [Crystal](https://mkdocstrings.github.io/crystal/), [Python](https://mkdocstrings.github.io/python/), + [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/), [TypeScript](https://mkdocstrings.github.io/typescript/), and [VBA](https://pypi.org/project/mkdocstrings-vba/) languages, as well as for [shell scripts/libraries](https://mkdocstrings.github.io/shell/). diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index 0d375a95..c4bbda57 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -8,6 +8,7 @@ A handler is what makes it possible to collect and render documentation for a pa - [Crystal](https://mkdocstrings.github.io/crystal/){ .external } - [Python](https://mkdocstrings.github.io/python/){ .external } - [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/){ .external } +- [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/){ .external } - [Shell](https://mkdocstrings.github.io/shell/){ .external } - [TypeScript](https://mkdocstrings.github.io/typescript/){ .external } - [VBA](https://pypi.org/project/mkdocstrings-vba/){ .external } diff --git a/mkdocs.yml b/mkdocs.yml index dc53327f..9163e91d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,7 @@ nav: - Crystal: https://mkdocstrings.github.io/crystal/ - Python: https://mkdocstrings.github.io/python/ - Python (Legacy): https://mkdocstrings.github.io/python-legacy/ + - MATLAB: https://watermarkhu.nl/mkdocstrings-matlab/ - Shell: https://mkdocstrings.github.io/shell/ - TypeScript: https://mkdocstrings.github.io/typescript/ - VBA: https://pypi.org/project/mkdocstrings-vba From eec7fb4bab948ef6db594fc1d1688be0554c5780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Flar=20Kutlu?= Date: Mon, 15 Sep 2025 12:14:26 +0200 Subject: [PATCH 17/51] fix: Create default SSL context in main thread before downloading inventories Issue-796: https://github.com/mkdocstrings/mkdocstrings/issue/796 PR-797: https://github.com/mkdocstrings/mkdocstrings/pull/797 --- src/mkdocstrings/_internal/handlers/base.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 60910436..c4e9950d 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -7,6 +7,7 @@ import datetime import importlib import inspect +import ssl import sys from concurrent import futures from io import BytesIO @@ -753,6 +754,11 @@ def _download_inventories(self) -> None: to_download.extend((handler, url, conf) for url, conf in inv_configs) if to_download: + # YORE: EOL 3.12: Remove block. + # NOTE: Create context in main thread to fix issue + # https://github.com/mkdocstrings/mkdocstrings/issues/796. + _ = ssl.create_default_context() + thread_pool = futures.ThreadPoolExecutor(4) for handler, url, conf in to_download: _logger.debug("Downloading inventory from %s", url) From 18f650441629ca56a30befccb5fb0b3a361aff11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 15 Sep 2025 12:17:01 +0200 Subject: [PATCH 18/51] ci: Fix warning --- src/mkdocstrings/_internal/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 00b112d3..2277775c 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -125,7 +125,7 @@ def run(self, parent: Element, blocks: MutableSequence[str]) -> None: heading_level = match["heading"].count("#") _logger.debug("Matched '::: %s'", identifier) - html, handler, data = self._process_block(identifier, block, heading_level) + html, handler, _ = self._process_block(identifier, block, heading_level) el = Element("div", {"class": "mkdocstrings"}) # The final HTML is inserted as opaque to subsequent processing, and only revealed at the end. el.text = self.md.htmlStash.store(html) From b550cdb6be37a65b487154b0edbd5cedc822b4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 19 Sep 2025 12:49:13 +0200 Subject: [PATCH 19/51] chore: Prepare release 0.30.1 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 658ae74f..6346ccd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.30.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.1) - 2025-09-19 + +[Compare with 0.30.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.0...0.30.1) + +### Bug Fixes + +- Create default SSL context in main thread before downloading inventories ([eec7fb4](https://github.com/mkdocstrings/mkdocstrings/commit/eec7fb4bab948ef6db594fc1d1688be0554c5780) by Çağlar Kutlu). [Issue-796](https://github.com/mkdocstrings/mkdocstrings/issue/796), [PR-797](https://github.com/mkdocstrings/mkdocstrings/pull/797) + ## [0.30.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.0) - 2025-07-23 [Compare with 0.29.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.1...0.30.0) From 438be895b5edfe92121b2bd3a0654959ca343d4f Mon Sep 17 00:00:00 2001 From: Mark Shui Hu Date: Sun, 28 Sep 2025 18:00:21 +0200 Subject: [PATCH 20/51] docs: Add links to GitHub Actions handler PR-800: https://github.com/mkdocstrings/mkdocstrings/pull/800 --- README.md | 1 + docs/usage/handlers.md | 1 + mkdocs.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/README.md b/README.md index cb6887e6..23d3246c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdo We currently have [handlers](https://mkdocstrings.github.io/handlers/overview/) for the [C](https://mkdocstrings.github.io/c/), [Crystal](https://mkdocstrings.github.io/crystal/), + [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/), [Python](https://mkdocstrings.github.io/python/), [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/), [TypeScript](https://mkdocstrings.github.io/typescript/), and diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index c4bbda57..efdaccd1 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -6,6 +6,7 @@ A handler is what makes it possible to collect and render documentation for a pa - [C](https://mkdocstrings.github.io/c/){ .external } - [Crystal](https://mkdocstrings.github.io/crystal/){ .external } +- [GitHub Actions](https://watermarkhu.nl/mkdocstrings-github/){ .external } - [Python](https://mkdocstrings.github.io/python/){ .external } - [Python (Legacy)](https://mkdocstrings.github.io/python-legacy/){ .external } - [MATLAB](https://watermarkhu.nl/mkdocstrings-matlab/){ .external } diff --git a/mkdocs.yml b/mkdocs.yml index 9163e91d..52721b3e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,6 +26,7 @@ nav: - All handlers: - C: https://mkdocstrings.github.io/c/ - Crystal: https://mkdocstrings.github.io/crystal/ + - GitHub Actions: https://watermarkhu.nl/mkdocstrings-github/ - Python: https://mkdocstrings.github.io/python/ - Python (Legacy): https://mkdocstrings.github.io/python-legacy/ - MATLAB: https://watermarkhu.nl/mkdocstrings-matlab/ From 75ff96877a7602a6ae02839b00ebc03e5b8fc91d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 9 Oct 2025 13:15:07 +0200 Subject: [PATCH 21/51] chore: Template upgrade --- .copier-answers.yml | 2 +- .github/pull_request_template.md | 15 ++++++++ .github/workflows/ci.yml | 29 ++++++++++++-- Makefile | 7 ++++ README.md | 2 +- config/pytest.ini | 4 ++ config/pytest_39.ini | 19 ++++++++++ docs/js/insiders.js | 13 ++++--- duties.py | 23 +++++++---- scripts/get_version.py | 7 +++- scripts/make.py | 59 +++++++++++++++++++++++------ src/mkdocstrings/_internal/debug.py | 2 +- tests/test_api.py | 6 ++- 13 files changed, 154 insertions(+), 34 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 config/pytest_39.ini diff --git a/.copier-answers.yml b/.copier-answers.yml index fa6c4f9c..e200114f 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.8.4 +_commit: 1.10.1 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..6f0f2faf --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +### For reviewers + + +- [ ] I did not use AI +- [ ] I used AI and thoroughly reviewed every code/docs change + +### Description of the change + + +### Relevant resources + + +- +- +- diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dd6da41..b721b7a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,10 +2,16 @@ name: ci on: push: + - main + - test-me-* pull_request: branches: - main +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + defaults: run: shell: bash @@ -15,13 +21,30 @@ env: LC_ALL: en_US.utf-8 PYTHONIOENCODING: UTF-8 PYTHONPATH: docs + PYTHONWARNDEFAULTENCODING: "1" PYTHON_VERSIONS: "" jobs: quality: + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + python-version: + - "3.9" + - "3.13" + include: + - os: ubuntu-latest + python-version: "3.10" + - os: ubuntu-latest + python-version: "3.11" + - os: ubuntu-latest + python-version: "3.12" - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -33,7 +56,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Setup uv uses: astral-sh/setup-uv@v5 @@ -109,7 +132,7 @@ jobs: - lowest-direct exclude: ${{ fromJSON(needs.exclude-test-jobs.outputs.jobs) }} runs-on: ${{ matrix.os }} - continue-on-error: ${{ matrix.python-version == '3.14' }} + continue-on-error: true steps: - name: Checkout diff --git a/Makefile b/Makefile index 5e88121d..1b3391da 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,13 @@ # the `make` command will point at the `scripts/make` shell script. # This Makefile is just here to allow auto-completion in the terminal. +default: help + @echo + @echo 'Enable direnv in your shell to use the `make` command: `direnv allow`' + @echo 'Or use `python scripts/make ARGS` to run the commands/tasks directly.' + +.DEFAULT_GOAL: default + actions = \ allrun \ changelog \ diff --git a/README.md b/README.md index 23d3246c..af264b77 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://mkdocstrings.github.io/) [![pypi version](https://img.shields.io/pypi/v/mkdocstrings.svg)](https://pypi.org/project/mkdocstrings/) [![conda version](https://img.shields.io/conda/vn/conda-forge/mkdocstrings)](https://anaconda.org/conda-forge/mkdocstrings) -[![gitter](https://badges.gitter.im/join%20chat.svg)](https://app.gitter.im/#/room/#mkdocstrings:gitter.im) +[![gitter](https://img.shields.io/badge/matrix-chat-4DB798.svg?style=flat)](https://app.gitter.im/#/room/#mkdocstrings:gitter.im) Automatic documentation from sources, for [MkDocs](https://www.mkdocs.org/). Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdocstrings/community). diff --git a/config/pytest.ini b/config/pytest.ini index 288b6cff..f65bd620 100644 --- a/config/pytest.ini +++ b/config/pytest.ini @@ -10,5 +10,9 @@ testpaths = # action:message_regex:warning_class:module_regex:line filterwarnings = error + default::EncodingWarning + error::EncodingWarning:mkdocstrings ignore:.*`get_anchors` method:DeprecationWarning:mkdocstrings ignore:.*Importing from:DeprecationWarning:mkdocstrings_handlers + # TODO: Remove once pytest-xdist 4 is released. + ignore:.*rsyncdir:DeprecationWarning:xdist diff --git a/config/pytest_39.ini b/config/pytest_39.ini new file mode 100644 index 00000000..27f45425 --- /dev/null +++ b/config/pytest_39.ini @@ -0,0 +1,19 @@ +# YORE: EOL 3.9: Remove file. +# This file is used on 3.9 due to forward compatibility issue with filterwarnings. +# See https://github.com/pytest-dev/pytest/issues/11101. +[pytest] +python_files = + test_*.py +addopts = + --cov + --cov-config config/coverage.ini +testpaths = + tests + +# action:message_regex:warning_class:module_regex:line +filterwarnings = + error + ignore:.*`get_anchors` method:DeprecationWarning:mkdocstrings + ignore:.*Importing from:DeprecationWarning:mkdocstrings_handlers + # TODO: Remove once pytest-xdist 4 is released. + ignore:.*rsyncdir:DeprecationWarning:xdist diff --git a/docs/js/insiders.js b/docs/js/insiders.js index 8bb68485..a86a0918 100644 --- a/docs/js/insiders.js +++ b/docs/js/insiders.js @@ -29,11 +29,14 @@ function updatePremiumSponsors(dataURL, rank) { let html = ''; html += `${capRank} sponsors

` sponsors.forEach(function (sponsor) { - html += ` - - ${sponsor.name} - - ` + html += `` + if (sponsor.image) { + html += `${sponsor.name}` + } else if (sponsor.imageLight && sponsor.imageDark) { + html += `${sponsor.name}` + html += `${sponsor.name}` + } + html += ''; }); html += '

' sponsorsDiv.innerHTML = html; diff --git a/duties.py b/duties.py index b75d8b55..ee20b497 100644 --- a/duties.py +++ b/duties.py @@ -26,6 +26,8 @@ WINDOWS = os.name == "nt" PTY = not WINDOWS and not CI MULTIRUN = os.environ.get("MULTIRUN", "0") == "1" +PY_VERSION = f"{sys.version_info.major}{sys.version_info.minor}" +PY_DEV = "314" def pyprefix(title: str) -> str: @@ -84,7 +86,7 @@ def check(ctx: Context) -> None: """Check it all!""" -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_quality(ctx: Context) -> None: """Check the code quality.""" ctx.run( @@ -93,7 +95,7 @@ def check_quality(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_docs(ctx: Context) -> None: """Check if the documentation builds correctly.""" Path("htmlcov").mkdir(parents=True, exist_ok=True) @@ -105,7 +107,7 @@ def check_docs(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" os.environ["MYPYPATH"] = "src" @@ -116,7 +118,7 @@ def check_types(ctx: Context) -> None: ) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def check_api(ctx: Context, *cli_args: str) -> None: """Check for API breaking changes.""" ctx.run( @@ -237,19 +239,24 @@ def coverage(ctx: Context) -> None: ctx.run(tools.coverage.html(rcfile="config/coverage.ini")) -@duty +@duty(nofail=PY_VERSION == PY_DEV) def test(ctx: Context, *cli_args: str, match: str = "") -> None: # noqa: PT028 """Run the test suite. Parameters: match: A pytest expression to filter selected tests. """ - py_version = f"{sys.version_info.major}{sys.version_info.minor}" - os.environ["COVERAGE_FILE"] = f".coverage.{py_version}" + os.environ["COVERAGE_FILE"] = f".coverage.{PY_VERSION}" + os.environ["PYTHONWARNDEFAULTENCODING"] = "1" + config_file = "config/pytest.ini" + # YORE: EOL 3.9: Remove block. + if sys.version_info[:2] < (3, 10): + config_file = "config/pytest_39.ini" + ctx.run( tools.pytest( "tests", - config_file="config/pytest.ini", + config_file=config_file, select=match, color="yes", ).add_args("-n", "auto", *cli_args), diff --git a/scripts/get_version.py b/scripts/get_version.py index 6734e5b6..3c425a73 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -4,7 +4,12 @@ from contextlib import suppress from pathlib import Path -from pdm.backend.hooks.version import SCMVersion, Version, default_version_formatter, get_version_from_scm +from pdm.backend.hooks.version import ( # ty: ignore[unresolved-import] + SCMVersion, + Version, + default_version_formatter, + get_version_from_scm, +) _root = Path(__file__).parent.parent _changelog = _root / "CHANGELOG.md" diff --git a/scripts/make.py b/scripts/make.py index 55679baa..1e697bcc 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -15,6 +15,7 @@ PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.9 3.10 3.11 3.12 3.13 3.14").split() +PYTHON_DEV = "3.14" def shell(cmd: str, *, capture_output: bool = False, **kwargs: Any) -> str | None: @@ -67,16 +68,31 @@ def setup() -> None: uv_install(venv_path) +class _RunError(subprocess.CalledProcessError): + def __init__(self, *args: Any, python_version: str, **kwargs: Any): + super().__init__(*args, **kwargs) + self.python_version = python_version + + def run(version: str, cmd: str, *args: str, **kwargs: Any) -> None: """Run a command in a virtual environment.""" kwargs = {"check": True, **kwargs} uv_run = ["uv", "run", "--no-sync"] - if version == "default": - with environ(UV_PROJECT_ENVIRONMENT=".venv"): - subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 - else: - with environ(UV_PROJECT_ENVIRONMENT=f".venvs/{version}", MULTIRUN="1"): - subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 + try: + if version == "default": + with environ(UV_PROJECT_ENVIRONMENT=".venv"): + subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 + else: + with environ(UV_PROJECT_ENVIRONMENT=f".venvs/{version}", MULTIRUN="1"): + subprocess.run([*uv_run, cmd, *args], **kwargs) # noqa: S603, PLW1510 + except subprocess.CalledProcessError as process: + raise _RunError( + returncode=process.returncode, + python_version=version, + cmd=process.cmd, + output=process.output, + stderr=process.stderr, + ) from process def multirun(cmd: str, *args: str, **kwargs: Any) -> None: @@ -144,19 +160,31 @@ def main() -> int: cmd = args.pop(0) if cmd == "run": - run("default", *args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + run("default", *args) # ty: ignore[missing-argument] return 0 if cmd == "multirun": - multirun(*args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + multirun(*args) # ty: ignore[missing-argument] return 0 if cmd == "allrun": - allrun(*args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + allrun(*args) # ty: ignore[missing-argument] return 0 if cmd.startswith("3."): - run(cmd, *args) + if not args: + print("make: run: missing command", file=sys.stderr) + return 1 + run(cmd, *args) # ty: ignore[missing-argument] return 0 opts = [] @@ -183,7 +211,14 @@ def main() -> int: if __name__ == "__main__": try: sys.exit(main()) - except subprocess.CalledProcessError as process: + except _RunError as process: if process.output: print(process.output, file=sys.stderr) - sys.exit(process.returncode) + if (code := process.returncode) == 139: # noqa: PLR2004 + print( + f"✗ (python{process.python_version}) '{' '.join(process.cmd)}' failed with return code {code} (segfault)", + file=sys.stderr, + ) + if process.python_version == PYTHON_DEV: + code = 0 + sys.exit(code) diff --git a/src/mkdocstrings/_internal/debug.py b/src/mkdocstrings/_internal/debug.py index 7b56409b..f6b11600 100644 --- a/src/mkdocstrings/_internal/debug.py +++ b/src/mkdocstrings/_internal/debug.py @@ -85,7 +85,7 @@ def _get_debug_info() -> _Environment: interpreter_version=py_version, interpreter_path=sys.executable, platform=platform.platform(), - variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], + variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], # ty: ignore[invalid-argument-type] packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) diff --git a/tests/test_api.py b/tests/test_api.py index 57f0ce20..ea672073 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -91,7 +91,7 @@ def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | g def _fixture_inventory() -> Inventory: inventory_file = Path(__file__).parent.parent / "site" / "objects.inv" if not inventory_file.exists(): - raise pytest.skip("The objects inventory is not available.") + pytest.skip("The objects inventory is not available.") # ty: ignore[call-non-callable] with inventory_file.open("rb") as file: return Inventory.parse_sphinx(file) @@ -137,7 +137,9 @@ def test_api_matches_inventory(inventory: Inventory, public_objects: list[griffe """All public objects are added to the inventory.""" ignore_names = {"__getattr__", "__init__", "__repr__", "__str__", "__post_init__"} not_in_inventory = [ - obj.path for obj in public_objects if obj.name not in ignore_names and obj.path not in inventory + f"{obj.relative_filepath}:{obj.lineno}: {obj.path}" + for obj in public_objects + if obj.name not in ignore_names and obj.path not in inventory ] msg = "Objects not in the inventory (try running `make run mkdocs build`):\n{paths}" assert not not_in_inventory, msg.format(paths="\n".join(sorted(not_in_inventory))) From 5698963317af8e5d790a558fd945371f6ef3811a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 9 Oct 2025 17:52:31 +0200 Subject: [PATCH 22/51] chore: Template upgrade --- .copier-answers.yml | 2 +- .github/workflows/ci.yml | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index e200114f..f459d439 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.10.1 +_commit: 1.10.2 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b721b7a5..253c08f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,8 +2,9 @@ name: ci on: push: - - main - - test-me-* + branches: + - main + - test-me-* pull_request: branches: - main @@ -36,13 +37,13 @@ jobs: python-version: - "3.9" - "3.13" - include: - - os: ubuntu-latest - python-version: "3.10" - - os: ubuntu-latest - python-version: "3.11" - - os: ubuntu-latest - python-version: "3.12" + include: + - os: ubuntu-latest + python-version: "3.10" + - os: ubuntu-latest + python-version: "3.11" + - os: ubuntu-latest + python-version: "3.12" runs-on: ${{ matrix.os }} From b8f35c14f1b93408096cd2289782159beb0cdf03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Nov 2025 12:01:43 +0100 Subject: [PATCH 23/51] chore: Template upgrade --- .copier-answers.yml | 6 +- .github/workflows/ci.yml | 46 +++------ .github/workflows/release.yml | 21 +--- .github/workflows/sponsors.yml | 26 +++++ README.md | 5 + config/pytest_39.ini | 19 ---- docs/.overrides/main.html | 12 +-- docs/css/insiders.css | 124 ----------------------- docs/insiders/changelog.md | 3 - docs/insiders/goals.yml | 13 --- docs/insiders/index.md | 166 ------------------------------- docs/insiders/installation.md | 67 ------------- docs/js/insiders.js | 77 --------------- duties.py | 70 ++----------- mkdocs.yml | 8 +- pyproject.toml | 3 +- scripts/insiders.py | 173 --------------------------------- scripts/make.py | 4 +- 18 files changed, 65 insertions(+), 778 deletions(-) create mode 100644 .github/workflows/sponsors.yml delete mode 100644 config/pytest_39.ini delete mode 100644 docs/css/insiders.css delete mode 100644 docs/insiders/changelog.md delete mode 100644 docs/insiders/goals.yml delete mode 100644 docs/insiders/index.md delete mode 100644 docs/insiders/installation.md delete mode 100644 docs/js/insiders.js delete mode 100644 scripts/insiders.py diff --git a/.copier-answers.yml b/.copier-answers.yml index f459d439..6c5d650e 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.10.2 +_commit: 1.11.1 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli @@ -8,12 +8,8 @@ copyright_date: '2019' copyright_holder: Timothée Mazzucotelli copyright_holder_email: dev@pawamoy.fr copyright_license: ISC -insiders: true -insiders_email: insiders@pawamoy.fr -insiders_repository_name: mkdocstrings project_description: Automatic documentation from sources, for MkDocs. project_name: mkdocstrings -public_release: true python_package_command_line_name: '' python_package_distribution_name: mkdocstrings python_package_import_name: mkdocstrings diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 253c08f9..54dd741c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,15 +35,15 @@ jobs: - macos-latest - windows-latest python-version: - - "3.9" - - "3.13" + - "3.10" + - "3.14" include: - - os: ubuntu-latest - python-version: "3.10" - os: ubuntu-latest python-version: "3.11" - os: ubuntu-latest python-version: "3.12" + - os: ubuntu-latest + python-version: "3.13" runs-on: ${{ matrix.os }} @@ -55,7 +55,7 @@ jobs: fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -82,39 +82,15 @@ jobs: - name: Store objects inventory for tests uses: actions/upload-artifact@v4 + if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == '3.13' }} with: name: objects.inv path: site/objects.inv - exclude-test-jobs: - runs-on: ubuntu-latest - outputs: - jobs: ${{ steps.exclude-jobs.outputs.jobs }} - steps: - - id: exclude-jobs - run: | - if ${{ github.repository_owner == 'pawamoy-insiders' }}; then - echo 'jobs=[ - {"os": "macos-latest"}, - {"os": "windows-latest"}, - {"python-version": "3.10"}, - {"python-version": "3.11"}, - {"python-version": "3.12"}, - {"python-version": "3.13"}, - {"python-version": "3.14"} - ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT - else - echo 'jobs=[ - {"os": "macos-latest", "resolution": "lowest-direct"}, - {"os": "windows-latest", "resolution": "lowest-direct"} - ]' | tr -d '[:space:]' >> $GITHUB_OUTPUT - fi - tests: needs: - quality - - exclude-test-jobs strategy: matrix: os: @@ -122,16 +98,20 @@ jobs: - macos-latest - windows-latest python-version: - - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" + - "3.15" resolution: - highest - lowest-direct - exclude: ${{ fromJSON(needs.exclude-test-jobs.outputs.jobs) }} + exclude: + - os: macos-latest + resolution: lowest-direct + - os: windows-latest + resolution: lowest-direct runs-on: ${{ matrix.os }} continue-on-error: true @@ -143,7 +123,7 @@ jobs: fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} allow-prereleases: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73347dad..1c7cda36 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,30 +15,15 @@ jobs: fetch-depth: 0 fetch-tags: true - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: "3.13" - name: Setup uv uses: astral-sh/setup-uv@v5 - - name: Build dists - if: github.repository_owner == 'pawamoy-insiders' - run: uv tool run --from build pyproject-build - - name: Upload dists artifact - uses: actions/upload-artifact@v4 - if: github.repository_owner == 'pawamoy-insiders' - with: - name: mkdocstrings-insiders - path: ./dist/* - name: Prepare release notes - if: github.repository_owner != 'pawamoy-insiders' run: uv tool run git-changelog --release-notes > release-notes.md - - name: Create release with assets - uses: softprops/action-gh-release@v2 - if: github.repository_owner == 'pawamoy-insiders' - with: - files: ./dist/* - name: Create release uses: softprops/action-gh-release@v2 - if: github.repository_owner != 'pawamoy-insiders' with: body_path: release-notes.md + diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml new file mode 100644 index 00000000..8dd9150f --- /dev/null +++ b/.github/workflows/sponsors.yml @@ -0,0 +1,26 @@ +name: Update sponsors + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-readme: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Update README and create PR + uses: pawamoy/readme-insert@main + with: + markup-url: https://pawamoy.github.io/sponsors.txt + start-marker: '' + end-marker: '' + commit-message: 'chore: Update sponsors section in README' + pr-title: 'chore: Update sponsors section in README' + pr-body: 'This PR updates the sponsors section in the README file.' diff --git a/README.md b/README.md index af264b77..20f6fd39 100644 --- a/README.md +++ b/README.md @@ -133,3 +133,8 @@ In one of your markdown files: ``` See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for more examples! + +## Sponsors + + + diff --git a/config/pytest_39.ini b/config/pytest_39.ini deleted file mode 100644 index 27f45425..00000000 --- a/config/pytest_39.ini +++ /dev/null @@ -1,19 +0,0 @@ -# YORE: EOL 3.9: Remove file. -# This file is used on 3.9 due to forward compatibility issue with filterwarnings. -# See https://github.com/pytest-dev/pytest/issues/11101. -[pytest] -python_files = - test_*.py -addopts = - --cov - --cov-config config/coverage.ini -testpaths = - tests - -# action:message_regex:warning_class:module_regex:line -filterwarnings = - error - ignore:.*`get_anchors` method:DeprecationWarning:mkdocstrings - ignore:.*Importing from:DeprecationWarning:mkdocstrings_handlers - # TODO: Remove once pytest-xdist 4 is released. - ignore:.*rsyncdir:DeprecationWarning:xdist diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html index 1e956857..c702362f 100644 --- a/docs/.overrides/main.html +++ b/docs/.overrides/main.html @@ -1,13 +1,11 @@ {% extends "base.html" %} {% block announce %} - - Fund this project through - sponsorship - - {% include ".icons/octicons/heart-fill-16.svg" %} - — - + Fund this project through + sponsorship + + {% include ".icons/octicons/heart-fill-16.svg" %} + — Follow @pawamoy on diff --git a/docs/css/insiders.css b/docs/css/insiders.css deleted file mode 100644 index e7b9c74f..00000000 --- a/docs/css/insiders.css +++ /dev/null @@ -1,124 +0,0 @@ -@keyframes heart { - - 0%, - 40%, - 80%, - 100% { - transform: scale(1); - } - - 20%, - 60% { - transform: scale(1.15); - } -} - -@keyframes vibrate { - 0%, 2%, 4%, 6%, 8%, 10%, 12%, 14%, 16%, 18% { - -webkit-transform: translate3d(-2px, 0, 0); - transform: translate3d(-2px, 0, 0); - } - 1%, 3%, 5%, 7%, 9%, 11%, 13%, 15%, 17%, 19% { - -webkit-transform: translate3d(2px, 0, 0); - transform: translate3d(2px, 0, 0); - } - 20%, 100% { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.heart { - color: #e91e63; -} - -.pulse { - animation: heart 1000ms infinite; -} - -.vibrate { - animation: vibrate 2000ms infinite; -} - -.new-feature svg { - fill: var(--md-accent-fg-color) !important; -} - -a.insiders { - color: #e91e63; -} - -.sponsorship-list { - width: 100%; -} - -.sponsorship-item { - border-radius: 100%; - display: inline-block; - height: 1.6rem; - margin: 0.1rem; - overflow: hidden; - width: 1.6rem; -} - -.sponsorship-item:focus, .sponsorship-item:hover { - transform: scale(1.1); -} - -.sponsorship-item img { - filter: grayscale(100%) opacity(75%); - height: auto; - width: 100%; -} - -.sponsorship-item:focus img, .sponsorship-item:hover img { - filter: grayscale(0); -} - -.sponsorship-item.private { - background: var(--md-default-fg-color--lightest); - color: var(--md-default-fg-color); - font-size: .6rem; - font-weight: 700; - line-height: 1.6rem; - text-align: center; -} - -.mastodon { - color: #897ff8; - border-radius: 100%; - box-shadow: inset 0 0 0 .05rem currentcolor; - display: inline-block; - height: 1.2rem !important; - padding: .25rem; - transition: all .25s; - vertical-align: bottom !important; - width: 1.2rem; -} - -.premium-sponsors { - text-align: center; -} - -#silver-sponsors img { - height: 140px; -} - -#bronze-sponsors img { - height: 140px; -} - -#bronze-sponsors p { - display: flex; - flex-wrap: wrap; - justify-content: center; -} - -#bronze-sponsors a { - display: block; - flex-shrink: 0; -} - -.sponsors-total { - font-weight: bold; -} \ No newline at end of file diff --git a/docs/insiders/changelog.md b/docs/insiders/changelog.md deleted file mode 100644 index 0f438566..00000000 --- a/docs/insiders/changelog.md +++ /dev/null @@ -1,3 +0,0 @@ -# Changelog - -## mkdocstrings Insiders diff --git a/docs/insiders/goals.yml b/docs/insiders/goals.yml deleted file mode 100644 index 0e27b997..00000000 --- a/docs/insiders/goals.yml +++ /dev/null @@ -1,13 +0,0 @@ -goals: - 500: - name: PlasmaVac User Guide - features: [] - 1000: - name: GraviFridge Fluid Renewal - features: [] - 1500: - name: HyperLamp Navigation Tips - features: [] - 2000: - name: FusionDrive Ejection Configuration - features: [] diff --git a/docs/insiders/index.md b/docs/insiders/index.md deleted file mode 100644 index ce59f6bb..00000000 --- a/docs/insiders/index.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Insiders ---- - -# Insiders - -*mkdocstrings* follows the **sponsorware** release strategy, which means that new features are first exclusively released to sponsors as part of [Insiders][]. Read on to learn [what sponsorships achieve][sponsorship], [how to become a sponsor][sponsors] to get access to Insiders, and [what's in it for you][features]! - -## What is Insiders? - -*mkdocstrings Insiders* is a private fork of *mkdocstrings*, hosted as a private GitHub repository. Almost[^1] [all new features][features] are developed as part of this fork, which means that they are immediately available to all eligible sponsors, as they are granted access to this private repository. - -[^1]: In general, every new feature is first exclusively released to sponsors, but sometimes upstream dependencies enhance existing features that must be supported by *mkdocstrings*. - -Every feature is tied to a [funding goal][funding] in monthly subscriptions. When a funding goal is hit, the features that are tied to it are merged back into *mkdocstrings* and released for general availability, making them available to all users. Bugfixes are always released in tandem. - -Sponsorships start as low as [**$10 a month**][sponsors].[^2] - -[^2]: Note that $10 a month is the minimum amount to become eligible for Insiders. While GitHub Sponsors also allows to sponsor lower amounts or one-time amounts, those can't be granted access to Insiders due to technical reasons. Such contributions are still very much welcome as they help ensuring the project's sustainability. - -## What sponsorships achieve - -Sponsorships make this project sustainable, as they buy the maintainers of this project time – a very scarce resource – which is spent on the development of new features, bug fixing, stability improvement, issue triage and general support. The biggest bottleneck in Open Source is time.[^3] - -[^3]: Making an Open Source project sustainable is exceptionally hard: maintainers burn out, projects are abandoned. That's not great and very unpredictable. The sponsorware model ensures that if you decide to use *mkdocstrings*, you can be sure that bugs are fixed quickly and new features are added regularly. - -If you're unsure if you should sponsor this project, check out the list of [completed funding goals][goals completed] to learn whether you're already using features that were developed with the help of sponsorships. You're most likely using at least a handful of them, [thanks to our awesome sponsors][sponsors]! - -## What's in it for me? - -```python exec="1" session="insiders" -data_source = [ - "docs/insiders/goals.yml", - ("griffe-pydantic", "https://mkdocstrings.github.io/griffe-pydantic/", "insiders/goals.yml"), - ("griffe-typedoc", "https://mkdocstrings.github.io/griffe-typedoc/", "insiders/goals.yml"), - ("griffe-warnings-deprecated", "https://mkdocstrings.github.io/griffe-warnings-deprecated/", "insiders/goals.yml"), - ("mkdocstrings-c", "https://mkdocstrings.github.io/c/", "insiders/goals.yml"), - ("mkdocstrings-python", "https://mkdocstrings.github.io/python/", "insiders/goals.yml"), - ("mkdocstrings-shell", "https://mkdocstrings.github.io/shell/", "insiders/goals.yml"), - ("mkdocstrings-typescript", "https://mkdocstrings.github.io/typescript/", "insiders/goals.yml"), -] -``` - - -```python exec="1" session="insiders" idprefix="" ---8<-- "scripts/insiders.py" - -if unreleased_features: - print( - "The moment you [become a sponsor](#how-to-become-a-sponsor), you'll get **immediate " - f"access to {len(unreleased_features)} additional features** that you can start using right away, and " - "which are currently exclusively available to sponsors:\n" - ) - - for feature in unreleased_features: - feature.render(badge=True) - - print( - "\n\nThese are just the features related to this project. " - "[See the complete feature list on the author's main Insiders page](https://pawamoy.github.io/insiders/#whats-in-it-for-me)." - ) -else: - print( - "The moment you [become a sponsor](#how-to-become-a-sponsor), you'll get immediate " - "access to all released features that you can start using right away, and " - "which are exclusively available to sponsors. At this moment, there are no " - "Insiders features for this project, but checkout the [next funding goals](#goals) " - "to see what's coming, as well as **[the feature list for all Insiders projects](https://pawamoy.github.io/insiders/#whats-in-it-for-me).**" - ) -``` - - -Additionally, your sponsorship will give more weight to your upvotes on issues, helping us prioritize work items in our backlog. For more information on how we prioritize work, see this page: [Backlog management][backlog]. - -## How to become a sponsor - -Thanks for your interest in sponsoring! In order to become an eligible sponsor with your GitHub account, visit [pawamoy's sponsor profile][github sponsor profile], and complete a sponsorship of **$10 a month or more**. You can use your individual or organization GitHub account for sponsoring. - -Sponsorships lower than $10 a month are also very much appreciated, and useful. They won't grant you access to Insiders, but they will be counted towards reaching sponsorship goals. Every sponsorship helps us implementing new features and releasing them to the public. - -**Important:** By default, when you're sponsoring **[@pawamoy][github sponsor profile]** through a GitHub organization, all the publicly visible members of the organization will be invited to join our private repositories. If you wish to only grant access to a subset of users, please send a short email to insiders@pawamoy.fr with the name of your organization and the GitHub accounts of the users that should be granted access. - -**Tip:** to ensure that access is not tied to a particular individual GitHub account, you can create a bot account (i.e. a GitHub account that is not tied to a specific individual), and use this account for the sponsoring. After being granted access to our private repositories, the bot account can create private forks of our private repositories into your own organization, which all members of your organization will have access to. - -You can cancel your sponsorship anytime.[^5] - -[^5]: If you cancel your sponsorship, GitHub schedules a cancellation request which will become effective at the end of the billing cycle. This means that even though you cancel your sponsorship, you will keep your access to Insiders as long as your cancellation isn't effective. All charges are processed by GitHub through Stripe. As we don't receive any information regarding your payment, and GitHub doesn't offer refunds, sponsorships are non-refundable. - - -[:octicons-heart-fill-24:{ .pulse }   Join our awesome sponsors][github sponsor profile]{ .md-button .md-button--primary } - -
-
-
-
-
-
-
- -
- - - If you sponsor publicly, you're automatically added here with a link to your profile and avatar to show your support for *mkdocstrings*. Alternatively, if you wish to keep your sponsorship private, you'll be a silent +1. You can select visibility during checkout and change it afterwards. - - -## Funding - -### Goals - -The following section lists all funding goals. Each goal contains a list of features prefixed with a checkmark symbol, denoting whether a feature is :octicons-check-circle-fill-24:{ style="color: #00e676" } already available or :octicons-check-circle-fill-24:{ style="color: var(--md-default-fg-color--lightest)" } planned, but not yet implemented. When the funding goal is hit, the features are released for general availability. - -```python exec="1" session="insiders" idprefix="" -for goal in goals.values(): - if not goal.complete: - goal.render() -``` - -### Goals completed - -This section lists all funding goals that were previously completed, which means that those features were part of Insiders, but are now generally available and can be used by all users. - -```python exec="1" session="insiders" idprefix="" -for goal in goals.values(): - if goal.complete: - goal.render() -``` - -## Frequently asked questions - -### Compatibility - -> We're building an open source project and want to allow outside collaborators to use *mkdocstrings* locally without having access to Insiders. Is this still possible? - -Yes. Insiders is compatible with *mkdocstrings*. Almost all new features and configuration options are either backward-compatible or implemented behind feature flags. Most Insiders features enhance the overall experience, though while these features add value for the users of your project, they shouldn't be necessary for previewing when making changes to content. - -### Payment - -> We don't want to pay for sponsorship every month. Are there any other options? - -Yes. You can sponsor on a yearly basis by [switching your GitHub account to a yearly billing cycle][billing cycle]. If for some reason you cannot do that, you could also create a dedicated GitHub account with a yearly billing cycle, which you only use for sponsoring (some sponsors already do that). - -If you have any problems or further questions, please reach out to insiders@pawamoy.fr. - -### Terms - -> Are we allowed to use Insiders under the same terms and conditions as *mkdocstrings*? - -Yes. Whether you're an individual or a company, you may use *mkdocstrings Insiders* precisely under the same terms as *mkdocstrings*, which are given by the [ISC license][license]. However, we kindly ask you to respect our **fair use policy**: - -- Please **don't distribute the source code** of Insiders. You may freely use it for public, private or commercial projects, privately fork or mirror it, but please don't make the source code public, as it would counteract the sponsorware strategy. -- If you cancel your subscription, your access to the private repository is revoked, and you will miss out on all future updates of Insiders. However, you may **use the latest version** that's available to you **as long as you like**. Just remember that [GitHub deletes private forks][private forks]. - -[backlog]: https://pawamoy.github.io/backlog/ -[insiders]: #what-is-insiders -[sponsorship]: #what-sponsorships-achieve -[sponsors]: #how-to-become-a-sponsor -[features]: #whats-in-it-for-me -[funding]: #funding -[goals completed]: #goals-completed -[github sponsor profile]: https://github.com/sponsors/pawamoy -[billing cycle]: https://docs.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/changing-the-duration-of-your-billing-cycle -[license]: ../license.md -[private forks]: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/removing-a-collaborator-from-a-personal-repository - - - diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md deleted file mode 100644 index 1df4608b..00000000 --- a/docs/insiders/installation.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Getting started with Insiders ---- - -# Getting started with Insiders - -*mkdocstrings Insiders* is a compatible drop-in replacement for *mkdocstrings*, and can be installed similarly using `pip` or `git`. Note that in order to access the Insiders repository, you need to [become an eligible sponsor][] of @pawamoy on GitHub. - -## Installation - -### with the `insiders` tool - -[`insiders`][insiders-tool] is a tool that helps you keep up-to-date versions of Insiders projects in the PyPI index of your choice (self-hosted, Google registry, Artifactory, etc.). - -**We kindly ask that you do not upload the distributions to public registries, as it is against our [Terms of use][].** - -### with pip (ssh/https) - -*mkdocstrings Insiders* can be installed with `pip` [using SSH][install-pip-ssh]: - -```bash -pip install git+ssh://git@github.com/pawamoy-insiders/mkdocstrings.git -``` - -Or using HTTPS: - -```bash -pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/mkdocstrings.git -``` - ->? NOTE: **How to get a GitHub personal access token?** The `GH_TOKEN` environment variable is a GitHub token. It can be obtained by creating a [personal access token][github-pat] for your GitHub account. It will give you access to the Insiders repository, programmatically, from the command line or GitHub Actions workflows: -> -> 1. Go to https://github.com/settings/tokens -> 2. Click on [Generate a new token][github-pat-new] -> 3. Enter a name and select the [`repo`][scopes] scope -> 4. Generate the token and store it in a safe place -> -> Note that the personal access token must be kept secret at all times, as it allows the owner to access your private repositories. - -### with Git - -Of course, you can use *mkdocstrings Insiders* directly using Git: - -``` -git clone git@github.com:pawamoy-insiders/mkdocstrings -``` - -When cloning with Git, the package must be installed: - -``` -pip install -e mkdocstrings -``` - -## Upgrading - -When upgrading Insiders, you should always check the version of *mkdocstrings* which makes up the first part of the version qualifier. For example, a version like `8.x.x.4.x.x` means that Insiders `4.x.x` is currently based on `8.x.x`. - -If the major version increased, it's a good idea to consult the [changelog][] and go through the steps to ensure your configuration is up to date and all necessary changes have been made. - -[become an eligible sponsor]: ./index.md#how-to-become-a-sponsor -[changelog]: ./changelog.md -[github-pat]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token -[github-pat-new]: https://github.com/settings/tokens/new -[insiders-tool]: https://pawamoy.github.io/insiders-project/ -[install-pip-ssh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh -[scopes]: https://docs.github.com/en/developers/apps/scopes-for-oauth-apps#available-scopes -[terms of use]: ./index.md#terms diff --git a/docs/js/insiders.js b/docs/js/insiders.js deleted file mode 100644 index a86a0918..00000000 --- a/docs/js/insiders.js +++ /dev/null @@ -1,77 +0,0 @@ -function humanReadableAmount(amount) { - const strAmount = String(amount); - if (strAmount.length >= 4) { - return `${strAmount.slice(0, strAmount.length - 3)},${strAmount.slice(-3)}`; - } - return strAmount; -} - -function getJSON(url, callback) { - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.responseType = 'json'; - xhr.onload = function () { - var status = xhr.status; - if (status === 200) { - callback(null, xhr.response); - } else { - callback(status, xhr.response); - } - }; - xhr.send(); -} - -function updatePremiumSponsors(dataURL, rank) { - let capRank = rank.charAt(0).toUpperCase() + rank.slice(1); - getJSON(dataURL + `/sponsors${capRank}.json`, function (err, sponsors) { - const sponsorsDiv = document.getElementById(`${rank}-sponsors`); - if (sponsors.length > 0) { - let html = ''; - html += `${capRank} sponsors

` - sponsors.forEach(function (sponsor) { - html += `` - if (sponsor.image) { - html += `${sponsor.name}` - } else if (sponsor.imageLight && sponsor.imageDark) { - html += `${sponsor.name}` - html += `${sponsor.name}` - } - html += ''; - }); - html += '

' - sponsorsDiv.innerHTML = html; - } - }); -} - -function updateInsidersPage(author_username) { - const sponsorURL = `https://github.com/sponsors/${author_username}` - const dataURL = `https://raw.githubusercontent.com/${author_username}/sponsors/main`; - getJSON(dataURL + '/numbers.json', function (err, numbers) { - document.getElementById('sponsors-count').innerHTML = numbers.count; - Array.from(document.getElementsByClassName('sponsors-total')).forEach(function (element) { - element.innerHTML = '$ ' + humanReadableAmount(numbers.total); - }); - getJSON(dataURL + '/sponsors.json', function (err, sponsors) { - const sponsorsElem = document.getElementById('sponsors'); - const privateSponsors = numbers.count - sponsors.length; - sponsors.forEach(function (sponsor) { - sponsorsElem.innerHTML += ` - - - - `; - }); - if (privateSponsors > 0) { - sponsorsElem.innerHTML += ` - - +${privateSponsors} - - `; - } - }); - }); - updatePremiumSponsors(dataURL, "gold"); - updatePremiumSponsors(dataURL, "silver"); - updatePremiumSponsors(dataURL, "bronze"); -} diff --git a/duties.py b/duties.py index ee20b497..04357dbb 100644 --- a/duties.py +++ b/duties.py @@ -6,10 +6,9 @@ import re import sys from contextlib import contextmanager -from functools import wraps from importlib.metadata import version as pkgversion from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING from duty import duty, tools @@ -37,21 +36,6 @@ def pyprefix(title: str) -> str: return title -def not_from_insiders(func: Callable) -> Callable: - @wraps(func) - def wrapper(ctx: Context, *args: Any, **kwargs: Any) -> None: - origin = ctx.run("git config --get remote.origin.url", silent=True) - if "pawamoy-insiders/griffe" in origin: - ctx.run( - lambda: False, - title="Not running this task from insiders repository (do that from public repo instead!)", - ) - return - func(ctx, *args, **kwargs) - - return wrapper - - @contextmanager def material_insiders() -> Iterator[bool]: if "+insiders" in pkgversion("mkdocs-material"): @@ -145,39 +129,13 @@ def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000 @duty -def docs_deploy(ctx: Context, *, force: bool = False) -> None: - """Deploy the documentation to GitHub pages. - - Parameters: - force: Whether to force deployment, even from non-Insiders version. - """ +def docs_deploy(ctx: Context) -> None: + """Deploy the documentation to GitHub pages.""" os.environ["DEPLOY"] = "true" with material_insiders() as insiders: if not insiders: ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!") - origin = ctx.run("git config --get remote.origin.url", silent=True, allow_overrides=False) - if "pawamoy-insiders/mkdocstrings" in origin: - ctx.run( - "git remote add org-pages git@github.com:mkdocstrings/mkdocstrings.github.io", - silent=True, - nofail=True, - allow_overrides=False, - ) - ctx.run( - tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), - title="Deploying documentation", - ) - elif force: - ctx.run( - tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), - title="Deploying documentation", - ) - else: - ctx.run( - lambda: False, - title="Not deploying docs from public repository (do that from insiders instead!)", - nofail=True, - ) + ctx.run(tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), title="Deploying documentation") @duty @@ -201,7 +159,6 @@ def build(ctx: Context) -> None: @duty -@not_from_insiders def publish(ctx: Context) -> None: """Publish source and wheel distributions to PyPI.""" if not Path("dist").exists(): @@ -215,7 +172,6 @@ def publish(ctx: Context) -> None: @duty(post=["build", "publish", "docs-deploy"]) -@not_from_insiders def release(ctx: Context, version: str = "") -> None: """Release a new Python package. @@ -226,7 +182,7 @@ def release(ctx: Context, version: str = "") -> None: ctx.run("false", title="A version must be provided") ctx.run("git add pyproject.toml CHANGELOG.md", title="Staging files", pty=PTY) ctx.run(["git", "commit", "-m", f"chore: Prepare release {version}"], title="Committing changes", pty=PTY) - ctx.run(f"git tag {version}", title="Tagging commit", pty=PTY) + ctx.run(f"git tag -m '' -a {version}", title="Tagging commit", pty=PTY) ctx.run("git push", title="Pushing commits", pty=False) ctx.run("git push --tags", title="Pushing tags", pty=False) @@ -240,24 +196,14 @@ def coverage(ctx: Context) -> None: @duty(nofail=PY_VERSION == PY_DEV) -def test(ctx: Context, *cli_args: str, match: str = "") -> None: # noqa: PT028 - """Run the test suite. - - Parameters: - match: A pytest expression to filter selected tests. - """ +def test(ctx: Context, *cli_args: str) -> None: + """Run the test suite.""" os.environ["COVERAGE_FILE"] = f".coverage.{PY_VERSION}" os.environ["PYTHONWARNDEFAULTENCODING"] = "1" - config_file = "config/pytest.ini" - # YORE: EOL 3.9: Remove block. - if sys.version_info[:2] < (3, 10): - config_file = "config/pytest_39.ini" - ctx.run( tools.pytest( "tests", - config_file=config_file, - select=match, + config_file="config/pytest.ini", color="yes", ).add_args("-n", "auto", *cli_args), title=pyprefix("Running tests"), diff --git a/mkdocs.yml b/mkdocs.yml index 52721b3e..cdfb5173 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,11 +41,6 @@ nav: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md - Coverage report: coverage.md -- Insiders: - - insiders/index.md - - Getting started: - - Installation: insiders/installation.md - - Changelog: insiders/changelog.md - Author's website: https://pawamoy.github.io/ theme: @@ -93,7 +88,6 @@ extra_css: - css/style.css - css/material.css - css/mkdocstrings.css -- css/insiders.css extra_javascript: - js/feedback.js @@ -158,7 +152,7 @@ plugins: show_root_heading: true show_root_full_path: false show_signature_annotations: true - show_source: false + show_source: true show_symbol_type_heading: true show_symbol_type_toc: true signature_crossrefs: true diff --git a/pyproject.toml b/pyproject.toml index 4db21462..14ba163b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}] license = "ISC" license-files = ["LICENSE"] readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" keywords = ["mkdocs", "mkdocs-plugin", "docstrings", "autodoc", "documentation"] dynamic = ["version"] classifiers = [ @@ -18,7 +18,6 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/scripts/insiders.py b/scripts/insiders.py deleted file mode 100644 index 4cd438d4..00000000 --- a/scripts/insiders.py +++ /dev/null @@ -1,173 +0,0 @@ -# Functions related to Insiders funding goals. - -from __future__ import annotations - -import json -import logging -import os -import posixpath -from dataclasses import dataclass -from datetime import date, datetime, timedelta -from itertools import chain -from pathlib import Path -from typing import TYPE_CHECKING, cast -from urllib.error import HTTPError -from urllib.parse import urljoin -from urllib.request import urlopen - -import yaml - -if TYPE_CHECKING: - from collections.abc import Iterable - -logger = logging.getLogger(f"mkdocs.logs.{__name__}") - - -def human_readable_amount(amount: int) -> str: - str_amount = str(amount) - if len(str_amount) >= 4: # noqa: PLR2004 - return f"{str_amount[: len(str_amount) - 3]},{str_amount[-3:]}" - return str_amount - - -@dataclass -class Project: - name: str - url: str - - -@dataclass -class Feature: - name: str - ref: str | None - since: date | None - project: Project | None - - def url(self, rel_base: str = "..") -> str | None: # noqa: D102 - if not self.ref: - return None - if self.project: - rel_base = self.project.url - return posixpath.join(rel_base, self.ref.lstrip("/")) - - def render(self, rel_base: str = "..", *, badge: bool = False) -> None: # noqa: D102 - new = "" - if badge: - recent = self.since and date.today() - self.since <= timedelta(days=60) # noqa: DTZ011 - if recent: - ft_date = self.since.strftime("%B %d, %Y") # type: ignore[union-attr] - new = f' :material-alert-decagram:{{ .new-feature .vibrate title="Added on {ft_date}" }}' - project = f"[{self.project.name}]({self.project.url}) — " if self.project else "" - feature = f"[{self.name}]({self.url(rel_base)})" if self.ref else self.name - print(f"- [{'x' if self.since else ' '}] {project}{feature}{new}") - - -@dataclass -class Goal: - name: str - amount: int - features: list[Feature] - complete: bool = False - - @property - def human_readable_amount(self) -> str: # noqa: D102 - return human_readable_amount(self.amount) - - def render(self, rel_base: str = "..") -> None: # noqa: D102 - print(f"#### $ {self.human_readable_amount} — {self.name}\n") - if self.features: - for feature in self.features: - feature.render(rel_base) - print("") - else: - print("There are no features in this goal for this project. ") - print( - "[See the features in this goal **for all Insiders projects.**]" - f"(https://pawamoy.github.io/insiders/#{self.amount}-{self.name.lower().replace(' ', '-')})", - ) - - -def load_goals(data: str, funding: int = 0, project: Project | None = None) -> dict[int, Goal]: - goals_data = yaml.safe_load(data)["goals"] - return { - amount: Goal( - name=goal_data["name"], - amount=amount, - complete=funding >= amount, - features=[ - Feature( - name=feature_data["name"], - ref=feature_data.get("ref"), - since=feature_data.get("since") and datetime.strptime(feature_data["since"], "%Y/%m/%d").date(), # noqa: DTZ007 - project=project, - ) - for feature_data in goal_data["features"] - ], - ) - for amount, goal_data in goals_data.items() - } - - -def _load_goals_from_disk(path: str, funding: int = 0) -> dict[int, Goal]: - project_dir = os.getenv("MKDOCS_CONFIG_DIR", ".") - try: - data = Path(project_dir, path).read_text() - except OSError as error: - raise RuntimeError(f"Could not load data from disk: {path}") from error - return load_goals(data, funding) - - -def _load_goals_from_url(source_data: tuple[str, str, str], funding: int = 0) -> dict[int, Goal]: - project_name, project_url, data_fragment = source_data - data_url = urljoin(project_url, data_fragment) - try: - with urlopen(data_url) as response: # noqa: S310 - data = response.read() - except HTTPError as error: - raise RuntimeError(f"Could not load data from network: {data_url}") from error - return load_goals(data, funding, project=Project(name=project_name, url=project_url)) - - -def _load_goals(source: str | tuple[str, str, str], funding: int = 0) -> dict[int, Goal]: - if isinstance(source, str): - return _load_goals_from_disk(source, funding) - return _load_goals_from_url(source, funding) - - -def funding_goals(source: str | list[str | tuple[str, str, str]], funding: int = 0) -> dict[int, Goal]: - if isinstance(source, str): - return _load_goals_from_disk(source, funding) - goals = {} - for src in source: - source_goals = _load_goals(src, funding) - for amount, goal in source_goals.items(): - if amount not in goals: - goals[amount] = goal - else: - goals[amount].features.extend(goal.features) - return {amount: goals[amount] for amount in sorted(goals)} - - -def feature_list(goals: Iterable[Goal]) -> list[Feature]: - return list(chain.from_iterable(goal.features for goal in goals)) - - -def load_json(url: str) -> str | list | dict: - with urlopen(url) as response: # noqa: S310 - return json.loads(response.read().decode()) - - -data_source = globals()["data_source"] -sponsor_url = "https://github.com/sponsors/pawamoy" -data_url = "https://raw.githubusercontent.com/pawamoy/sponsors/main" -numbers: dict[str, int] = load_json(f"{data_url}/numbers.json") # type: ignore[assignment] -sponsors: list[dict] = load_json(f"{data_url}/sponsors.json") # type: ignore[assignment] -current_funding = numbers["total"] -sponsors_count = numbers["count"] -goals = funding_goals(data_source, funding=current_funding) -ongoing_goals = [goal for goal in goals.values() if not goal.complete] -unreleased_features = sorted( - (ft for ft in feature_list(ongoing_goals) if ft.since), - key=lambda ft: cast("date", ft.since), - reverse=True, -) diff --git a/scripts/make.py b/scripts/make.py index 1e697bcc..b741a366 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -14,8 +14,8 @@ from collections.abc import Iterator -PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.9 3.10 3.11 3.12 3.13 3.14").split() -PYTHON_DEV = "3.14" +PYTHON_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.10 3.11 3.12 3.13 3.14 3.15").split() +PYTHON_DEV = "3.15" def shell(cmd: str, *, capture_output: bool = False, **kwargs: Any) -> str | None: From 39fbea1c2dd030f017ae6fd4a8653959f310a693 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 11 Nov 2025 00:58:50 +0000 Subject: [PATCH 24/51] chore: Update sponsors section in README --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index 20f6fd39..a486ed40 100644 --- a/README.md +++ b/README.md @@ -137,4 +137,62 @@ See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for mo ## Sponsors + +
+ +
Silver sponsors

+Material for MkDocs
+FastAPI
+Pydantic
+

+ +
Bronze sponsors

+Nixtla
+

+
+ +--- + +

+ofek +samuelcolvin +tlambert03 +ssbarnea +femtomc +cmarqu +kolenaIO +ramnes +machow +BenHammersley +trevorWieland +laenan8466 +MarcoGorelli +analog-cbarber +OdinManiac +rstudio-sponsorship +schlich +SuperCowPowers +butterlyn +livingbio +NemetschekAllplan +EricJayHartman +15r10nk +cdwilson +activeloopai +roboflow +wrath-codes +leodevian +cmclaughlin +blaisep +RapidataAI +rodolphebarbanneau +theSymbolSyndicate +blakeNaccarato +ChargeStorm +Alphadelta14 +

+ + +*And 8 more private sponsor(s).* + From 3076375ec7eb4c9c8b739e414a919a1be29df07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 19 Nov 2025 18:39:13 +0100 Subject: [PATCH 25/51] chore: Specify encoding when reading file in tests --- tests/test_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index acb9556c..833de692 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -41,7 +41,7 @@ def test_disabling_plugin(tmp_path: Path) -> None: mkdocs_config["plugins"].run_event("shutdown") # make sure the instruction was not processed - assert "::: mkdocstrings" in site_dir.joinpath("index.html").read_text() + assert "::: mkdocstrings" in site_dir.joinpath("index.html").read_text(encoding="utf8") def test_plugin_default_config(tmp_path: Path) -> None: From 6de266759b79eb72cddd300e6a0a8576085fae40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 19 Nov 2025 19:57:17 +0100 Subject: [PATCH 26/51] refactor: Expose the Markdown extension, to make mkdocstrings compatible with Zensical --- src/mkdocstrings/__init__.py | 3 +- src/mkdocstrings/_internal/extension.py | 79 ++++++++++++++++++++- src/mkdocstrings/_internal/handlers/base.py | 6 +- 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/mkdocstrings/__init__.py b/src/mkdocstrings/__init__.py index 71720f8a..137811b1 100644 --- a/src/mkdocstrings/__init__.py +++ b/src/mkdocstrings/__init__.py @@ -5,7 +5,7 @@ from __future__ import annotations -from mkdocstrings._internal.extension import AutoDocProcessor, MkdocstringsExtension +from mkdocstrings._internal.extension import AutoDocProcessor, MkdocstringsExtension, makeExtension from mkdocstrings._internal.handlers.base import ( BaseHandler, CollectionError, @@ -62,4 +62,5 @@ "get_template_logger", "get_template_logger_function", "get_template_path", + "makeExtension", ] diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 2277775c..c06a967b 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -35,6 +35,7 @@ from markdown.extensions import Extension from markdown.treeprocessors import Treeprocessor from mkdocs.exceptions import PluginError +from mkdocs_autorefs import AutorefsConfig, AutorefsPlugin from mkdocstrings._internal.handlers.base import BaseHandler, CollectionError, CollectorItem, Handlers from mkdocstrings._internal.loggers import get_logger @@ -43,7 +44,6 @@ from collections.abc import MutableSequence from markdown import Markdown - from mkdocs_autorefs import AutorefsPlugin _logger = get_logger("mkdocstrings") @@ -380,3 +380,80 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me "mkdocstrings_post_toc_labels", priority=4, # Right after 'toc'. ) + + +# ----------------------------------------------------------------------------- +# The following is only used by Zensical. The goal is to provide temporary +# compatibility for users migrating from MkDocs (and Material for MkDocs) +# to Zensical. When detecting the use of the mkdocstrings plugin in mkdocs.yml, +# Zensical will add the mkdocstrings extension to its Markdown extensions. + +_default_config: dict[str, Any] = { + "default_handler": "python", + "handlers": {}, + "custom_templates": None, + "locale": "en", + "enable_inventory": True, + "enabled": True, +} + + +def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str], dict[str, Any]]: + # Split markdown extensions and their configs from mkdocs.yml + mdx: list[str] = [] + mdx_config: dict[str, Any] = {} + for item in markdown_extensions: + if isinstance(item, str): + mdx.append(item) + elif isinstance(item, dict): + for key, value in item.items(): + mdx.append(key) + mdx_config[key] = value + break # Only one item per dict + return mdx, mdx_config + + +def makeExtension( # noqa: N802 + *args: Any, # noqa: ARG001 + **kwargs: Any, +) -> MkdocstringsExtension: + """Create the extension instance.""" + from zensical.config import _yaml_load # noqa: PLC0415 + + with open("mkdocs.yml", encoding="utf-8") as f: + mkdocs_config = _yaml_load(f) + + mkdocstrings_config = mkdocs_config.get("plugins", None) + if isinstance(mkdocstrings_config, dict): + mkdocstrings_config = mkdocstrings_config.get("mkdocstrings", {}) + elif isinstance(mkdocstrings_config, list): + for plugin in mkdocstrings_config: + if isinstance(plugin, dict) and "mkdocstrings" in plugin: + mkdocstrings_config = plugin["mkdocstrings"] + break + else: + mkdocstrings_config = _default_config + else: + mkdocstrings_config = _default_config + + mdx, mdx_config = _split_configs(mkdocs_config.get("markdown_extensions", [])) + + handlers = Handlers( + theme="material", + default=mkdocstrings_config.get("default_handler", _default_config["default_handler"]), + inventory_project=mkdocs_config.get("site_name", "Project"), + handlers_config=mkdocstrings_config.get("handlers", _default_config["handlers"]), + custom_templates=mkdocstrings_config.get("custom_templates", _default_config["custom_templates"]), + mdx=mdx, + mdx_config=mdx_config, + locale=mkdocstrings_config.get("locale", _default_config["locale"]), + tool_config=mkdocs_config, + ) + + handlers._download_inventories() + + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.scan_toc = False + + return MkdocstringsExtension(handlers=handlers, autorefs=autorefs, **kwargs) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index c4e9950d..af68a45c 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -436,8 +436,7 @@ def do_convert_markdown( treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph # type: ignore[attr-defined] if BacklinksTreeProcessor.name in treeprocessors: treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id # type: ignore[attr-defined] - - if autoref_hook: + if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # type: ignore[attr-defined] try: @@ -448,7 +447,8 @@ def do_convert_markdown( treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False # type: ignore[attr-defined] if BacklinksTreeProcessor.name in treeprocessors: treeprocessors[BacklinksTreeProcessor.name].initial_id = None # type: ignore[attr-defined] - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] + if AutorefsInlineProcessor.name in self.md.inlinePatterns: + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] self.md.reset() _markdown_conversion_layer -= 1 From fc4d588dc73b12c205a933a0a5808742119aa7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 27 Nov 2025 14:47:00 +0100 Subject: [PATCH 27/51] docs: Announce maintenance mode --- docs/.overrides/main.html | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html index c702362f..3bfd4775 100644 --- a/docs/.overrides/main.html +++ b/docs/.overrides/main.html @@ -1,18 +1,7 @@ {% extends "base.html" %} {% block announce %} - Fund this project through - sponsorship - - {% include ".icons/octicons/heart-fill-16.svg" %} - — - Follow - @pawamoy on - - - {% include ".icons/fontawesome/brands/mastodon.svg" %} - - Fosstodon - - for updates + ⚠️ mkdocstrings is in maintenance mode! + blog post + {% endblock %} From bebbb88d3f09249b0129b05f98fdbd9f2eaa6818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 27 Nov 2025 14:47:24 +0100 Subject: [PATCH 28/51] chore: Remove trailing space --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index cdfb5173..8250973b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -26,7 +26,7 @@ nav: - All handlers: - C: https://mkdocstrings.github.io/c/ - Crystal: https://mkdocstrings.github.io/crystal/ - - GitHub Actions: https://watermarkhu.nl/mkdocstrings-github/ + - GitHub Actions: https://watermarkhu.nl/mkdocstrings-github/ - Python: https://mkdocstrings.github.io/python/ - Python (Legacy): https://mkdocstrings.github.io/python-legacy/ - MATLAB: https://watermarkhu.nl/mkdocstrings-matlab/ From 6b73d5a2f455062ab6c68376c85adce6adc037a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 27 Nov 2025 15:32:23 +0100 Subject: [PATCH 29/51] refactor: Expect Zensical to pass extension configuration instead of loading it again from YAML --- src/mkdocstrings/_internal/extension.py | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index c06a967b..8ccea142 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -413,47 +413,47 @@ def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str], di return mdx, mdx_config +class _ToolConfig: + def __init__(self, config_file_path: str | None = None) -> None: + self.config_file_path = config_file_path + + def makeExtension( # noqa: N802 - *args: Any, # noqa: ARG001 - **kwargs: Any, + *, + default_handler: str | None = None, + inventory_project: str | None = None, + inventory_version: str | None = None, + handlers: dict[str, dict] | None = None, + custom_templates: str | None = None, + markdown_extensions: list[str | dict] | None = None, + locale: str | None = None, + config_file_path: str | None = None, ) -> MkdocstringsExtension: - """Create the extension instance.""" - from zensical.config import _yaml_load # noqa: PLC0415 - - with open("mkdocs.yml", encoding="utf-8") as f: - mkdocs_config = _yaml_load(f) - - mkdocstrings_config = mkdocs_config.get("plugins", None) - if isinstance(mkdocstrings_config, dict): - mkdocstrings_config = mkdocstrings_config.get("mkdocstrings", {}) - elif isinstance(mkdocstrings_config, list): - for plugin in mkdocstrings_config: - if isinstance(plugin, dict) and "mkdocstrings" in plugin: - mkdocstrings_config = plugin["mkdocstrings"] - break - else: - mkdocstrings_config = _default_config - else: - mkdocstrings_config = _default_config + """Create the extension instance. - mdx, mdx_config = _split_configs(mkdocs_config.get("markdown_extensions", [])) + We only support this function being used by Zensical. + Consider this function private API. + """ + mdx, mdx_config = _split_configs(markdown_extensions or []) + tool_config = _ToolConfig(config_file_path=config_file_path) - handlers = Handlers( + handlers_instance = Handlers( theme="material", - default=mkdocstrings_config.get("default_handler", _default_config["default_handler"]), - inventory_project=mkdocs_config.get("site_name", "Project"), - handlers_config=mkdocstrings_config.get("handlers", _default_config["handlers"]), - custom_templates=mkdocstrings_config.get("custom_templates", _default_config["custom_templates"]), + default=default_handler or _default_config["default_handler"], + inventory_project=inventory_project or "Project", + inventory_version=inventory_version or "0.0.0", + handlers_config=handlers or _default_config["handlers"], + custom_templates=custom_templates or _default_config["custom_templates"], mdx=mdx, mdx_config=mdx_config, - locale=mkdocstrings_config.get("locale", _default_config["locale"]), - tool_config=mkdocs_config, + locale=locale or _default_config["locale"], + tool_config=tool_config, ) - handlers._download_inventories() + handlers_instance._download_inventories() autorefs = AutorefsPlugin() autorefs.config = AutorefsConfig() autorefs.scan_toc = False - return MkdocstringsExtension(handlers=handlers, autorefs=autorefs, **kwargs) + return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs) From de34044a02b45250e215af0f969dca581dfb82c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 27 Nov 2025 15:42:59 +0100 Subject: [PATCH 30/51] refactor: Remove deprecated code before v1 --- pyproject.toml | 5 +- src/mkdocstrings/_internal/extension.py | 33 +--- src/mkdocstrings/_internal/handlers/base.py | 174 ++------------------ src/mkdocstrings/_internal/loggers.py | 6 +- src/mkdocstrings/_internal/plugin.py | 5 - src/mkdocstrings/extension.py | 17 -- src/mkdocstrings/handlers/__init__.py | 3 - src/mkdocstrings/handlers/base.py | 17 -- src/mkdocstrings/handlers/rendering.py | 17 -- src/mkdocstrings/inventory.py | 17 -- src/mkdocstrings/loggers.py | 17 -- src/mkdocstrings/plugin.py | 17 -- tests/test_api.py | 5 - 13 files changed, 22 insertions(+), 311 deletions(-) delete mode 100644 src/mkdocstrings/extension.py delete mode 100644 src/mkdocstrings/handlers/__init__.py delete mode 100644 src/mkdocstrings/handlers/base.py delete mode 100644 src/mkdocstrings/handlers/rendering.py delete mode 100644 src/mkdocstrings/inventory.py delete mode 100644 src/mkdocstrings/loggers.py delete mode 100644 src/mkdocstrings/plugin.py diff --git a/pyproject.toml b/pyproject.toml index 14ba163b..2b3d6c17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,15 +30,12 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - # YORE: Bump 1: Replace `2.11.1` with `3.1` within line. - "Jinja2>=2.11.1", + "Jinja2>=3.1", "Markdown>=3.6", "MarkupSafe>=1.1", "mkdocs>=1.6", "mkdocs-autorefs>=1.4", "pymdown-extensions>=6.3", - # YORE: EOL 3.9: Remove line. - "importlib-metadata>=4.6; python_version < '3.10'", ] [project.optional-dependencies] diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 8ccea142..3a6f4530 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -26,7 +26,6 @@ from functools import partial from inspect import signature from typing import TYPE_CHECKING, Any -from warnings import warn from xml.etree.ElementTree import Element import yaml @@ -172,19 +171,7 @@ def _process_block( # Heading level obtained from Markdown (`##`) takes precedence. local_options["heading_level"] = heading_level - # YORE: Bump 1: Replace block with line 2. - if handler.get_options.__func__ is not BaseHandler.get_options: # type: ignore[attr-defined] - options = handler.get_options(local_options) - else: - warn( - "mkdocstrings v1 will start using your handler's `get_options` method to build options " - "instead of merging the global and local options (dictionaries). ", - DeprecationWarning, - stacklevel=1, - ) - handler_config = self._handlers.get_handler_config(handler_name) - global_options = handler_config.get("options", {}) - options = {**global_options, **local_options} + options = handler.get_options(local_options) _logger.debug("Collecting data") try: @@ -266,23 +253,7 @@ def _process_headings(self, handler: BaseHandler, element: Element) -> None: # Register all identifiers for this object # both in the autorefs plugin and in the inventory. aliases: tuple[str, ...] - # YORE: Bump 1: Replace block with line 16. - if hasattr(handler, "get_anchors"): - warn( - "The `get_anchors` method is deprecated. " - "Declare a `get_aliases` method instead, accepting a string (identifier) " - "instead of a collected object.", - DeprecationWarning, - stacklevel=1, - ) - try: - data_object = handler.collect(rendered_id, getattr(handler, "fallback_config", {})) - except CollectionError: - aliases = () - else: - aliases = handler.get_anchors(data_object) - else: - aliases = handler.get_aliases(rendered_id) + aliases = handler.get_aliases(rendered_id) for alias in aliases: if alias != rendered_id: diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index af68a45c..2eb9b3e6 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -6,10 +6,9 @@ import datetime import importlib -import inspect import ssl -import sys from concurrent import futures +from importlib.metadata import entry_points from io import BytesIO from pathlib import Path from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, cast @@ -33,12 +32,6 @@ from mkdocstrings._internal.inventory import Inventory from mkdocstrings._internal.loggers import get_logger, get_template_logger -# YORE: EOL 3.9: Replace block with line 4. -if sys.version_info < (3, 10): - from importlib_metadata import entry_points -else: - from importlib.metadata import entry_points - if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Mapping, Sequence @@ -104,21 +97,15 @@ class BaseHandler: To add custom CSS, add an `extra_css` variable or create an 'style.css' file beside the templates. """ - # YORE: Bump 1: Replace ` = ""` with `` within line. - name: ClassVar[str] = "" + name: ClassVar[str] """The handler's name, for example "python".""" - # YORE: Bump 1: Replace ` = ""` with `` within line. - domain: ClassVar[str] = "" + domain: ClassVar[str] """The handler's domain, used to register objects in the inventory, for example "py".""" enable_inventory: ClassVar[bool] = False """Whether the inventory creation is enabled.""" - # YORE: Bump 1: Remove block. - fallback_config: ClassVar[dict] = {} - """Fallback configuration when searching anchors for identifiers.""" - fallback_theme: ClassVar[str] = "" """Fallback theme to use when a template isn't found in the configured theme.""" @@ -127,16 +114,11 @@ class BaseHandler: def __init__( self, - # YORE: Bump 1: Remove line. - *args: Any, - # YORE: Bump 1: Remove line. - **kwargs: Any, - # YORE: Bump 1: Replace `# ` with `` within block. - # *, - # theme: str, - # custom_templates: str | None, - # mdx: Sequence[str | Extension], - # mdx_config: Mapping[str, Any], + *, + theme: str, + custom_templates: str | None, + mdx: Sequence[str | Extension], + mdx_config: Mapping[str, Any], ) -> None: """Initialize the object. @@ -149,58 +131,6 @@ def __init__( mdx (list[str | Extension]): A list of Markdown extensions to use. mdx_config (Mapping[str, Mapping[str, Any]]): Configuration for the Markdown extensions. """ - # YORE: Bump 1: Remove block. - handler = "" - theme = "" - custom_templates = None - if args: - handler, args = args[0], args[1:] - if args: - theme, args = args[0], args[1:] - warn( - "The `theme` argument must be passed as a keyword argument.", - DeprecationWarning, - stacklevel=2, - ) - if args: - custom_templates, args = args[0], args[1:] - warn( - "The `custom_templates` argument must be passed as a keyword argument.", - DeprecationWarning, - stacklevel=2, - ) - handler = kwargs.pop("handler", handler) - theme = kwargs.pop("theme", theme) - custom_templates = kwargs.pop("custom_templates", custom_templates) - mdx = kwargs.pop("mdx", None) - mdx_config = kwargs.pop("mdx_config", None) - if handler: - if not self.name: - type(self).name = handler - warn( - "The `handler` argument is deprecated. The handler name must be specified as a class attribute.", - DeprecationWarning, - stacklevel=2, - ) - if not self.domain: - warn( - "The `domain` attribute must be specified as a class attribute.", - DeprecationWarning, - stacklevel=2, - ) - if mdx is None: - warn( - "The `mdx` argument must be provided (as a keyword argument).", - DeprecationWarning, - stacklevel=2, - ) - if mdx_config is None: - warn( - "The `mdx_config` argument must be provided (as a keyword argument).", - DeprecationWarning, - stacklevel=2, - ) - self.theme = theme """The selected theme.""" self.custom_templates = custom_templates @@ -533,20 +463,11 @@ def get_headings(self) -> Sequence[Element]: self._headings.clear() return result - # YORE: Bump 1: Replace `*args: Any, **kwargs: Any` with `config: Any`. - def update_env(self, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 + def update_env(self, config: Any) -> None: """Update the Jinja environment.""" - # YORE: Bump 1: Remove line. - warn("No need to call `super().update_env()` anymore.", DeprecationWarning, stacklevel=2) def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: """Update our handler to point to our configured Markdown instance, grabbing some of the config from `md`.""" - # YORE: Bump 1: Remove block. - if self.mdx is None and config is not None: - self.mdx = config.get("mdx", None) or config.get("markdown_extensions", None) or () - if self.mdx_config is None and config is not None: - self.mdx_config = config.get("mdx_config", None) or config.get("mdx_configs", None) or {} - extensions: list[str | Extension] = [*self.mdx, MkdocstringsInnerExtension(self._headings)] new_md = Markdown(extensions=extensions, extension_configs=self.mdx_config) @@ -561,17 +482,7 @@ def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: self.env.filters["highlight"] = Highlighter(new_md).highlight - # YORE: Bump 1: Replace block with `self.update_env(config)`. - parameters = inspect.signature(self.update_env).parameters - if "md" in parameters: - warn( - "The `update_env(md)` parameter is deprecated. Use `self.md` instead.", - DeprecationWarning, - stacklevel=1, - ) - self.update_env(new_md, config) - elif "config" in parameters: - self.update_env(config) + self.update_env(config) class Handlers: @@ -624,35 +535,6 @@ def __init__( self._inv_futures: dict[futures.Future, tuple[BaseHandler, str, Any]] = {} - # YORE: Bump 1: Remove block. - def get_anchors(self, identifier: str) -> tuple[str, ...]: - """Return the canonical HTML anchor for the identifier, if any of the seen handlers can collect it. - - Arguments: - identifier: The identifier (one that [collect][mkdocstrings.BaseHandler.collect] can accept). - - Returns: - A tuple of strings - anchors without '#', or an empty tuple if there isn't any identifier familiar with it. - """ - for handler in self._handlers.values(): - try: - if hasattr(handler, "get_anchors"): - warn( - "The `get_anchors` method is deprecated. " - "Declare a `get_aliases` method instead, accepting a string (identifier) " - "instead of a collected object.", - DeprecationWarning, - stacklevel=1, - ) - aliases = handler.get_anchors(handler.collect(identifier, getattr(handler, "fallback_config", {}))) - else: - aliases = handler.get_aliases(identifier) - except CollectionError: - continue - if aliases: - return aliases - return () - def get_handler_name(self, config: dict) -> str: """Return the handler name defined in an "autodoc" instruction YAML configuration, or the global default handler. @@ -696,34 +578,14 @@ def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHand handler_config = self._handlers_config.get(name, {}) module = importlib.import_module(f"mkdocstrings_handlers.{name}") - # YORE: Bump 1: Remove block. - kwargs = { - "theme": self._theme, - "custom_templates": self._custom_templates, - "mdx": self._mdx, - "mdx_config": self._mdx_config, - "handler_config": handler_config, - "tool_config": self._tool_config, - } - if "config_file_path" in inspect.signature(module.get_handler).parameters: - kwargs["config_file_path"] = self._tool_config.get("config_file_path") - warn( - "The `config_file_path` argument in `get_handler` functions is deprecated. " - "Use `tool_config.get('config_file_path')` instead.", - DeprecationWarning, - stacklevel=1, - ) - self._handlers[name] = module.get_handler(**kwargs) - - # YORE: Bump 1: Replace `# ` with `` within block. - # self._handlers[name] = module.get_handler( - # theme=self._theme, - # custom_templates=self._custom_templates, - # mdx=self._mdx, - # mdx_config=self._mdx_config, - # handler_config=handler_config, - # tool_config=self._tool_config, - # ) + self._handlers[name] = module.get_handler( + theme=self._theme, + custom_templates=self._custom_templates, + mdx=self._mdx, + mdx_config=self._mdx_config, + handler_config=handler_config, + tool_config=self._tool_config, + ) return self._handlers[name] def _download_inventories(self) -> None: diff --git a/src/mkdocstrings/_internal/loggers.py b/src/mkdocstrings/_internal/loggers.py index 6c6304c3..c67a7f4e 100644 --- a/src/mkdocstrings/_internal/loggers.py +++ b/src/mkdocstrings/_internal/loggers.py @@ -7,11 +7,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Callable -# YORE: Bump 1: Replace block with line 2. -try: - from jinja2 import pass_context -except ImportError: - from jinja2 import contextfunction as pass_context # type: ignore[attr-defined,no-redef] +from jinja2 import pass_context if TYPE_CHECKING: from collections.abc import MutableMapping, Sequence diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index 2af14a7a..2c27a60a 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -19,7 +19,6 @@ from inspect import signature from re import Match from typing import TYPE_CHECKING, Any -from warnings import catch_warnings, simplefilter from mkdocs.config import Config from mkdocs.config import config_options as opt @@ -166,10 +165,6 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: autorefs.scan_toc = False config.plugins["autorefs"] = autorefs _logger.debug("Added a subdued autorefs instance %r", autorefs) - # YORE: Bump 1: Remove block. - with catch_warnings(): - simplefilter("ignore", category=DeprecationWarning) - autorefs.get_fallback_anchor = handlers.get_anchors mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) config.markdown_extensions.append(mkdocstrings_extension) # type: ignore[arg-type] diff --git a/src/mkdocstrings/extension.py b/src/mkdocstrings/extension.py deleted file mode 100644 index c7943652..00000000 --- a/src/mkdocstrings/extension.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import extension - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.extension` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(extension, name) diff --git a/src/mkdocstrings/handlers/__init__.py b/src/mkdocstrings/handlers/__init__.py deleted file mode 100644 index b684324a..00000000 --- a/src/mkdocstrings/handlers/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. diff --git a/src/mkdocstrings/handlers/base.py b/src/mkdocstrings/handlers/base.py deleted file mode 100644 index c55a50ba..00000000 --- a/src/mkdocstrings/handlers/base.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal.handlers import base - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.handlers.base` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(base, name) diff --git a/src/mkdocstrings/handlers/rendering.py b/src/mkdocstrings/handlers/rendering.py deleted file mode 100644 index f3f04eea..00000000 --- a/src/mkdocstrings/handlers/rendering.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal.handlers import rendering - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.handlers.rendering` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(rendering, name) diff --git a/src/mkdocstrings/inventory.py b/src/mkdocstrings/inventory.py deleted file mode 100644 index 7192acff..00000000 --- a/src/mkdocstrings/inventory.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import inventory - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.inventory` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(inventory, name) diff --git a/src/mkdocstrings/loggers.py b/src/mkdocstrings/loggers.py deleted file mode 100644 index 25545ca5..00000000 --- a/src/mkdocstrings/loggers.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import loggers - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.loggers` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(loggers, name) diff --git a/src/mkdocstrings/plugin.py b/src/mkdocstrings/plugin.py deleted file mode 100644 index dbb6abf9..00000000 --- a/src/mkdocstrings/plugin.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Deprecated. Import from `mkdocstrings` directly.""" - -# YORE: Bump 1: Remove file. - -import warnings -from typing import Any - -from mkdocstrings._internal import plugin - - -def __getattr__(name: str) -> Any: - warnings.warn( - "Importing from `mkdocstrings.plugin` is deprecated. Import from `mkdocstrings` directly.", - DeprecationWarning, - stacklevel=2, - ) - return getattr(plugin, name) diff --git a/tests/test_api.py b/tests/test_api.py index ea672073..9821e65c 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -152,8 +152,6 @@ def test_inventory_matches_api( ) -> None: """The inventory doesn't contain any additional Python object.""" not_in_api = [] - # YORE: Bump 1: Remove line. - deprecated_modules = {"extension", "handlers", "inventory", "loggers", "plugin"} public_api_paths = {obj.path for obj in public_objects} public_api_paths.add("mkdocstrings") for item in inventory.values(): @@ -163,9 +161,6 @@ def test_inventory_matches_api( and (item.name == "mkdocstrings" or item.name.startswith("mkdocstrings.")) ): obj = loader.modules_collection[item.name] - # YORE: Bump 1: Remove block. - if any(obj.path.startswith(f"mkdocstrings.{module}") for module in deprecated_modules): - continue if obj.path not in public_api_paths and not any(path in public_api_paths for path in obj.aliases): not_in_api.append(item.name) From 68760a9ec55772c8b330b056c2d0896877324b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 27 Nov 2025 16:39:22 +0100 Subject: [PATCH 31/51] chore: Prepare release 1.0.0 --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6346ccd3..9def4ba8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,37 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.0) - 2025-11-27 + +[Compare with 0.30.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.1...1.0.0) + +### Breaking Changes + +- `BaseHandler.name`: *Attribute value was changed*: `''` -> unset +- `BaseHandler.domain`: *Attribute value was changed*: `''` -> unset +- `BaseHandler.fallback_config`: *Public object was removed* +- `BaseHandler.__init__(args)`: *Parameter was removed* +- `BaseHandler.__init__(kwargs)`: *Parameter was removed* +- `BaseHandler.__init__(theme)`: *Parameter was added as required* +- `BaseHandler.__init__(custom_templates)`: *Parameter was added as required* +- `BaseHandler.__init__(mdx)`: *Parameter was added as required* +- `BaseHandler.__init__(mdx_config)`: *Parameter was added as required* +- `BaseHandler.update_env(args)`: *Parameter was removed* +- `BaseHandler.update_env(kwargs)`: *Parameter was removed* +- `BaseHandler.update_env(config)`: *Parameter was added as required* +- `Handlers.get_anchors`: *Public object was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.plugin`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.loggers`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.inventory`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.extension`: *Public module was removed* (import from `mkdocstrings` directly) +- `mkdocstrings.handlers`: *Public module was removed* (import from `mkdocstrings` directly) + +### Code Refactoring + +- Remove deprecated code before v1 ([de34044](https://github.com/mkdocstrings/mkdocstrings/commit/de34044a02b45250e215af0f969dca581dfb82c5) by Timothée Mazzucotelli). +- Expect Zensical to pass extension configuration instead of loading it again from YAML ([6b73d5a](https://github.com/mkdocstrings/mkdocstrings/commit/6b73d5a2f455062ab6c68376c85adce6adc037a3) by Timothée Mazzucotelli). +- Expose the Markdown extension, to make mkdocstrings compatible with Zensical ([6de2667](https://github.com/mkdocstrings/mkdocstrings/commit/6de266759b79eb72cddd300e6a0a8576085fae40) by Timothée Mazzucotelli). + ## [0.30.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.1) - 2025-09-19 [Compare with 0.30.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.0...0.30.1) From cc54d77f63ecb97c4bcc6fb1b447eddad6e842ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 27 Nov 2025 16:40:59 +0100 Subject: [PATCH 32/51] chore: Template upgrade --- .copier-answers.yml | 2 +- duties.py | 41 ++++++++++------------------------------- mkdocs.yml | 5 +---- 3 files changed, 12 insertions(+), 36 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index 6c5d650e..dafd61dc 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.11.1 +_commit: 1.11.2 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/duties.py b/duties.py index 04357dbb..653e2f0d 100644 --- a/duties.py +++ b/duties.py @@ -5,16 +5,12 @@ import os import re import sys -from contextlib import contextmanager -from importlib.metadata import version as pkgversion from pathlib import Path from typing import TYPE_CHECKING from duty import duty, tools if TYPE_CHECKING: - from collections.abc import Iterator - from duty.context import Context @@ -36,18 +32,6 @@ def pyprefix(title: str) -> str: return title -@contextmanager -def material_insiders() -> Iterator[bool]: - if "+insiders" in pkgversion("mkdocs-material"): - os.environ["MATERIAL_INSIDERS"] = "true" - try: - yield True - finally: - os.environ.pop("MATERIAL_INSIDERS") - else: - yield False - - def _get_changelog_version() -> str: changelog_version_re = re.compile(r"^## \[(\d+\.\d+\.\d+)\].*$") with Path(__file__).parent.joinpath("CHANGELOG.md").open("r", encoding="utf8") as file: @@ -84,11 +68,10 @@ def check_docs(ctx: Context) -> None: """Check if the documentation builds correctly.""" Path("htmlcov").mkdir(parents=True, exist_ok=True) Path("htmlcov/index.html").touch(exist_ok=True) - with material_insiders(): - ctx.run( - tools.mkdocs.build(strict=True, verbose=True), - title=pyprefix("Building documentation"), - ) + ctx.run( + tools.mkdocs.build(strict=True, verbose=True), + title=pyprefix("Building documentation"), + ) @duty(nofail=PY_VERSION == PY_DEV) @@ -120,22 +103,18 @@ def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000 host: The host to serve the docs from. port: The port to serve the docs on. """ - with material_insiders(): - ctx.run( - tools.mkdocs.serve(dev_addr=f"{host}:{port}").add_args(*cli_args), - title="Serving documentation", - capture=False, - ) + ctx.run( + tools.mkdocs.serve(dev_addr=f"{host}:{port}").add_args(*cli_args), + title="Serving documentation", + capture=False, + ) @duty def docs_deploy(ctx: Context) -> None: """Deploy the documentation to GitHub pages.""" os.environ["DEPLOY"] = "true" - with material_insiders() as insiders: - if not insiders: - ctx.run(lambda: False, title="Not deploying docs without Material for MkDocs Insiders!") - ctx.run(tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), title="Deploying documentation") + ctx.run(tools.mkdocs.gh_deploy(force=True), title="Deploying documentation") @duty diff --git a/mkdocs.yml b/mkdocs.yml index 8250973b..d2c5085e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -188,10 +188,7 @@ plugins: reference/plugin.md: reference/api.md#mkdocstrings.plugin - minify: minify_html: !ENV [DEPLOY, false] -- group: - enabled: !ENV [MATERIAL_INSIDERS, false] - plugins: - - typeset +- typeset extra: social: From afefc0fbccc09f7b6f0c92cf3446434d241f1658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sun, 30 Nov 2025 15:40:34 +0100 Subject: [PATCH 33/51] chore: Fix docs-deploy duty --- duties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/duties.py b/duties.py index 653e2f0d..0759f7a5 100644 --- a/duties.py +++ b/duties.py @@ -114,7 +114,7 @@ def docs(ctx: Context, *cli_args: str, host: str = "127.0.0.1", port: int = 8000 def docs_deploy(ctx: Context) -> None: """Deploy the documentation to GitHub pages.""" os.environ["DEPLOY"] = "true" - ctx.run(tools.mkdocs.gh_deploy(force=True), title="Deploying documentation") + ctx.run(tools.mkdocs.gh_deploy(remote_name="org-pages", force=True), title="Deploying documentation") @duty From b6a33e23ba411159253ee44d1b1fa63b9a9a7b7a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 6 Jan 2026 01:02:44 +0000 Subject: [PATCH 34/51] chore: Update sponsors section in README --- README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a486ed40..e28c653b 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,6 @@ See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for mo
Silver sponsors

-Material for MkDocs
FastAPI
Pydantic

@@ -165,23 +164,18 @@ See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for mo machow BenHammersley trevorWieland -laenan8466 MarcoGorelli analog-cbarber OdinManiac rstudio-sponsorship schlich -SuperCowPowers butterlyn livingbio NemetschekAllplan EricJayHartman 15r10nk -cdwilson activeloopai roboflow -wrath-codes -leodevian cmclaughlin blaisep RapidataAI @@ -190,9 +184,10 @@ See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for mo blakeNaccarato ChargeStorm Alphadelta14 +Cusp-AI

-*And 8 more private sponsor(s).* +*And 7 more private sponsor(s).* From f43f1ee2cd38a0dba64fc7d0db3c5ffb037bf7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 15 Jan 2026 15:06:16 +0100 Subject: [PATCH 35/51] refactor: Support cross-references in Zensical PR-812: https://github.com/mkdocstrings/mkdocstrings/pull/812 --- src/mkdocstrings/_internal/extension.py | 42 +++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 3a6f4530..da713a07 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -34,7 +34,7 @@ from markdown.extensions import Extension from markdown.treeprocessors import Treeprocessor from mkdocs.exceptions import PluginError -from mkdocs_autorefs import AutorefsConfig, AutorefsPlugin +from mkdocs_autorefs import AutorefsConfig, AutorefsExtension, AutorefsPlugin from mkdocstrings._internal.handlers.base import BaseHandler, CollectionError, CollectorItem, Handlers from mkdocstrings._internal.loggers import get_logger @@ -316,17 +316,26 @@ class MkdocstringsExtension(Extension): It cannot work outside of `mkdocstrings`. """ - def __init__(self, handlers: Handlers, autorefs: AutorefsPlugin, **kwargs: Any) -> None: + def __init__( + self, + handlers: Handlers, + autorefs: AutorefsPlugin, + *, + autorefs_extension: bool = False, + **kwargs: Any, + ) -> None: """Initialize the object. Arguments: handlers: The handlers container. autorefs: The autorefs plugin instance. + autorefs_extension: Whether the autorefs extension must be registered. **kwargs: Keyword arguments used by `markdown.extensions.Extension`. """ super().__init__(**kwargs) self._handlers = handlers self._autorefs = autorefs + self._autorefs_extension = autorefs_extension def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent method's name) """Register the extension. @@ -336,6 +345,12 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me Arguments: md: A `markdown.Markdown` instance. """ + md.registerExtension(self) + + # Zensical integration: get the current page from the Zensical-specific preprocessor. + if "zensical_current_page" in md.preprocessors: + self._autorefs.current_page = md.preprocessors["zensical_current_page"] # type: ignore[assignment] + md.parser.blockprocessors.register( AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs), "mkdocstrings", @@ -352,6 +367,9 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me priority=4, # Right after 'toc'. ) + if self._autorefs_extension: + AutorefsExtension(self._autorefs).extendMarkdown(md) + # ----------------------------------------------------------------------------- # The following is only used by Zensical. The goal is to provide temporary @@ -421,10 +439,22 @@ def makeExtension( # noqa: N802 tool_config=tool_config, ) - handlers_instance._download_inventories() - autorefs = AutorefsPlugin() autorefs.config = AutorefsConfig() - autorefs.scan_toc = False + autorefs.config.resolve_closest = True + autorefs.config.link_titles = "auto" + autorefs.config.strip_title_tags = "auto" + autorefs.scan_toc = True + autorefs._link_titles = "external" + autorefs._strip_title_tags = False - return MkdocstringsExtension(handlers=handlers_instance, autorefs=autorefs) + handlers_instance._download_inventories() + register = autorefs.register_url + for identifier, url in handlers_instance._yield_inventory_items(): + register(identifier, url) + + return MkdocstringsExtension( + handlers=handlers_instance, + autorefs=autorefs, + autorefs_extension=True, + ) From 0edd18af00fa5907bb31e8309ecf5ad4309da552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 15 Jan 2026 15:06:54 +0100 Subject: [PATCH 36/51] chore: Clean up after v1 --- CHANGELOG.md | 5 +---- docs/usage/handlers.md | 2 +- mkdocs.yml | 7 ------- pyproject.toml | 2 +- src/mkdocstrings/_internal/handlers/base.py | 4 ++-- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9def4ba8..4fac770e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -952,8 +952,6 @@ See issue [#74](https://github.com/pawamoy/mkdocstrings/issues/74). ### Features - Prepare for new `pytkdocs` version ([336421a](https://github.com/pawamoy/mkdocstrings/commit/336421af95d752671276c2e88c5c173bff4093cc)). Add options `filters` and `members` to the Python collector to reflect the new `pytkdocs` options. - See [the default configuration of the Python collector](https://pawamoy.github.io/mkdocstrings/reference/handlers/python/#mkdocstrings.handlers.python.PythonCollector.DEFAULT_CONFIG). - ## [0.9.1](https://github.com/pawamoy/mkdocstrings/releases/tag/0.9.1) - 2020-03-21 @@ -979,8 +977,7 @@ No identified breaking changes for end-users. - **Better cross-references:** cross-references now not only work between documented objects (between all languages, given the objects' identifiers are unique), but also for every heading of your Markdown pages. - **Configuration options:** the rendering of Python documentation can now be configured, - (globally and locally thanks to the handlers system), - [check the docs!](https://pawamoy.github.io/mkdocstrings/reference/handlers/python/#mkdocstrings.handlers.python.PythonRenderer.DEFAULT_CONFIG) + (globally and locally thanks to the handlers system). Also see the [recommended CSS](https://pawamoy.github.io/mkdocstrings/handlers/python/#recommended-style). - **Proper logging messages:** `mkdocstrings` now logs debug, warning and error messages, useful when troubleshooting. diff --git a/docs/usage/handlers.md b/docs/usage/handlers.md index efdaccd1..6f326431 100644 --- a/docs/usage/handlers.md +++ b/docs/usage/handlers.md @@ -205,7 +205,7 @@ to use the templates of another handler. In you handler, override the ```python from pathlib import Path -from mkdocstrings.handlers.base import BaseHandler +from mkdocstrings import BaseHandler class CobraHandler(BaseHandler): diff --git a/mkdocs.yml b/mkdocs.yml index d2c5085e..8423ce25 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -179,13 +179,6 @@ plugins: handlers/overview.md: usage/handlers.md reference/mkdocstrings.md: reference/api.md reference/index.md: reference/api.md#mkdocstrings - reference/extension.md: reference/api.md#mkdocstrings.extension - reference/handlers/index.md: reference/api.md#mkdocstrings.handlers - reference/handlers/base.md: reference/api.md#mkdocstrings.handlers.base - reference/handlers/rendering.md: reference/api.md#mkdocstrings.handlers.rendering - reference/inventory.md: reference/api.md#mkdocstrings.inventory - reference/loggers.md: reference/api.md#mkdocstrings.loggers - reference/plugin.md: reference/api.md#mkdocstrings.plugin - minify: minify_html: !ENV [DEPLOY, false] - typeset diff --git a/pyproject.toml b/pyproject.toml index 2b3d6c17..7f033477 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,7 +113,7 @@ ci = [ "mkdocs-minify-plugin>=0.8", "mkdocs-redirects>=1.2.1", "mkdocs-section-index>=0.3", - "mkdocstrings-python>=1.16.2", + "mkdocstrings-python>=2.0", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", ] diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 2eb9b3e6..05808ef9 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -241,7 +241,7 @@ def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem: Arguments: identifier: An identifier for which to collect data. For example, in Python, - it would be 'mkdocstrings.handlers' to collect documentation about the handlers module. + it would be 'mkdocstrings.BaseHandler' to collect documentation about the BaseHandler class. It can be anything that you can feed to the tool of your choice. options: The final configuration options. @@ -560,7 +560,7 @@ def get_handler_config(self, name: str) -> dict: def get_handler(self, name: str, handler_config: dict | None = None) -> BaseHandler: """Get a handler thanks to its name. - This function dynamically imports a module named "mkdocstrings.handlers.NAME", calls its + This function dynamically imports a module named "mkdocstrings_handlers.NAME", calls its `get_handler` method to get an instance of a handler, and caches it in dictionary. It means that during one run (for each reload when serving, or once when building), a handler is instantiated only once, and reused for each "autodoc" instruction asking for it. From d37d9079e5381350b2e3ffc5f698e28a5b572d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 17 Jan 2026 19:20:23 +0100 Subject: [PATCH 37/51] refactor: Support manual cross-references in Zensical too --- src/mkdocstrings/_internal/extension.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index da713a07..b9174aec 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -387,7 +387,7 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me } -def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str], dict[str, Any]]: +def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str | Extension], dict[str, Any]]: # Split markdown extensions and their configs from mkdocs.yml mdx: list[str] = [] mdx_config: dict[str, Any] = {} @@ -399,7 +399,7 @@ def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str], di mdx.append(key) mdx_config[key] = value break # Only one item per dict - return mdx, mdx_config + return mdx, mdx_config # type: ignore[return-value] class _ToolConfig: @@ -426,6 +426,17 @@ def makeExtension( # noqa: N802 mdx, mdx_config = _split_configs(markdown_extensions or []) tool_config = _ToolConfig(config_file_path=config_file_path) + autorefs = AutorefsPlugin() + autorefs.config = AutorefsConfig() + autorefs.config.resolve_closest = True + autorefs.config.link_titles = "auto" + autorefs.config.strip_title_tags = "auto" + autorefs.scan_toc = True + autorefs._link_titles = "external" + autorefs._strip_title_tags = False + + mdx.append(AutorefsExtension(autorefs)) + handlers_instance = Handlers( theme="material", default=default_handler or _default_config["default_handler"], @@ -439,15 +450,6 @@ def makeExtension( # noqa: N802 tool_config=tool_config, ) - autorefs = AutorefsPlugin() - autorefs.config = AutorefsConfig() - autorefs.config.resolve_closest = True - autorefs.config.link_titles = "auto" - autorefs.config.strip_title_tags = "auto" - autorefs.scan_toc = True - autorefs._link_titles = "external" - autorefs._strip_title_tags = False - handlers_instance._download_inventories() register = autorefs.register_url for identifier, url in handlers_instance._yield_inventory_items(): From cb8a3c781674b8e23cf0048955d70358d7bab49e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 19 Jan 2026 12:36:12 +0100 Subject: [PATCH 38/51] chore: Prepare release 1.0.1 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fac770e..5e8050e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.1) - 2026-01-19 + +[Compare with 1.0.0](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.0...1.0.1) + +### Code Refactoring + +- Support manual cross-references in Zensical too ([d37d907](https://github.com/mkdocstrings/mkdocstrings/commit/d37d9079e5381350b2e3ffc5f698e28a5b572d36) by Timothée Mazzucotelli). +- Support cross-references in Zensical ([f43f1ee](https://github.com/mkdocstrings/mkdocstrings/commit/f43f1ee2cd38a0dba64fc7d0db3c5ffb037bf7f7) by Timothée Mazzucotelli). [PR-812](https://github.com/mkdocstrings/mkdocstrings/pull/812) + ## [1.0.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.0) - 2025-11-27 [Compare with 0.30.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.30.1...1.0.0) From 9f79141d7eb35aba0c89a43795df0ee22a25a61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 24 Jan 2026 16:57:02 +0100 Subject: [PATCH 39/51] refactor: Use global instances for handlers and autorefs This allows to reduce redudant computations while running through Zensical. For example, we won't load (read from the disk) inventories on each page rendering, only once per build. --- src/mkdocstrings/_internal/extension.py | 93 ++++++++++++++++--------- 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index b9174aec..d20e57ac 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -407,6 +407,10 @@ def __init__(self, config_file_path: str | None = None) -> None: self.config_file_path = config_file_path +_AUTOREFS = None +_HANDLERS = None + + def makeExtension( # noqa: N802 *, default_handler: str | None = None, @@ -423,40 +427,65 @@ def makeExtension( # noqa: N802 We only support this function being used by Zensical. Consider this function private API. """ - mdx, mdx_config = _split_configs(markdown_extensions or []) - tool_config = _ToolConfig(config_file_path=config_file_path) - - autorefs = AutorefsPlugin() - autorefs.config = AutorefsConfig() - autorefs.config.resolve_closest = True - autorefs.config.link_titles = "auto" - autorefs.config.strip_title_tags = "auto" - autorefs.scan_toc = True - autorefs._link_titles = "external" - autorefs._strip_title_tags = False - - mdx.append(AutorefsExtension(autorefs)) - - handlers_instance = Handlers( - theme="material", - default=default_handler or _default_config["default_handler"], - inventory_project=inventory_project or "Project", - inventory_version=inventory_version or "0.0.0", - handlers_config=handlers or _default_config["handlers"], - custom_templates=custom_templates or _default_config["custom_templates"], - mdx=mdx, - mdx_config=mdx_config, - locale=locale or _default_config["locale"], - tool_config=tool_config, - ) + global _AUTOREFS # noqa: PLW0603 + if _AUTOREFS is None: + _AUTOREFS = AutorefsPlugin() + _AUTOREFS.config = AutorefsConfig() + _AUTOREFS.config.resolve_closest = True + _AUTOREFS.config.link_titles = "auto" + _AUTOREFS.config.strip_title_tags = "auto" + _AUTOREFS.scan_toc = True + _AUTOREFS._link_titles = "external" + _AUTOREFS._strip_title_tags = False + + global _HANDLERS # noqa: PLW0603 + if _HANDLERS is None: + mdx, mdx_config = _split_configs(markdown_extensions or []) + tool_config = _ToolConfig(config_file_path=config_file_path) + mdx.append(AutorefsExtension(_AUTOREFS)) + _HANDLERS = Handlers( + theme="material", + default=default_handler or _default_config["default_handler"], + inventory_project=inventory_project or "Project", + inventory_version=inventory_version or "0.0.0", + handlers_config=handlers or _default_config["handlers"], + custom_templates=custom_templates or _default_config["custom_templates"], + mdx=mdx, + mdx_config=mdx_config, + locale=locale or _default_config["locale"], + tool_config=tool_config, + ) - handlers_instance._download_inventories() - register = autorefs.register_url - for identifier, url in handlers_instance._yield_inventory_items(): - register(identifier, url) + _HANDLERS._download_inventories() + register = _AUTOREFS.register_url + for identifier, url in _HANDLERS._yield_inventory_items(): + register(identifier, url) return MkdocstringsExtension( - handlers=handlers_instance, - autorefs=autorefs, + handlers=_HANDLERS, + autorefs=_AUTOREFS, autorefs_extension=True, ) + + +def _reset() -> None: + global _AUTOREFS, _HANDLERS # noqa: PLW0603 + _AUTOREFS = None + _HANDLERS = None + + +def _get_autorefs() -> dict[str, Any]: + if _AUTOREFS: + return { + "primary": _AUTOREFS._primary_url_map, + "secondary": _AUTOREFS._secondary_url_map, + "inventory": _AUTOREFS._abs_url_map, + "titles": _AUTOREFS._title_map, + } + return {} + + +def _get_inventory() -> bytes: + if _HANDLERS: + return _HANDLERS.inventory.format_sphinx() + return b"" From 4e66617fbe26636f86117c26b2482c21892166fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 24 Jan 2026 16:57:13 +0100 Subject: [PATCH 40/51] chore: Prepare release 1.0.2 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e8050e6..d1c96386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.2) - 2026-01-24 + +[Compare with 1.0.1](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.1...1.0.2) + +### Code Refactoring + +- Use global instances for handlers and autorefs ([9f79141](https://github.com/mkdocstrings/mkdocstrings/commit/9f79141d7eb35aba0c89a43795df0ee22a25a61e) by Timothée Mazzucotelli). + ## [1.0.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.1) - 2026-01-19 [Compare with 1.0.0](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.0...1.0.1) From dbf263dfdd2fdd769d66fa62bdd388e05988bc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 5 Feb 2026 18:34:27 +0100 Subject: [PATCH 41/51] fix: Propagate Zensical's `zrelpath` processor --- src/mkdocstrings/_internal/handlers/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 05808ef9..4e0e7dfb 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -477,6 +477,10 @@ def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: relpath = md.treeprocessors["relpath"] new_relpath = type(relpath)(relpath.file, relpath.files, relpath.config) # type: ignore[attr-defined,call-arg] new_md.treeprocessors.register(new_relpath, "relpath", priority=0) + elif "zrelpath" in md.treeprocessors: + zrelpath = md.treeprocessors["zrelpath"] + new_zrelpath = type(zrelpath)(new_md, zrelpath.path, zrelpath.use_directory_urls) # type: ignore[attr-defined,call-arg] + new_md.treeprocessors.register(new_zrelpath, "zrelpath", priority=0) self._md = new_md From cc3d6a4d286668a9a249ac38dfaf8b45470f3314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 5 Feb 2026 18:37:33 +0100 Subject: [PATCH 42/51] chore: Template upgrade --- .copier-answers.yml | 2 +- .github/workflows/ci.yml | 6 +++++- .gitignore | 1 - config/mypy.ini | 5 ----- config/ruff.toml | 25 +++---------------------- config/vscode/settings.json | 3 --- duties.py | 9 ++++----- pyproject.toml | 4 ++-- scripts/gen_credits.py | 23 ++++++++++++----------- scripts/get_version.py | 2 +- scripts/make.py | 14 +++++++------- src/mkdocstrings/_internal/debug.py | 2 +- tests/test_api.py | 6 +++--- 13 files changed, 39 insertions(+), 63 deletions(-) delete mode 100644 config/mypy.ini diff --git a/.copier-answers.yml b/.copier-answers.yml index dafd61dc..3c48bac1 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.11.2 +_commit: 1.11.6 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54dd741c..a8d0a185 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,8 +44,11 @@ jobs: python-version: "3.12" - os: ubuntu-latest python-version: "3.13" + - os: ubuntu-latest + python-version: "3.15-dev" runs-on: ${{ matrix.os }} + continue-on-error: true steps: - name: Checkout @@ -103,7 +106,7 @@ jobs: - "3.12" - "3.13" - "3.14" - - "3.15" + - "3.15-dev" resolution: - highest - lowest-direct @@ -112,6 +115,7 @@ jobs: resolution: lowest-direct - os: windows-latest resolution: lowest-direct + runs-on: ${{ matrix.os }} continue-on-error: true diff --git a/.gitignore b/.gitignore index 9fea0472..faeb06ae 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,5 @@ uv.lock # cache .cache/ .pytest_cache/ -.mypy_cache/ .ruff_cache/ __pycache__/ diff --git a/config/mypy.ini b/config/mypy.ini deleted file mode 100644 index 814e2ac8..00000000 --- a/config/mypy.ini +++ /dev/null @@ -1,5 +0,0 @@ -[mypy] -ignore_missing_imports = true -exclude = tests/fixtures/ -warn_unused_ignores = true -show_error_codes = true diff --git a/config/ruff.toml b/config/ruff.toml index 65416253..db162aba 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -5,30 +5,9 @@ line-length = 120 exclude = [ "tests/fixtures/*.py", ] -select = [ - "A", "ANN", "ARG", - "B", "BLE", - "C", "C4", - "COM", - "D", "DTZ", - "E", "ERA", "EXE", - "F", "FBT", - "G", - "I", "ICN", "INP", "ISC", - "N", - "PGH", "PIE", "PL", "PLC", "PLE", "PLR", "PLW", "PT", "PYI", - "Q", - "RUF", "RSE", "RET", - "S", "SIM", "SLF", - "T", "T10", "T20", "TCH", "TID", "TRY", - "UP", - "W", - "YTT", -] +select = ["ALL"] ignore = [ "A001", # Variable is shadowing a Python builtin - "ANN101", # Missing type annotation for self - "ANN102", # Missing type annotation for cls "ANN204", # Missing return type annotation for special method __str__ "ANN401", # Dynamically typed expressions (typing.Any) are disallowed "ARG005", # Unused lambda argument @@ -36,6 +15,8 @@ ignore = [ "D105", # Missing docstring in magic method "D417", # Missing argument description in the docstring "E501", # Line too long + "EM101", # String literal when raising exception + "EM102", # f-string when raising exception "ERA001", # Commented out code "G004", # Logging statement uses f-string "PLR0911", # Too many return statements diff --git a/config/vscode/settings.json b/config/vscode/settings.json index 949856d1..87ecd639 100644 --- a/config/vscode/settings.json +++ b/config/vscode/settings.json @@ -4,9 +4,6 @@ "**/.venvs*/**": true, "**/venv*/**": true }, - "mypy-type-checker.args": [ - "--config-file=config/mypy.ini" - ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.testing.pytestArgs": [ diff --git a/duties.py b/duties.py index 0759f7a5..3f09916a 100644 --- a/duties.py +++ b/duties.py @@ -22,7 +22,7 @@ PTY = not WINDOWS and not CI MULTIRUN = os.environ.get("MULTIRUN", "0") == "1" PY_VERSION = f"{sys.version_info.major}{sys.version_info.minor}" -PY_DEV = "314" +PY_DEV = "315" def pyprefix(title: str) -> str: @@ -35,7 +35,7 @@ def pyprefix(title: str) -> str: def _get_changelog_version() -> str: changelog_version_re = re.compile(r"^## \[(\d+\.\d+\.\d+)\].*$") with Path(__file__).parent.joinpath("CHANGELOG.md").open("r", encoding="utf8") as file: - return next(filter(bool, map(changelog_version_re.match, file))).group(1) # type: ignore[union-attr] + return next(filter(bool, map(changelog_version_re.match, file))).group(1) # ty: ignore[invalid-argument-type] @duty @@ -77,10 +77,9 @@ def check_docs(ctx: Context) -> None: @duty(nofail=PY_VERSION == PY_DEV) def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" - os.environ["MYPYPATH"] = "src" - os.environ["FORCE_COLOR"] = "1" + py = f"{sys.version_info.major}.{sys.version_info.minor}" ctx.run( - tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"), + tools.ty.check(*PY_SRC_LIST, color=True, error_on_warning=True, python_version=py), title=pyprefix("Type-checking"), ) diff --git a/pyproject.toml b/pyproject.toml index 7f033477..f661a98b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,7 +62,7 @@ getter = "scripts.get_version:get_version" [tool.pdm.build] # Include as much as possible in the source distribution, to help redistributors. -excludes = ["**/.pytest_cache", "**/.mypy_cache"] +excludes = ["**/.pytest_cache"] source-includes = [ "config", "docs", @@ -98,7 +98,7 @@ ci = [ "pytest-cov>=5.0", "pytest-randomly>=3.15", "pytest-xdist>=3.6", - "mypy>=1.10", + "ty>=0.0.14", "types-markdown>=3.6", "types-pyyaml>=6.0", ] diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py index b5499b7a..8e8a7bf8 100644 --- a/scripts/gen_credits.py +++ b/scripts/gen_credits.py @@ -47,7 +47,7 @@ def _norm_name(name: str) -> str: return name.replace("_", "-").replace(".", "-").lower() -def _requirements(deps: list[str]) -> dict[str, Requirement]: +def _requirements(deps: Iterable[str]) -> dict[str, Requirement]: return {_norm_name((req := Requirement(dep)).name): req for dep in deps} @@ -63,8 +63,8 @@ def _extra_marker(req: Requirement) -> str | None: def _get_metadata() -> Metadata: metadata = {} for pkg in distributions(): - name = _norm_name(pkg.name) # type: ignore[attr-defined,unused-ignore] - metadata[name] = _merge_fields(pkg.metadata) # type: ignore[arg-type] + name = _norm_name(pkg.name) + metadata[name] = _merge_fields(pkg.metadata) # ty: ignore[invalid-argument-type] metadata[name]["spec"] = set() metadata[name]["extras"] = set() metadata[name].setdefault("summary", "") @@ -77,10 +77,11 @@ def _set_license(metadata: PackageMetadata) -> None: license_name = license_field if isinstance(license_field, str) else " + ".join(license_field) check_classifiers = license_name in ("UNKNOWN", "Dual License", "") or license_name.count("\n") if check_classifiers: - license_names = [] - for classifier in metadata["classifier"]: - if classifier.startswith("License ::"): - license_names.append(classifier.rsplit("::", 1)[1].strip()) + license_names = [ + classifier.rsplit("::", 1)[1].strip() + for classifier in metadata["classifier"] + if classifier.startswith("License ::") + ] license_name = " + ".join(license_names) metadata["license"] = license_name or "?" @@ -90,8 +91,8 @@ def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata for dep_name, dep_req in base_deps.items(): if dep_name not in metadata or dep_name == "mkdocstrings": continue - metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # type: ignore[operator] - metadata[dep_name]["extras"] |= dep_req.extras # type: ignore[operator] + metadata[dep_name]["spec"] |= {str(spec) for spec in dep_req.specifier} # ty: ignore[unsupported-operator] + metadata[dep_name]["extras"] |= dep_req.extras # ty: ignore[unsupported-operator] deps[dep_name] = metadata[dep_name] again = True @@ -109,7 +110,7 @@ def _get_deps(base_deps: dict[str, Requirement], metadata: Metadata) -> Metadata and dep_name != project["name"] and (not extra_marker or extra_marker in deps[pkg_name]["extras"]) ): - metadata[dep_name]["spec"] |= {str(spec) for spec in requirement.specifier} # type: ignore[operator] + metadata[dep_name]["spec"] |= {str(spec) for spec in requirement.specifier} # ty: ignore[unsupported-operator] deps[dep_name] = metadata[dep_name] again = True @@ -121,7 +122,7 @@ def _render_credits() -> str: dev_dependencies = _get_deps(_requirements(devdeps), metadata) prod_dependencies = _get_deps( _requirements( - chain( # type: ignore[arg-type] + chain( project.get("dependencies", []), chain(*project.get("optional-dependencies", {}).values()), ), diff --git a/scripts/get_version.py b/scripts/get_version.py index 3c425a73..d56f5858 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -22,7 +22,7 @@ def get_version() -> str: if scm_version.version <= Version("0.1"): # Missing Git tags? with suppress(OSError, StopIteration): # noqa: SIM117 with _changelog.open("r", encoding="utf8") as file: - match = next(filter(None, map(_changelog_version_re.match, file))) + match = next(filter(None, map(_changelog_version_re.match, file))) # ty: ignore[invalid-argument-type] scm_version = scm_version._replace(version=Version(match.group(1))) return default_version_formatter(scm_version) diff --git a/scripts/make.py b/scripts/make.py index b741a366..7fa7b56d 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -117,8 +117,8 @@ def clean() -> None: for path in paths_to_clean: shutil.rmtree(path, ignore_errors=True) - cache_dirs = {".cache", ".pytest_cache", ".mypy_cache", ".ruff_cache", "__pycache__"} - for dirpath in Path(".").rglob("*/"): + cache_dirs = {".cache", ".pytest_cache", ".ruff_cache", "__pycache__"} + for dirpath in Path().rglob("*/"): if dirpath.parts[0] not in (".venv", ".venvs") and dirpath.name in cache_dirs: shutil.rmtree(dirpath, ignore_errors=True) @@ -151,7 +151,7 @@ def main() -> int: ), flush=True, ) - if os.path.exists(".venv"): + if Path(".venv").exists(): print("\nAvailable tasks", flush=True) run("default", "duty", "--list") return 0 @@ -163,28 +163,28 @@ def main() -> int: if not args: print("make: run: missing command", file=sys.stderr) return 1 - run("default", *args) # ty: ignore[missing-argument] + run("default", *args) return 0 if cmd == "multirun": if not args: print("make: run: missing command", file=sys.stderr) return 1 - multirun(*args) # ty: ignore[missing-argument] + multirun(*args) return 0 if cmd == "allrun": if not args: print("make: run: missing command", file=sys.stderr) return 1 - allrun(*args) # ty: ignore[missing-argument] + allrun(*args) return 0 if cmd.startswith("3."): if not args: print("make: run: missing command", file=sys.stderr) return 1 - run(cmd, *args) # ty: ignore[missing-argument] + run(cmd, *args) return 0 opts = [] diff --git a/src/mkdocstrings/_internal/debug.py b/src/mkdocstrings/_internal/debug.py index f6b11600..7b56409b 100644 --- a/src/mkdocstrings/_internal/debug.py +++ b/src/mkdocstrings/_internal/debug.py @@ -85,7 +85,7 @@ def _get_debug_info() -> _Environment: interpreter_version=py_version, interpreter_path=sys.executable, platform=platform.platform(), - variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], # ty: ignore[invalid-argument-type] + variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) diff --git a/tests/test_api.py b/tests/test_api.py index 9821e65c..0d714f0a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -50,7 +50,7 @@ def _yield_public_objects( if modules: yield member yield from _yield_public_objects( - member, # type: ignore[arg-type] + member, # ty: ignore[invalid-argument-type] modules=modules, modulelevel=modulelevel, inherited=inherited, @@ -62,7 +62,7 @@ def _yield_public_objects( continue if member.is_class and not modulelevel: yield from _yield_public_objects( - member, # type: ignore[arg-type] + member, # ty: ignore[invalid-argument-type] modules=modules, modulelevel=False, inherited=inherited, @@ -91,7 +91,7 @@ def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | g def _fixture_inventory() -> Inventory: inventory_file = Path(__file__).parent.parent / "site" / "objects.inv" if not inventory_file.exists(): - pytest.skip("The objects inventory is not available.") # ty: ignore[call-non-callable] + pytest.skip("The objects inventory is not available.") with inventory_file.open("rb") as file: return Inventory.parse_sphinx(file) From 1624e2c52fbdff2ee611142edbf52454427f8f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 5 Feb 2026 18:59:57 +0100 Subject: [PATCH 43/51] ci: Update lint/type-checking --- config/ruff.toml | 3 ++ config/ty.toml | 2 ++ duties.py | 8 ++++- src/mkdocstrings/_internal/download.py | 13 ++++--- src/mkdocstrings/_internal/extension.py | 10 +++--- src/mkdocstrings/_internal/handlers/base.py | 34 +++++++++---------- .../_internal/handlers/rendering.py | 6 ++-- src/mkdocstrings/_internal/loggers.py | 2 +- src/mkdocstrings/_internal/plugin.py | 18 +++++----- tests/conftest.py | 4 +-- tests/test_extension.py | 8 ++--- tests/test_handlers.py | 6 ++-- tests/test_inventory.py | 3 +- 13 files changed, 66 insertions(+), 51 deletions(-) create mode 100644 config/ty.toml diff --git a/config/ruff.toml b/config/ruff.toml index db162aba..95d92083 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -18,6 +18,7 @@ ignore = [ "EM101", # String literal when raising exception "EM102", # f-string when raising exception "ERA001", # Commented out code + "FIX", # TODO, FIXME, etc. "G004", # Logging statement uses f-string "PLR0911", # Too many return statements "PLR0912", # Too many branches @@ -25,6 +26,8 @@ ignore = [ "PLR0915", # Too many statements "SLF001", # Private member accessed "S704", # Unsafe use of `markupsafe.Markup` + "TD002", # Missing author in TODO + "TD003", # Missing issue link for TODO "TRY003", # Avoid specifying long messages outside the exception class ] diff --git a/config/ty.toml b/config/ty.toml new file mode 100644 index 00000000..545856a3 --- /dev/null +++ b/config/ty.toml @@ -0,0 +1,2 @@ +[src] +exclude = ["tests/fixtures"] diff --git a/duties.py b/duties.py index 3f09916a..974ed169 100644 --- a/duties.py +++ b/duties.py @@ -79,7 +79,13 @@ def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" py = f"{sys.version_info.major}.{sys.version_info.minor}" ctx.run( - tools.ty.check(*PY_SRC_LIST, color=True, error_on_warning=True, python_version=py), + tools.ty.check( + *PY_SRC_LIST, + config_file="config/ty.toml", + color=True, + error_on_warning=True, + python_version=py, + ), title=pyprefix("Type-checking"), ) diff --git a/src/mkdocstrings/_internal/download.py b/src/mkdocstrings/_internal/download.py index ffe25e6b..2d8b25f1 100644 --- a/src/mkdocstrings/_internal/download.py +++ b/src/mkdocstrings/_internal/download.py @@ -1,14 +1,19 @@ +from __future__ import annotations + import base64 import gzip import os import re import urllib.parse import urllib.request -from collections.abc import Mapping -from typing import BinaryIO, Optional +from typing import TYPE_CHECKING, BinaryIO from mkdocstrings._internal.loggers import get_logger +if TYPE_CHECKING: + from collections.abc import Mapping + + _logger = get_logger("mkdocstrings") # Regex pattern for an environment variable in the form ${ENV_VAR}. @@ -25,11 +30,11 @@ def _download_url_with_gz(url: str) -> bytes: with urllib.request.urlopen(req) as resp: # noqa: S310 content: BinaryIO = resp if "gzip" in resp.headers.get("content-encoding", ""): - content = gzip.GzipFile(fileobj=resp) # type: ignore[assignment] + content = gzip.GzipFile(fileobj=resp) # ty: ignore[invalid-assignment] return content.read() -def _expand_env_vars(credential: str, url: str, env: Optional[Mapping[str, str]] = None) -> str: +def _expand_env_vars(credential: str, url: str, env: Mapping[str, str] | None = None) -> str: """A safe implementation of environment variable substitution. It only supports the following forms: `${ENV_VAR}`. diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index d20e57ac..8ba43aba 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -189,7 +189,7 @@ def _process_block( if "locale" in signature(handler.render).parameters: render = partial(handler.render, locale=self._handlers._locale) else: - render = handler.render # type: ignore[assignment] + render = handler.render try: rendered = render(data, options) except TemplateNotFound as exc: @@ -301,7 +301,7 @@ def _remove_duplicated_headings(self, parent: Element) -> None: class _TocLabelsTreeProcessor(Treeprocessor): def run(self, root: Element) -> None: # noqa: ARG002 - self._override_toc_labels(self.md.toc_tokens) # type: ignore[attr-defined] + self._override_toc_labels(self.md.toc_tokens) # ty: ignore[unresolved-attribute] def _override_toc_labels(self, tokens: list[dict[str, Any]]) -> None: for token in tokens: @@ -349,7 +349,7 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me # Zensical integration: get the current page from the Zensical-specific preprocessor. if "zensical_current_page" in md.preprocessors: - self._autorefs.current_page = md.preprocessors["zensical_current_page"] # type: ignore[assignment] + self._autorefs.current_page = md.preprocessors["zensical_current_page"] md.parser.blockprocessors.register( AutoDocProcessor(md, handlers=self._handlers, autorefs=self._autorefs), @@ -389,7 +389,7 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str | Extension], dict[str, Any]]: # Split markdown extensions and their configs from mkdocs.yml - mdx: list[str] = [] + mdx: list[str | Extension] = [] mdx_config: dict[str, Any] = {} for item in markdown_extensions: if isinstance(item, str): @@ -399,7 +399,7 @@ def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str | Ex mdx.append(key) mdx_config[key] = value break # Only one item per dict - return mdx, mdx_config # type: ignore[return-value] + return mdx, mdx_config class _ToolConfig: diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 4e0e7dfb..41d89da9 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -150,16 +150,14 @@ def __init__( # add extended theme templates extended_templates_dirs = self.get_extended_templates_dirs(self.name) - for templates_dir in extended_templates_dirs: - paths.append(templates_dir / self.theme) + paths.extend(templates_dir / self.theme for templates_dir in extended_templates_dirs) # add fallback theme templates if self.fallback_theme and self.fallback_theme != self.theme: paths.append(themes_dir / self.fallback_theme) # add fallback theme of extended templates - for templates_dir in extended_templates_dirs: - paths.append(templates_dir / self.fallback_theme) + paths.extend(templates_dir / self.fallback_theme for templates_dir in extended_templates_dirs) for path in paths: css_path = path / "style.css" @@ -361,24 +359,24 @@ def do_convert_markdown( global _markdown_conversion_layer # noqa: PLW0603 _markdown_conversion_layer += 1 treeprocessors = self.md.treeprocessors - treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level # type: ignore[attr-defined] - treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" # type: ignore[attr-defined] - treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph # type: ignore[attr-defined] + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = heading_level + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = html_id and html_id + "--" + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = strip_paragraph if BacklinksTreeProcessor.name in treeprocessors: - treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id # type: ignore[attr-defined] + treeprocessors[BacklinksTreeProcessor.name].initial_id = html_id if autoref_hook and AutorefsInlineProcessor.name in self.md.inlinePatterns: - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # type: ignore[attr-defined] + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = autoref_hook # ty: ignore[unresolved-attribute] try: return Markup(self.md.convert(text)) finally: - treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 # type: ignore[attr-defined] - treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" # type: ignore[attr-defined] - treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False # type: ignore[attr-defined] + treeprocessors[HeadingShiftingTreeprocessor.name].shift_by = 0 + treeprocessors[IdPrependingTreeprocessor.name].id_prefix = "" + treeprocessors[ParagraphStrippingTreeprocessor.name].strip = False if BacklinksTreeProcessor.name in treeprocessors: - treeprocessors[BacklinksTreeProcessor.name].initial_id = None # type: ignore[attr-defined] + treeprocessors[BacklinksTreeProcessor.name].initial_id = None if AutorefsInlineProcessor.name in self.md.inlinePatterns: - self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None # type: ignore[attr-defined] + self.md.inlinePatterns[AutorefsInlineProcessor.name].hook = None self.md.reset() _markdown_conversion_layer -= 1 @@ -475,11 +473,11 @@ def _update_env(self, md: Markdown, *, config: Any | None = None) -> None: # MkDocs adds its own (required) extension that's not part of the config. Propagate it. if "relpath" in md.treeprocessors: relpath = md.treeprocessors["relpath"] - new_relpath = type(relpath)(relpath.file, relpath.files, relpath.config) # type: ignore[attr-defined,call-arg] + new_relpath = type(relpath)(relpath.file, relpath.files, relpath.config) new_md.treeprocessors.register(new_relpath, "relpath", priority=0) elif "zrelpath" in md.treeprocessors: zrelpath = md.treeprocessors["zrelpath"] - new_zrelpath = type(zrelpath)(new_md, zrelpath.path, zrelpath.use_directory_urls) # type: ignore[attr-defined,call-arg] + new_zrelpath = type(zrelpath)(new_md, zrelpath.path, zrelpath.use_directory_urls) new_md.treeprocessors.register(new_zrelpath, "zrelpath", priority=0) self._md = new_md @@ -603,7 +601,7 @@ def _download_inventories(self) -> None: for handler_name, conf in self._handlers_config.items(): handler = self.get_handler(handler_name) - if handler.get_inventory_urls.__func__ is BaseHandler.get_inventory_urls: # type: ignore[attr-defined] + if handler.get_inventory_urls.__func__ is BaseHandler.get_inventory_urls: if inv_configs := conf.pop("import", ()): warn( "mkdocstrings v1 will stop handling 'import' in handlers configuration. " @@ -645,7 +643,7 @@ def _yield_inventory_items(self) -> Iterator[tuple[str, str]]: for fut, (handler, url, conf) in reversed(self._inv_futures.items()): try: yield from handler.load_inventory(BytesIO(fut.result()), url, **conf) - except Exception as error: # noqa: BLE001 + except Exception as error: # noqa: BLE001,PERF203 _logger.error("Couldn't load inventory %s through handler '%s': %s", url, handler.name, error) # noqa: TRY400 self._inv_futures = {} diff --git a/src/mkdocstrings/_internal/handlers/rendering.py b/src/mkdocstrings/_internal/handlers/rendering.py index 25db87e1..264a77ef 100644 --- a/src/mkdocstrings/_internal/handlers/rendering.py +++ b/src/mkdocstrings/_internal/handlers/rendering.py @@ -84,7 +84,7 @@ def __init__(self, md: Markdown): self._css_class = config.pop("css_class", "highlight") super().__init__(**{name: opt for name, opt in config.items() if name in self._highlight_config_keys}) - def highlight( + def highlight( # ty: ignore[invalid-method-override] self, src: str, language: str | None = None, @@ -113,7 +113,7 @@ def highlight( src = textwrap.dedent(src) kwargs.setdefault("css_class", self._css_class) - old_linenums = self.linenums # type: ignore[has-type] + old_linenums = self.linenums if linenums is not None: self.linenums = linenums try: @@ -240,7 +240,7 @@ def __init__(self, md: Markdown, headings: list[Element]): def run(self, root: Element) -> None: """Record all heading elements encountered in the document.""" - permalink_class = self.md.treeprocessors["toc"].permalink_class # type: ignore[attr-defined] + permalink_class = self.md.treeprocessors["toc"].permalink_class for el in root.iter(): if self.regex.fullmatch(el.tag): el = copy.copy(el) # noqa: PLW2901 diff --git a/src/mkdocstrings/_internal/loggers.py b/src/mkdocstrings/_internal/loggers.py index c67a7f4e..29f620a3 100644 --- a/src/mkdocstrings/_internal/loggers.py +++ b/src/mkdocstrings/_internal/loggers.py @@ -82,7 +82,7 @@ def log(self, level: int, msg: object, *args: object, **kwargs: object) -> None: if (key := (self, str(msg))) in self._logged: return self._logged.add(key) - super().log(level, msg, *args, **kwargs) # type: ignore[arg-type] + super().log(level, msg, *args, **kwargs) # ty: ignore[invalid-argument-type] class TemplateLogger: diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index 2c27a60a..2e7a8d39 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -140,7 +140,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: handlers = Handlers( default=self.config.default_handler, handlers_config=self.config.handlers, - theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), + theme=config.theme.name or os.path.dirname(config.theme.dirs[0]), # noqa: PTH120 custom_templates=self.config.custom_templates, mdx=config.markdown_extensions, mdx_config=config.mdx_configs, @@ -156,7 +156,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: autorefs: AutorefsPlugin try: # If autorefs plugin is explicitly enabled, just use it. - autorefs = config.plugins["autorefs"] # type: ignore[assignment] + autorefs = config.plugins["autorefs"] # ty: ignore[invalid-assignment] _logger.debug("Picked up existing autorefs instance %r", autorefs) except KeyError: # Otherwise, add a limited instance of it that acts only on what's added through `register_anchor`. @@ -167,7 +167,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: _logger.debug("Added a subdued autorefs instance %r", autorefs) mkdocstrings_extension = MkdocstringsExtension(handlers, autorefs) - config.markdown_extensions.append(mkdocstrings_extension) # type: ignore[arg-type] + config.markdown_extensions.append(mkdocstrings_extension) # ty: ignore[invalid-argument-type] config.extra_css.insert(0, self.css_filename) # So that it has lower priority than user files. @@ -199,7 +199,7 @@ def plugin_enabled(self) -> bool: @event_priority(50) # Early, before autorefs' starts applying cross-refs and collecting backlinks. def _on_env_load_inventories(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 if self.plugin_enabled and self._handlers: - register = config.plugins["autorefs"].register_url # type: ignore[attr-defined] + register = config.plugins["autorefs"].register_url # ty: ignore[possibly-missing-attribute] for identifier, url in self._handlers._yield_inventory_items(): register(identifier, url) @@ -207,14 +207,14 @@ def _on_env_load_inventories(self, env: Environment, config: MkDocsConfig, *args def _on_env_add_css(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 if self.plugin_enabled and self._handlers: css_content = "\n".join(handler.extra_css for handler in self.handlers.seen_handlers) - write_file(css_content.encode("utf-8"), os.path.join(config.site_dir, self.css_filename)) + write_file(css_content.encode("utf-8"), os.path.join(config.site_dir, self.css_filename)) # noqa: PTH118 @event_priority(-20) # Late, not important. def _on_env_write_inventory(self, env: Environment, config: MkDocsConfig, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 if self.plugin_enabled and self._handlers and self.inventory_enabled: _logger.debug("Creating inventory file objects.inv") inv_contents = self.handlers.inventory.format_sphinx() - write_file(inv_contents, os.path.join(config.site_dir, "objects.inv")) + write_file(inv_contents, os.path.join(config.site_dir, "objects.inv")) # noqa: PTH118 @event_priority(-100) # Last, after autorefs has finished applying cross-refs and collecting backlinks. def _on_env_apply_backlinks(self, env: Environment, /, *, config: MkDocsConfig, files: Files) -> Environment: # noqa: ARG002 @@ -226,12 +226,12 @@ def repl(match: Match) -> str: # The handler doesn't implement backlinks, # return early to avoid computing them. - if handler.render_backlinks.__func__ is BaseHandler.render_backlinks: # type: ignore[attr-defined] + if handler.render_backlinks.__func__ is BaseHandler.render_backlinks: return "" identifier = match.group(1) aliases = handler.get_aliases(identifier) - backlinks = self._autorefs.get_backlinks(identifier, *aliases, from_url=file.page.url) # type: ignore[union-attr] + backlinks = self._autorefs.get_backlinks(identifier, *aliases, from_url=file.page.url) # No backlinks, avoid calling the handler's method. if not backlinks: @@ -240,7 +240,7 @@ def repl(match: Match) -> str: if "locale" in signature(handler.render_backlinks).parameters: render_backlinks = partial(handler.render_backlinks, locale=self.handlers._locale) else: - render_backlinks = handler.render_backlinks # type: ignore[assignment] + render_backlinks = handler.render_backlinks return render_backlinks(backlinks) diff --git a/tests/conftest.py b/tests/conftest.py index 8a132e29..0c9a89ad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,7 +23,7 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera """Yield a MkDocs configuration object.""" conf = MkDocsConfig() while hasattr(request, "_parent_request") and hasattr(request._parent_request, "_parent_request"): - request = request._parent_request + request = request._parent_request # ty: ignore[invalid-assignment] conf_dict = { "site_name": "foo", @@ -33,7 +33,7 @@ def fixture_mkdocs_conf(request: pytest.FixtureRequest, tmp_path: Path) -> Itera **getattr(request, "param", {}), } # Re-create it manually as a workaround for https://github.com/mkdocs/mkdocs/issues/2289 - mdx_configs: dict[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) + mdx_configs: dict[str, Any] = dict(ChainMap(*conf_dict.get("markdown_extensions", []))) # ty: ignore[invalid-argument-type] conf.load_dict(conf_dict) assert conf.validate() == ([], []) diff --git a/tests/test_extension.py b/tests/test_extension.py index c283f9c0..6ba8adc1 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -99,7 +99,7 @@ def test_no_double_toc(ext_markdown: Markdown, expect_permalink: str) -> None: ) assert output.count(expect_permalink) == 5 assert 'id="tests.fixtures.headings--foo"' in output - assert ext_markdown.toc_tokens == [ # type: ignore[attr-defined] # the member gets populated only with 'toc' extension + assert ext_markdown.toc_tokens == [ # ty: ignore[unresolved-attribute] { "level": 1, "id": "aa", @@ -154,10 +154,10 @@ def test_use_custom_handler(ext_markdown: Markdown) -> None: def test_register_every_identifier_alias(plugin: MkdocstringsPlugin, ext_markdown: Markdown) -> None: """Assert that we don't preemptively register all identifiers of a rendered object.""" - handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] + handler = plugin._handlers.get_handler("python") # ty: ignore[possibly-missing-attribute] ids = ("id1", "id2", "id3") - handler.get_aliases = lambda _: ids # type: ignore[method-assign] - autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs # type: ignore[attr-defined] + handler.get_aliases = lambda _: ids # ty: ignore[invalid-assignment] + autorefs = ext_markdown.parser.blockprocessors["mkdocstrings"]._autorefs class Page: url = "foo" diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 30bdbdfc..fd9eb714 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -62,7 +62,7 @@ def test_extended_templates(tmp_path: Path, plugin: MkdocstringsPlugin) -> None: tmp_path: Temporary folder. plugin: Instance of our plugin. """ - handler = plugin._handlers.get_handler("python") # type: ignore[union-attr] + handler = plugin._handlers.get_handler("python") # ty: ignore[possibly-missing-attribute] # monkeypatch Jinja env search path search_paths = [ @@ -71,7 +71,7 @@ def test_extended_templates(tmp_path: Path, plugin: MkdocstringsPlugin) -> None: extended_theme := tmp_path / "extended_theme", extended_fallback_theme := tmp_path / "extended_fallback_theme", ] - handler.env.loader.searchpath = search_paths # type: ignore[union-attr] + handler.env.loader.searchpath = search_paths # ty: ignore[invalid-assignment] # assert "new" template is not found with pytest.raises(expected_exception=TemplateNotFound): @@ -117,7 +117,7 @@ def test_nested_autodoc(ext_markdown: Markdown) -> None: ) assert 'id="tests.fixtures.nesting.Class"' in output assert 'id="tests.fixtures.nesting.Class.method"' in output - assert ext_markdown.toc_tokens == [ # type: ignore[attr-defined] + assert ext_markdown.toc_tokens == [ # ty: ignore[unresolved-attribute] { "level": 1, "id": "tests.fixtures.nesting.Class", diff --git a/tests/test_inventory.py b/tests/test_inventory.py index ab61e599..858ac340 100644 --- a/tests/test_inventory.py +++ b/tests/test_inventory.py @@ -4,6 +4,7 @@ from io import BytesIO from os.path import join +from pathlib import Path import pytest from mkdocs.commands.build import build @@ -48,7 +49,7 @@ def test_sphinx_load_mkdocstrings_inventory_file() -> None: mkdocs_config["plugins"].run_event("shutdown") own_inv = mkdocs_config["plugins"]["mkdocstrings"].handlers.inventory - with open("site/objects.inv", "rb") as fp: + with Path("site/objects.inv").open("rb") as fp: sphinx_inv = sphinx.InventoryFile.load(fp, "", join) sphinx_inv_length = sum(len(sphinx_inv[key]) for key in sphinx_inv) From 65b27ec8d1d671eddf021e48b0114cc3f8aca14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 5 Feb 2026 19:02:18 +0100 Subject: [PATCH 44/51] fix: Forward extension instances directly passed from Zensical --- src/mkdocstrings/_internal/extension.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 8ba43aba..4439d400 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -387,12 +387,14 @@ def extendMarkdown(self, md: Markdown) -> None: # noqa: N802 (casing: parent me } -def _split_configs(markdown_extensions: list[str | dict]) -> tuple[list[str | Extension], dict[str, Any]]: +def _split_configs( + markdown_extensions: list[str | dict[str, dict[str, Any]] | Extension], +) -> tuple[list[str | Extension], dict[str, dict[str, Any]]]: # Split markdown extensions and their configs from mkdocs.yml mdx: list[str | Extension] = [] - mdx_config: dict[str, Any] = {} + mdx_config: dict[str, dict[str, Any]] = {} for item in markdown_extensions: - if isinstance(item, str): + if isinstance(item, (str, Extension)): mdx.append(item) elif isinstance(item, dict): for key, value in item.items(): @@ -418,7 +420,7 @@ def makeExtension( # noqa: N802 inventory_version: str | None = None, handlers: dict[str, dict] | None = None, custom_templates: str | None = None, - markdown_extensions: list[str | dict] | None = None, + markdown_extensions: list[str | dict | Extension] | None = None, locale: str | None = None, config_file_path: str | None = None, ) -> MkdocstringsExtension: From 8bdff16af916b7285eae5a07eb085c21754be3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 7 Feb 2026 15:31:28 +0100 Subject: [PATCH 45/51] chore: Prepare release 1.0.3 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1c96386..53065d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.0.3](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.3) - 2026-02-07 + +[Compare with 1.0.2](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.2...1.0.3) + +### Bug Fixes + +- Forward extension instances directly passed from Zensical ([65b27ec](https://github.com/mkdocstrings/mkdocstrings/commit/65b27ec8d1d671eddf021e48b0114cc3f8aca14a) by Timothée Mazzucotelli). +- Propagate Zensical's `zrelpath` processor ([dbf263d](https://github.com/mkdocstrings/mkdocstrings/commit/dbf263dfdd2fdd769d66fa62bdd388e05988bc78) by Timothée Mazzucotelli). + ## [1.0.2](https://github.com/mkdocstrings/mkdocstrings/releases/tag/1.0.2) - 2026-01-24 [Compare with 1.0.1](https://github.com/mkdocstrings/mkdocstrings/compare/1.0.1...1.0.2) From e500a2b416656ede76aac316304415e428dd0aa1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Feb 2026 01:27:52 +0000 Subject: [PATCH 46/51] chore: Update sponsors section in README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e28c653b..3bfb8316 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,6 @@ See the [Usage](https://mkdocstrings.github.io/usage) section of the docs for mo
Silver sponsors

FastAPI
-Pydantic

Bronze sponsors

From a0c47b9992416cf02b8dfc7a76a6c5503e98cd9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 11 Mar 2026 12:11:40 +0100 Subject: [PATCH 47/51] docs: Fix broken link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3bfb8316..052690ba 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Come have a chat or ask questions on our [Gitter channel](https://gitter.im/mkdo - [**Language-agnostic:**](https://mkdocstrings.github.io/handlers/overview/) just like *MkDocs*, *mkdocstrings* is written in Python but is language-agnostic. It means you can use it with any programming language, as long as there is a - [**handler**](https://mkdocstrings.github.io/reference/handlers/base/) for it. + [**handler**](https://mkdocstrings.github.io/reference/api/#mkdocstrings.BaseHandler) for it. We currently have [handlers](https://mkdocstrings.github.io/handlers/overview/) for the [C](https://mkdocstrings.github.io/c/), [Crystal](https://mkdocstrings.github.io/crystal/), From 3d1969a279ea396792c682810d029503e48d8fcd Mon Sep 17 00:00:00 2001 From: Simon Lloyd Date: Wed, 15 Apr 2026 10:06:10 +0100 Subject: [PATCH 48/51] fix: Add timeout when downloading inventories (10 seconds) Issue-819: https://github.com/mkdocstrings/mkdocstrings/issues/819 --- src/mkdocstrings/_internal/download.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mkdocstrings/_internal/download.py b/src/mkdocstrings/_internal/download.py index 2d8b25f1..52bf42f5 100644 --- a/src/mkdocstrings/_internal/download.py +++ b/src/mkdocstrings/_internal/download.py @@ -19,6 +19,9 @@ # Regex pattern for an environment variable in the form ${ENV_VAR}. _ENV_VAR_PATTERN = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}") +# Timeout in seconds for downloading. +_TIMEOUT = 10 + def _download_url_with_gz(url: str) -> bytes: url, auth_header = _extract_auth_from_url(url) @@ -27,7 +30,7 @@ def _download_url_with_gz(url: str) -> bytes: url, headers={"Accept-Encoding": "gzip", "User-Agent": "mkdocstrings/0.15.0", **auth_header}, ) - with urllib.request.urlopen(req) as resp: # noqa: S310 + with urllib.request.urlopen(req, timeout=_TIMEOUT) as resp: # noqa: S310 content: BinaryIO = resp if "gzip" in resp.headers.get("content-encoding", ""): content = gzip.GzipFile(fileobj=resp) # ty: ignore[invalid-assignment] From 5f82a5822c50dc928ee4a133c1445543d3afa393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 15 Apr 2026 11:12:01 +0200 Subject: [PATCH 49/51] chore: Template upgrade --- .copier-answers.yml | 2 +- CONTRIBUTING.md | 5 +- LICENSE | 2 +- README.md | 2 +- config/ruff.toml | 3 +- config/ty.toml | 4 + config/vscode/launch.json | 3 +- config/vscode/settings.json | 1 + docs/.overrides/partials/comments.html | 2 +- docs/.overrides/partials/path-item.html | 22 --- docs/css/apidocs.css | 21 +++ docs/css/material.css | 4 - docs/css/mkdocstrings.css | 82 --------- duties.py | 34 ++-- mkdocs.yml | 209 ----------------------- pyproject.toml | 18 +- scripts/gen_credits.py | 6 +- tests/test_api.py | 4 +- zensical.toml | 214 ++++++++++++++++++++++++ 19 files changed, 283 insertions(+), 355 deletions(-) delete mode 100644 docs/.overrides/partials/path-item.html create mode 100644 docs/css/apidocs.css delete mode 100644 docs/css/material.css delete mode 100644 docs/css/mkdocstrings.css delete mode 100644 mkdocs.yml create mode 100644 zensical.toml diff --git a/.copier-answers.yml b/.copier-answers.yml index 3c48bac1..b709d322 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.11.6 +_commit: 1.11.15 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78db50f2..82526a81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,8 @@ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. +**Please always create an issue before working on a new feature or a bug fix, so that we can discuss the implementation and make sure that your work will be merged.** + ## Environment setup Nothing easier! @@ -38,8 +40,7 @@ Run `make help` to see all the available actions! ## Tasks -The entry-point to run commands and tasks is the `make` Python script, located in the `scripts` directory. Try running `make` to show the available commands and tasks. The *commands* do not need the Python dependencies to be installed, -while the *tasks* do. The cross-platform tasks are written in Python, thanks to [duty](https://github.com/pawamoy/duty). +The entry-point to run commands and tasks is the `make` Python script, located in the `scripts` directory. Try running `make` to show the available commands and tasks. The *commands* do not need the Python dependencies to be installed, while the *tasks* do. The cross-platform tasks are written in Python, thanks to [duty](https://github.com/pawamoy/duty). If you work in VSCode, we provide [an action to configure VSCode](https://pawamoy.github.io/copier-uv/work/#vscode-setup) for the project. diff --git a/LICENSE b/LICENSE index aa2449ff..6270ba3e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ ISC License -Copyright (c) 2019, Timothée Mazzucotelli +Copyright (c) 2019, Timothée Mazzucotelli and contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/README.md b/README.md index 052690ba..8ddfc16b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mkdocstrings [![ci](https://github.com/mkdocstrings/mkdocstrings/workflows/ci/badge.svg)](https://github.com/mkdocstrings/mkdocstrings/actions?query=workflow%3Aci) -[![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://mkdocstrings.github.io/) +[![documentation](https://img.shields.io/badge/docs-zensical-FF9100.svg?style=flat)](https://mkdocstrings.github.io/mkdocstrings/) [![pypi version](https://img.shields.io/pypi/v/mkdocstrings.svg)](https://pypi.org/project/mkdocstrings/) [![conda version](https://img.shields.io/conda/vn/conda-forge/mkdocstrings)](https://anaconda.org/conda-forge/mkdocstrings) [![gitter](https://img.shields.io/badge/matrix-chat-4DB798.svg?style=flat)](https://app.gitter.im/#/room/#mkdocstrings:gitter.im) diff --git a/config/ruff.toml b/config/ruff.toml index 95d92083..6cb05666 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -1,5 +1,6 @@ -target-version = "py39" +target-version = "py310" line-length = 120 +output-format = "concise" [lint] exclude = [ diff --git a/config/ty.toml b/config/ty.toml index 545856a3..97724fa8 100644 --- a/config/ty.toml +++ b/config/ty.toml @@ -1,2 +1,6 @@ [src] exclude = ["tests/fixtures"] + +[terminal] +error-on-warning = true +output-format = "concise" diff --git a/config/vscode/launch.json b/config/vscode/launch.json index 5f3742be..6571bd99 100644 --- a/config/vscode/launch.json +++ b/config/vscode/launch.json @@ -23,11 +23,10 @@ "name": "docs", "type": "debugpy", "request": "launch", - "module": "mkdocs", + "module": "zensical", "justMyCode": false, "args": [ "serve", - "-v" ] }, { diff --git a/config/vscode/settings.json b/config/vscode/settings.json index 87ecd639..51587578 100644 --- a/config/vscode/settings.json +++ b/config/vscode/settings.json @@ -16,6 +16,7 @@ "ruff.lint.args": [ "--config=config/ruff.toml" ], + "ty.configurationFile": "config/ty.toml", "yaml.schemas": { "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml" }, diff --git a/docs/.overrides/partials/comments.html b/docs/.overrides/partials/comments.html index cc341ba9..793b075c 100644 --- a/docs/.overrides/partials/comments.html +++ b/docs/.overrides/partials/comments.html @@ -1,5 +1,5 @@ - +