From 4a5ee10c65de28b7921a56ef2c222d2f3417edaa Mon Sep 17 00:00:00 2001 From: Pete Stenger Date: Mon, 17 Feb 2025 08:43:04 -0600 Subject: [PATCH 01/53] feat: Add option to show/hide overloads PR-250: https://github.com/mkdocstrings/python/pull/250 --- docs/usage/configuration/signatures.md | 49 +++++++++++++++++++ src/mkdocstrings_handlers/python/config.py | 8 +++ .../templates/material/_base/class.html.jinja | 2 +- .../material/_base/function.html.jinja | 2 +- 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/docs/usage/configuration/signatures.md b/docs/usage/configuration/signatures.md index c97cb5a6..98c865e5 100644 --- a/docs/usage/configuration/signatures.md +++ b/docs/usage/configuration/signatures.md @@ -433,6 +433,55 @@ function(param1, param2=None) //// /// +[](){#option-show_overloads} +## `show_overloads` + +Whether to render function / method overloads. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + show_overloads: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + show_overloads: false +``` + +/// admonition | Preview + type: preview +//// tab | With overloads +

function

+ + +```python +@overload +function(param1: int): ... + +@overload +function(param1: str): ... + +function(param1: str | int) +``` +Function docstring. + +//// +//// tab | Without overloads +

function

+ +```python +function(param1: str | int) +``` +Function docstring. + +//// +/// + [](){#option-signature_crossrefs} ## `signature_crossrefs` diff --git a/src/mkdocstrings_handlers/python/config.py b/src/mkdocstrings_handlers/python/config.py index 3bab3920..6607d01c 100644 --- a/src/mkdocstrings_handlers/python/config.py +++ b/src/mkdocstrings_handlers/python/config.py @@ -568,6 +568,14 @@ class PythonInputOptions: ), ] = False + show_overloads: Annotated[ + bool, + Field( + group="signatures", + description="Show the overloads of a function or method.", + ), + ] = True + separate_signature: Annotated[ bool, Field( diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja index aefa98d1..e8f89fa3 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja @@ -82,7 +82,7 @@ Context: {% if config.merge_init_into_class %} {% if "__init__" in all_members %} {% with function = all_members["__init__"] %} - {% if function.overloads %} + {% if function.overloads and config.show_overloads %}
{% for overload in function.overloads %} {% filter format_signature(overload, config.line_length, annotations=True, crossrefs=config.signature_crossrefs) %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja index 5e803ffb..b475cf1b 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja @@ -80,7 +80,7 @@ Context: This block renders the signature for the function, as well as its overloaded signatures if any. -#} - {% if function.overloads %} + {% if function.overloads and config.show_overloads %}
{% for overload in function.overloads %} {% filter format_signature(overload, config.line_length, annotations=True, crossrefs=config.signature_crossrefs) %} From e3a303c83fbf765c6f380b3628ddb14841da3aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 17 Feb 2025 15:45:41 +0100 Subject: [PATCH 02/53] chore: Prepare release 1.16.0 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e8f8fe8..05e4e3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,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.16.0](https://github.com/mkdocstrings/python/releases/tag/1.16.0) - 2025-02-17 + +[Compare with 1.15.1](https://github.com/mkdocstrings/python/compare/1.15.1...1.16.0) + +### Features + +- Add option to show/hide overloads ([4a5ee10](https://github.com/mkdocstrings/python/commit/4a5ee10c65de28b7921a56ef2c222d2f3417edaa) by Pete Stenger). [PR-250](https://github.com/mkdocstrings/python/pull/250) + ## [1.15.1](https://github.com/mkdocstrings/python/releases/tag/1.15.1) - 2025-02-17 [Compare with 1.15.0](https://github.com/mkdocstrings/python/compare/1.15.0...1.15.1) From 0f497d185ba1860c61555803bfc4b311a410bd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 18 Feb 2025 21:46:54 +0100 Subject: [PATCH 03/53] fix: Give precedence to user-provided paths when they are already listed in `sys.path` Issue-248: https://github.com/mkdocstrings/python/discussions/248 --- src/mkdocstrings_handlers/python/handler.py | 8 +++++--- tests/test_handler.py | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 30cd2058..0051be0f 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -128,9 +128,11 @@ def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None: # If it's not absolute, make path relative to the config file path, then make it absolute. if not os.path.isabs(path): path = os.path.abspath(base_dir / path) # noqa: PLW2901 - # Don't add duplicates. - if path not in search_paths: - search_paths.insert(0, path) + # Remove pre-listed paths. + if path in search_paths: + search_paths.remove(path) + # Give precedence to user-provided paths. + search_paths.insert(0, path) self._paths = search_paths self._modules_collection: ModulesCollection = ModulesCollection() diff --git a/tests/test_handler.py b/tests/test_handler.py index 365b5f23..7cf8dc54 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -3,19 +3,19 @@ from __future__ import annotations import os +import sys from glob import glob +from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING import pytest from griffe import DocstringSectionExamples, DocstringSectionKind, temporary_visited_module -from mkdocstrings_handlers.python.config import PythonOptions +from mkdocstrings_handlers.python.config import PythonConfig, PythonOptions from mkdocstrings_handlers.python.handler import CollectionError, PythonHandler if TYPE_CHECKING: - from pathlib import Path - from mkdocstrings.plugin import MkdocstringsPlugin @@ -167,3 +167,15 @@ def function(self): module["Class.function"].lineno = None module["attribute"].lineno = None assert handler.render(module, PythonOptions(show_source=True)) + + +def test_give_precedence_to_user_paths() -> None: + """Assert user paths take precedence over default paths.""" + last_sys_path = sys.path[-1] + handler = PythonHandler( + base_dir=Path("."), + config=PythonConfig.from_data(paths=[last_sys_path]), + mdx=[], + mdx_config={}, + ) + assert handler._paths[0] == last_sys_path From 15a7009f854e669ffdbe4b6822fc0b030d88fd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 18 Feb 2025 21:47:49 +0100 Subject: [PATCH 04/53] chore: Prepare release 1.16.1 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05e4e3ed..127f5295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,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.16.1](https://github.com/mkdocstrings/python/releases/tag/1.16.1) - 2025-02-18 + +[Compare with 1.16.0](https://github.com/mkdocstrings/python/compare/1.16.0...1.16.1) + +### Bug Fixes + +- Give precedence to user-provided paths when they are already listed in `sys.path` ([0f497d1](https://github.com/mkdocstrings/python/commit/0f497d185ba1860c61555803bfc4b311a410bd39) by Timothée Mazzucotelli). [Issue-248](https://github.com/mkdocstrings/python/discussions/248) + ## [1.16.0](https://github.com/mkdocstrings/python/releases/tag/1.16.0) - 2025-02-17 [Compare with 1.15.1](https://github.com/mkdocstrings/python/compare/1.15.1...1.16.0) From ea1ab498be836c94eb695ace05c41357b12f2c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 24 Feb 2025 17:16:44 +0100 Subject: [PATCH 05/53] build: Depend on mkdocs-autorefs >= 1.4 and mkdocstrings >= 0.28.2 --- pyproject.toml | 4 ++-- src/mkdocstrings_handlers/python/rendering.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b4a453e4..e69145f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,8 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "mkdocstrings>=0.28", - "mkdocs-autorefs>=1.2", + "mkdocstrings>=0.28.2", + "mkdocs-autorefs>=1.4", "griffe>=0.49", "typing-extensions>=4.0; python_version < '3.11'", ] diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index b5c893c0..d284567a 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -28,7 +28,7 @@ ) from jinja2 import TemplateNotFound, pass_context, pass_environment from markupsafe import Markup -from mkdocs_autorefs.references import AutorefsHookInterface +from mkdocs_autorefs import AutorefsHookInterface from mkdocstrings.loggers import get_logger if TYPE_CHECKING: From cfa9848603f6c021f33af12f334267f7f17de2da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 24 Feb 2025 17:17:59 +0100 Subject: [PATCH 06/53] chore: Prepare release 1.16.2 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 127f5295..66bb2047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,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.16.2](https://github.com/mkdocstrings/python/releases/tag/1.16.2) - 2025-02-24 + +[Compare with 1.16.1](https://github.com/mkdocstrings/python/compare/1.16.1...1.16.2) + +### Build + +- Depend on mkdocs-autorefs >= 1.4 and mkdocstrings >= 0.28.2 ([ea1ab49](https://github.com/mkdocstrings/python/commit/ea1ab498be836c94eb695ace05c41357b12f2c95) by Timothée Mazzucotelli). + ## [1.16.1](https://github.com/mkdocstrings/python/releases/tag/1.16.1) - 2025-02-18 [Compare with 1.16.0](https://github.com/mkdocstrings/python/compare/1.16.0...1.16.1) From a657d07499eb82d22337c169aa86b1cdd85543fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Fri, 28 Feb 2025 18:24:13 +0100 Subject: [PATCH 07/53] fix: De-duplicate summary sections Give precedence to manually written sections. Issue-134: https://github.com/mkdocstrings/python/issues/134 --- .../_base/summary/attributes.html.jinja | 26 ++--- .../material/_base/summary/classes.html.jinja | 26 ++--- .../_base/summary/functions.html.jinja | 26 ++--- .../material/_base/summary/modules.html.jinja | 26 ++--- tests/test_handler.py | 101 +++++++++++++++++- 5 files changed, 156 insertions(+), 49 deletions(-) diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja index 8bc8cb60..3675eda7 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja @@ -7,15 +7,17 @@ -#} {% endblock logs %} -{% with section = obj.attributes - |filter_objects( - filters=config.filters, - members_list=members_list, - inherited_members=config.inherited_members, - keep_no_docstrings=config.show_if_no_docstring, - ) - |order_members(config.members_order, members_list) - |as_attributes_section(check_public=not members_list) - %} - {% if section %}{% include "docstring/attributes"|get_template with context %}{% endif %} -{% endwith %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "attributes") %} + {% with section = obj.attributes + |filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + ) + |order_members(config.members_order, members_list) + |as_attributes_section(check_public=not members_list) + %} + {% if section %}{% include "docstring/attributes"|get_template with context %}{% endif %} + {% endwith %} +{% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja index 1b1ef8f3..3db63f3b 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja @@ -7,15 +7,17 @@ -#} {% endblock logs %} -{% with section = obj.classes - |filter_objects( - filters=config.filters, - members_list=members_list, - inherited_members=config.inherited_members, - keep_no_docstrings=config.show_if_no_docstring, - ) - |order_members(config.members_order, members_list) - |as_classes_section(check_public=not members_list) - %} - {% if section %}{% include "docstring/classes"|get_template with context %}{% endif %} -{% endwith %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "classes") %} + {% with section = obj.classes + |filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + ) + |order_members(config.members_order, members_list) + |as_classes_section(check_public=not members_list) + %} + {% if section %}{% include "docstring/classes"|get_template with context %}{% endif %} + {% endwith %} +{% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja index f03dfba2..60369401 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja @@ -7,15 +7,17 @@ -#} {% endblock logs %} -{% with section = obj.functions - |filter_objects( - filters=config.filters, - members_list=members_list, - inherited_members=config.inherited_members, - keep_no_docstrings=config.show_if_no_docstring, - ) - |order_members(config.members_order, members_list) - |as_functions_section(check_public=not members_list) - %} - {% if section %}{% include "docstring/functions"|get_template with context %}{% endif %} -{% endwith %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "functions") %} + {% with section = obj.functions + |filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + ) + |order_members(config.members_order, members_list) + |as_functions_section(check_public=not members_list) + %} + {% if section %}{% include "docstring/functions"|get_template with context %}{% endif %} + {% endwith %} +{% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja index 606711c5..dd14ee9f 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja @@ -7,15 +7,17 @@ -#} {% endblock logs %} -{% with section = obj.modules - |filter_objects( - filters=config.filters, - members_list=members_list, - inherited_members=config.inherited_members, - keep_no_docstrings=config.show_if_no_docstring, - ) - |order_members("alphabetical", members_list) - |as_modules_section(check_public=not members_list) - %} - {% if section %}{% include "docstring/modules"|get_template with context %}{% endif %} -{% endwith %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "modules") %} + {% with section = obj.modules + |filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + ) + |order_members("alphabetical", members_list) + |as_modules_section(check_public=not members_list) + %} + {% if section %}{% include "docstring/modules"|get_template with context %}{% endif %} + {% endwith %} +{% endif %} diff --git a/tests/test_handler.py b/tests/test_handler.py index 7cf8dc54..356a6ed1 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING import pytest -from griffe import DocstringSectionExamples, DocstringSectionKind, temporary_visited_module +from griffe import Docstring, DocstringSectionExamples, DocstringSectionKind, Module, temporary_visited_module from mkdocstrings_handlers.python.config import PythonConfig, PythonOptions from mkdocstrings_handlers.python.handler import CollectionError, PythonHandler @@ -179,3 +179,102 @@ def test_give_precedence_to_user_paths() -> None: mdx_config={}, ) assert handler._paths[0] == last_sys_path + + +@pytest.mark.parametrize( + ("section", "code"), + [ + ( + "Attributes", + """ + class A: + '''Summary. + + Attributes: + x: X. + y: Y. + ''' + x: int = 0 + '''X.''' + y: int = 0 + '''Y.''' + """, + ), + ( + "Methods", + """ + class A: + '''Summary. + + Methods: + x: X. + y: Y. + ''' + def x(self): ... + '''X.''' + def y(self): ... + '''Y.''' + """, + ), + ( + "Functions", + """ + '''Summary. + + Functions: + x: X. + y: Y. + ''' + def x(): ... + '''X.''' + def y(): ... + '''Y.''' + """, + ), + ( + "Classes", + """ + '''Summary. + + Classes: + A: A. + B: B. + ''' + class A: ... + '''A.''' + class B: ... + '''B.''' + """, + ), + ( + "Modules", + """ + '''Summary. + + Modules: + a: A. + b: B. + ''' + """, + ), + ], +) +def test_deduplicate_summary_sections(handler: PythonHandler, section: str, code: str) -> None: + """Assert summary sections are deduplicated.""" + summary_section = section.lower() + summary_section = "functions" if summary_section == "methods" else summary_section + with temporary_visited_module(code, docstring_parser="google") as module: # type: ignore[arg-type] + if summary_section == "modules": + module.set_member("a", Module("A", docstring=Docstring("A."))) + module.set_member("b", Module("B", docstring=Docstring("B."))) + html = handler.render( + module, + handler.get_options( + { + "summary": {summary_section: True}, + "show_source": False, + "show_submodules": True, + }, + ), + ) + assert html.count(f"{section}:") == 1 From 110b9f773e064b65385b28fd6beb44e28be2318f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 8 Mar 2025 22:54:35 +0100 Subject: [PATCH 08/53] chore: Stop testing on Python 3.14 locally --- scripts/make.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/make.py b/scripts/make.py index 3d427296..a3b9e751 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -16,7 +16,7 @@ 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_VERSIONS = os.getenv("PYTHON_VERSIONS", "3.9 3.10 3.11 3.12 3.13").split() def shell(cmd: str, *, capture_output: bool = False, **kwargs: Any) -> str | None: From 9fa4f1636af240bb695661b7172f052cb11e0ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 8 Mar 2025 22:54:51 +0100 Subject: [PATCH 09/53] build: Depend on mkdocstrings 0.28.3 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e69145f9..cb429367 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "mkdocstrings>=0.28.2", + "mkdocstrings>=0.28.3", "mkdocs-autorefs>=1.4", "griffe>=0.49", "typing-extensions>=4.0; python_version < '3.11'", From da2ba13b1367ce107416d08f382fb9f2384c015c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 8 Mar 2025 22:55:37 +0100 Subject: [PATCH 10/53] refactor: Import from top-level `mkdocstrings` module --- src/mkdocstrings_handlers/python/config.py | 2 +- src/mkdocstrings_handlers/python/handler.py | 4 +--- src/mkdocstrings_handlers/python/rendering.py | 4 ++-- tests/conftest.py | 2 +- tests/helpers.py | 2 +- tests/test_handler.py | 2 +- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/mkdocstrings_handlers/python/config.py b/src/mkdocstrings_handlers/python/config.py index 6607d01c..a1ecdcd8 100644 --- a/src/mkdocstrings_handlers/python/config.py +++ b/src/mkdocstrings_handlers/python/config.py @@ -7,7 +7,7 @@ from dataclasses import field, fields from typing import TYPE_CHECKING, Annotated, Any, Literal -from mkdocstrings.loggers import get_logger +from mkdocstrings import get_logger # YORE: EOL 3.10: Replace block with line 2. if sys.version_info >= (3, 11): diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 0051be0f..d9db6669 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -22,9 +22,7 @@ patch_loggers, ) from mkdocs.exceptions import PluginError -from mkdocstrings.handlers.base import BaseHandler, CollectionError, CollectorItem, HandlerOptions -from mkdocstrings.inventory import Inventory -from mkdocstrings.loggers import get_logger +from mkdocstrings import BaseHandler, CollectionError, CollectorItem, HandlerOptions, Inventory, get_logger from mkdocstrings_handlers.python import rendering from mkdocstrings_handlers.python.config import PythonConfig, PythonOptions diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index d284567a..4b8a8ee4 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -29,7 +29,7 @@ from jinja2 import TemplateNotFound, pass_context, pass_environment from markupsafe import Markup from mkdocs_autorefs import AutorefsHookInterface -from mkdocstrings.loggers import get_logger +from mkdocstrings import get_logger if TYPE_CHECKING: from collections.abc import Iterator, Sequence @@ -37,7 +37,7 @@ from griffe import Attribute, Class, Function, Module from jinja2 import Environment, Template from jinja2.runtime import Context - from mkdocstrings.handlers.base import CollectorItem + from mkdocstrings import CollectorItem logger = get_logger(__name__) diff --git a/tests/conftest.py b/tests/conftest.py index 1c53cba4..5b2dd33f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,7 +15,7 @@ from markdown.core import Markdown from mkdocs.config.defaults import MkDocsConfig - from mkdocstrings.plugin import MkdocstringsPlugin + from mkdocstrings import MkdocstringsPlugin from mkdocstrings_handlers.python.handler import PythonHandler diff --git a/tests/helpers.py b/tests/helpers.py index 37c127e4..b0dc6ad7 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -14,7 +14,7 @@ from pathlib import Path import pytest - from mkdocstrings.plugin import MkdocstringsPlugin + from mkdocstrings import MkdocstringsPlugin from mkdocstrings_handlers.python.handler import PythonHandler diff --git a/tests/test_handler.py b/tests/test_handler.py index 356a6ed1..4d8b4f3d 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -16,7 +16,7 @@ from mkdocstrings_handlers.python.handler import CollectionError, PythonHandler if TYPE_CHECKING: - from mkdocstrings.plugin import MkdocstringsPlugin + from mkdocstrings import MkdocstringsPlugin def test_collect_missing_module(handler: PythonHandler) -> None: From b839ef025fb159c357064df8d5956ed6118db2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 8 Mar 2025 22:55:49 +0100 Subject: [PATCH 11/53] tests: Fix type checking import --- tests/test_themes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_themes.py b/tests/test_themes.py index a7b44795..ca7125d3 100644 --- a/tests/test_themes.py +++ b/tests/test_themes.py @@ -7,7 +7,7 @@ import pytest if TYPE_CHECKING: - from mkdocstrings.handlers.python import PythonHandler + from mkdocstrings_handlers.python.handler import PythonHandler @pytest.mark.parametrize( From 7dd67f9ed7749bf25a0621c3900cc3e5f8c0de4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Sat, 8 Mar 2025 22:58:31 +0100 Subject: [PATCH 12/53] chore: Prepare release 1.16.3 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66bb2047..1cd4ce23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,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). +## [1.16.3](https://github.com/mkdocstrings/python/releases/tag/1.16.3) - 2025-03-08 + +[Compare with 1.16.2](https://github.com/mkdocstrings/python/compare/1.16.2...1.16.3) + +### Build + +- Depend on mkdocstrings 0.28.3 ([9fa4f16](https://github.com/mkdocstrings/python/commit/9fa4f1636af240bb695661b7172f052cb11e0ec9) by Timothée Mazzucotelli). + +### Bug Fixes + +- De-duplicate summary sections ([a657d07](https://github.com/mkdocstrings/python/commit/a657d07499eb82d22337c169aa86b1cdd85543fa) by Timothée Mazzucotelli). [Issue-134](https://github.com/mkdocstrings/python/issues/134) + +### Code Refactoring + +- Import from top-level `mkdocstrings` module ([da2ba13](https://github.com/mkdocstrings/python/commit/da2ba13b1367ce107416d08f382fb9f2384c015c) by Timothée Mazzucotelli). + ## [1.16.2](https://github.com/mkdocstrings/python/releases/tag/1.16.2) - 2025-02-24 [Compare with 1.16.1](https://github.com/mkdocstrings/python/compare/1.16.1...1.16.2) From dc46ac9b4cfc642decd153dceb62e9f45c5c750e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Mar 2025 13:30:53 +0100 Subject: [PATCH 13/53] fix: Fix de-duplication of summary sections --- .../templates/material/_base/summary/attributes.html.jinja | 2 +- .../python/templates/material/_base/summary/classes.html.jinja | 2 +- .../templates/material/_base/summary/functions.html.jinja | 2 +- .../python/templates/material/_base/summary/modules.html.jinja | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja index 3675eda7..cabda067 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html.jinja @@ -7,7 +7,7 @@ -#} {% endblock logs %} -{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "attributes") %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "attributes") | list %} {% with section = obj.attributes |filter_objects( filters=config.filters, diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja index 3db63f3b..7527781b 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html.jinja @@ -7,7 +7,7 @@ -#} {% endblock logs %} -{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "classes") %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "classes") | list %} {% with section = obj.classes |filter_objects( filters=config.filters, diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja index 60369401..cdf75973 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html.jinja @@ -7,7 +7,7 @@ -#} {% endblock logs %} -{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "functions") %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "functions") | list %} {% with section = obj.functions |filter_objects( filters=config.filters, diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja index dd14ee9f..b9654593 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html.jinja @@ -7,7 +7,7 @@ -#} {% endblock logs %} -{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "modules") %} +{% if not obj.docstring.parsed | selectattr("kind.value", "eq", "modules") | list %} {% with section = obj.modules |filter_objects( filters=config.filters, From a0e888ce75d4952a230a6d3945617377fc3952c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Mar 2025 13:31:13 +0100 Subject: [PATCH 14/53] chore: Prepare release 1.16.4 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cd4ce23..ff775169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,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.16.4](https://github.com/mkdocstrings/python/releases/tag/1.16.4) - 2025-03-10 + +[Compare with 1.16.3](https://github.com/mkdocstrings/python/compare/1.16.3...1.16.4) + +### Bug Fixes + +- Fix de-duplication of summary sections ([dc46ac9](https://github.com/mkdocstrings/python/commit/dc46ac9b4cfc642decd153dceb62e9f45c5c750e) by Timothée Mazzucotelli). + ## [1.16.3](https://github.com/mkdocstrings/python/releases/tag/1.16.3) - 2025-03-08 [Compare with 1.16.2](https://github.com/mkdocstrings/python/compare/1.16.2...1.16.3) From 56bf627b9483a12228b769ae4690b84733061ea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Mar 2025 18:54:36 +0100 Subject: [PATCH 15/53] refactor: Prepare backlinks support Issue-153: https://github.com/mkdocstrings/python/issues/153 PR-252: https://github.com/mkdocstrings/python/pull/252 --- docs/css/mkdocstrings.css | 45 ++++++ docs/insiders/changelog.md | 4 + docs/insiders/goals.yml | 3 + docs/usage/configuration/general.md | 32 +++++ mkdocs.yml | 1 + src/mkdocstrings_handlers/python/config.py | 8 ++ src/mkdocstrings_handlers/python/handler.py | 1 + src/mkdocstrings_handlers/python/rendering.py | 58 +++++++- .../material/_base/attribute.html.jinja | 4 + .../material/_base/backlinks.html.jinja | 17 +++ .../templates/material/_base/class.html.jinja | 10 +- .../docstring/other_parameters.html.jinja | 6 +- .../_base/docstring/parameters.html.jinja | 12 +- .../_base/docstring/raises.html.jinja | 6 +- .../_base/docstring/receives.html.jinja | 8 +- .../_base/docstring/returns.html.jinja | 8 +- .../material/_base/docstring/warns.html.jinja | 6 +- .../_base/docstring/yields.html.jinja | 8 +- .../material/_base/expression.html.jinja | 14 +- .../material/_base/function.html.jinja | 4 + .../material/_base/module.html.jinja | 4 + .../material/_base/signature.html.jinja | 10 +- .../templates/material/backlinks.html.jinja | 1 + .../python/templates/material/style.css | 40 +++++- .../docstring/other_parameters.html.jinja | 2 +- .../_base/docstring/parameters.html.jinja | 4 +- .../_base/docstring/raises.html.jinja | 2 +- .../_base/docstring/receives.html.jinja | 2 +- .../_base/docstring/returns.html.jinja | 2 +- .../_base/docstring/warns.html.jinja | 2 +- .../_base/docstring/yields.html.jinja | 2 +- tests/snapshots/__init__.py | 130 ++++++------------ ...0f473c4dfdda12d99a171e9c76098c316067.html} | 2 +- ...22af372394971808e2a5d1b3a12286f1ec76.html} | 2 +- ...ab79c66766ec6f35383f4bfcb6a8d9e2a116.html} | 2 +- ...cd1d728d1bdfef7cbfac751af212e00a8663.html} | 2 +- ...d72f9da9db3aefe969a0d78c4052e7594372.html} | 2 +- ...301963d0170c2189d5aa18bb7c7eade84ea4.html} | 6 +- ...2eafa51ba39ec28c4c970fe7ea8e2c79f9d2.html} | 2 +- ...53e19516885fb65d0bd760c12aadd021507f.html} | 2 +- ...385a9fddb3ae628124965e0c7b81932a0c63.html} | 2 +- ...b9a938f42a115dc534cb72d12e15e998e96d.html} | 2 +- ...ab4908d94cd1bf19b777e1e6bc22e8aa02a5.html} | 2 +- ...b0a09a8915eb7f0e1f7be1ce99f5d59d9514.html} | 2 +- ...aa9a3e66171d312de8d7f836c69f0bc069b0.html} | 2 +- ...fd030467c0ccfa2cbc6a68616e06c6dc6a9b.html} | 2 +- ...b1e05e428834744442f43527ebf2b8acfb35.html} | 2 +- ...a642465f5fde9a37b4b969aa01f161ef25a4.html} | 2 +- ...1b9a81682634aaf2674ffd4cceb7fc44aba6.html} | 2 +- ...ad2501370ee9d67499b74afc83e258caff8e.html} | 2 +- ...72320842cb1ae6099ac3cf558031afda6d2e.html} | 2 +- ...d601f374eab35eea68c9beb8bab8fc269aed.html} | 2 +- ...acdf81104137ce91bd6d4121827f8d989d96.html} | 2 +- ...9312158c22f5bfdc845d2da02055fe14853c.html} | 2 +- ...a5d8de367274278976092bb824e99e523ca5.html} | 2 +- ...f54cb5f3543aa321169921326288a61f556c.html} | 2 +- ...062e3d45ab9f6a8f97e79ae62d32abc5e22a.html} | 2 +- ...2f926f95c6e88775f3e2eeaa63138d99837c.html} | 2 +- ...8bacf5b5322599bcbf0544ef8e9c0a27870b.html} | 2 +- ...125f55b54f90c2440ee2d856540415e77745.html} | 2 +- ...37041719faac884123527bab9a92e3a51be5.html} | 2 +- tests/test_handler.py | 2 +- 62 files changed, 342 insertions(+), 178 deletions(-) create mode 100644 src/mkdocstrings_handlers/python/templates/material/_base/backlinks.html.jinja create mode 100644 src/mkdocstrings_handlers/python/templates/material/backlinks.html.jinja rename tests/snapshots/external/{fb65efbbfc3ef9c2a06e6f539f8a75bec4276e61254539632a1d5f8f2c6c3452.html => 027ef7afeffc56219a09298c7db30f473c4dfdda12d99a171e9c76098c316067.html} (97%) rename tests/snapshots/external/{f7711b8af7689b331209f8c034c8cc3a2ec894372644a8eaee597418e9b55b3c.html => 0f046dea611f6c9e90b8eaed720f22af372394971808e2a5d1b3a12286f1ec76.html} (97%) rename tests/snapshots/external/{46e56f39b10d1e8ee4017bc11457bf76d169fc80b3d3e465213671b7f6e548eb.html => 13334b5b4fcf7267539b9eb99ca2ab79c66766ec6f35383f4bfcb6a8d9e2a116.html} (94%) rename tests/snapshots/external/{981438492e387bc82b23f09e3c5e0b452db5a1ffd88e52479db2b52a170fd8f9.html => 14bca0e5703be9cab876200d88cccd1d728d1bdfef7cbfac751af212e00a8663.html} (98%) rename tests/snapshots/external/{e6a9b76f268cde81a129e7273038db0ff3fcd73530442a30c48cf01dcbc30aaa.html => 19f98a747c015a074f3d3362d03ed72f9da9db3aefe969a0d78c4052e7594372.html} (97%) rename tests/snapshots/external/{955e5111f4262f280b0787a22dfa46c9ea93c80bc49e1a1de100349341d93fb9.html => 261a38d7a86b5e959b6d1c165108301963d0170c2189d5aa18bb7c7eade84ea4.html} (64%) rename tests/snapshots/external/{7c988c9e13efeadd20b911a95cc69973b715cceacdadefc540109aad3c274bde.html => 34b16654e7baa8e16315cef2919f2eafa51ba39ec28c4c970fe7ea8e2c79f9d2.html} (98%) rename tests/snapshots/external/{728c1344630190ac84514ebd8e5ae2d95ba8685e16d2c79749f675b5b2a6cea5.html => 3935bcf6d71b58daa0e4512cbf3f53e19516885fb65d0bd760c12aadd021507f.html} (97%) rename tests/snapshots/external/{052c34f22e4c711b1f13f53085cdd5e8edcfae4bdc1d8cb7f2ff76cd1c46cce5.html => 3c21330afd6529769164afe388e9385a9fddb3ae628124965e0c7b81932a0c63.html} (94%) rename tests/snapshots/external/{ae74b5980f9b6996ed6e112d53168fde16c32d92bed42fb3193f98e0e3f04602.html => 43d819f94dc7cafe9ed60ce604bab9a938f42a115dc534cb72d12e15e998e96d.html} (98%) rename tests/snapshots/external/{e8be7a9b1410e40dac79fe0ee29d3036e707a177b2ba2bdad25a6998bec570b7.html => 722165bce3ada19df43b169ea982ab4908d94cd1bf19b777e1e6bc22e8aa02a5.html} (98%) rename tests/snapshots/external/{7d5fe66539191786245991395e77d8ba0bbb22330cb08eaec2e84159bde4159b.html => 75b69b702f3b5fa3bc0d30091297b0a09a8915eb7f0e1f7be1ce99f5d59d9514.html} (98%) rename tests/snapshots/external/{b4b490164ab1a724cac7aba25bbc69a33e7dd44500e9337718cd96da1bb56325.html => 84193b3c9f5d84fef33daa61fb61aa9a3e66171d312de8d7f836c69f0bc069b0.html} (97%) rename tests/snapshots/external/{ba51e100acd4f6ad91f1ef484aa5f1bd537e661588b1742d93d0a6543cc3592c.html => 8733f7fb7b6d28b15bbe736f29c7fd030467c0ccfa2cbc6a68616e06c6dc6a9b.html} (98%) rename tests/snapshots/external/{0c2924ff976fa0e32ba66558a4f9e1eff4cd66196506a37977cdb33325a50718.html => 9720526cf5e4c44f27695c59764bb1e05e428834744442f43527ebf2b8acfb35.html} (98%) rename tests/snapshots/external/{0b1372d7f7c057905f665ad506f3dd3bee62fb9b1c8b2a39991550e7845c2b02.html => c0f102dbd7d4de76de40c06a8205a642465f5fde9a37b4b969aa01f161ef25a4.html} (98%) rename tests/snapshots/external/{052e71e7e9d5bec710fb2d36b009122c48eca0a19d0611df530e607f5bacdf6f.html => cd3e458517147c43c360525140aa1b9a81682634aaf2674ffd4cceb7fc44aba6.html} (94%) rename tests/snapshots/external/{d540895f6bf91c8c8e4abc02f40529a61c6cec71b18da2e4f02206ec18b901ef.html => cd51e40cc0ddf1d42b7c6bf7560ead2501370ee9d67499b74afc83e258caff8e.html} (98%) rename tests/snapshots/external/{cdc8126d78b690d11c09e3128df0f8d65379375a6bd390da30f5676bf2289cf2.html => d556527026068280df9b77db277472320842cb1ae6099ac3cf558031afda6d2e.html} (97%) rename tests/snapshots/external/{347d4ffe2cb3f2ca3f0d1f3f09cffa96645eb2af29983e75d807fccff96d8f75.html => dcf34c2f72697f7a4700e4a1f048d601f374eab35eea68c9beb8bab8fc269aed.html} (97%) rename tests/snapshots/external/{f848d4a9e516beeb1b1719630e34aa243a093ccd362a63e33dbd6202ae8ab75d.html => e5dc372374af6f90a5d456d8683aacdf81104137ce91bd6d4121827f8d989d96.html} (98%) rename tests/snapshots/external/{a1167b14f5a71a283817bf5866d2bb0bd08bf23dc054c6f7938a04f42feab99d.html => e8608b0de174402ca18f88ed58849312158c22f5bfdc845d2da02055fe14853c.html} (95%) rename tests/snapshots/external/{cc19537fdba4a26b10c60d5586b0eb7ef0264a783a3c47d1114d21fa8cfa3947.html => ea914f1afa9de4b5eddc9792c2b6a5d8de367274278976092bb824e99e523ca5.html} (98%) rename tests/snapshots/external/{a2c5be9bd5d1f0db3ff64b44353c1760f5eb69d7db6401da2f28518d0e8065c4.html => eac5bee59a9ee0a64602fd6bb8f4f54cb5f3543aa321169921326288a61f556c.html} (97%) rename tests/snapshots/external/{83119803338105f101311992d31947e4fcaf2c5a6c68cad6355d8611c1cc2e3f.html => f4150843096a1371b097478f8d67062e3d45ab9f6a8f97e79ae62d32abc5e22a.html} (97%) rename tests/snapshots/external/{59a9e1ffb2f0807b594a933444c78753a06f359527ea4adac85c72a7812b21d3.html => fca72854c849dc68c3ad072a41c32f926f95c6e88775f3e2eeaa63138d99837c.html} (97%) rename tests/snapshots/external/{88855b0284174733b57edd2043e0e8cd6a1a0223055f08b80031452eb05d9484.html => fd291f98ca28b8f15b5a8ed6a2608bacf5b5322599bcbf0544ef8e9c0a27870b.html} (97%) rename tests/snapshots/external/{3d072a22b9513eecb51c6a5f39b978c1c1d3ef56a572031a307fe1cad1f17eff.html => fe1cd23642d405d0b2a4d29ec4a2125f55b54f90c2440ee2d856540415e77745.html} (97%) rename tests/snapshots/external/{0fac4f5e7f455b351c60268567bfcbd0259b652d0534259efea7815aa15b1122.html => fe25ab7600392b4fd3a1438fb54337041719faac884123527bab9a92e3a51be5.html} (97%) diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css index 03c39d33..7d66153a 100644 --- a/docs/css/mkdocstrings.css +++ b/docs/css/mkdocstrings.css @@ -25,3 +25,48 @@ a.external:hover::after, a.autorefs-external:hover::after { background-color: var(--md-accent-fg-color); } + +/* 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/insiders/changelog.md b/docs/insiders/changelog.md index a6c7b907..25562a7d 100644 --- a/docs/insiders/changelog.md +++ b/docs/insiders/changelog.md @@ -2,6 +2,10 @@ ## mkdocstrings-python Insiders +### 1.10.0 March 10, 2025 { id="1.10.0" } + +- [Backlinks][backlinks] + ### 1.9.0 September 03, 2024 { id="1.9.0" } - [Relative cross-references][relative_crossrefs] diff --git a/docs/insiders/goals.yml b/docs/insiders/goals.yml index 16d1d507..2153312d 100644 --- a/docs/insiders/goals.yml +++ b/docs/insiders/goals.yml @@ -42,3 +42,6 @@ goals: - name: Scoped cross-references ref: /usage/configuration/docstrings/#scoped_crossrefs since: 2024/09/03 + - name: Backlinks + ref: /usage/configuration/general/#backlinks + since: 2025/03/10 diff --git a/docs/usage/configuration/general.md b/docs/usage/configuration/general.md index 3983a616..77b3f3f9 100644 --- a/docs/usage/configuration/general.md +++ b/docs/usage/configuration/general.md @@ -60,6 +60,38 @@ plugins: //// /// +[](){#option-backlinks} +## `backlinks` + +[:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — +[:octicons-tag-24: Insiders 1.10.0](../../insiders/changelog.md#1.10.0) + +- **:octicons-package-24: Type Literal["flat", "tree", False] :material-equal: `False`{ title="default value" }** + +The `backlinks` option enables rendering of backlinks within your API documentation. + +When an arbitrary section of your documentation links to an API symbol, this link will be collected as a backlink, and rendered below your API symbol. In short, the API symbol will link back to the section that links to it. Such backlinks will help your users navigate the documentation, as they will immediately which functions return a specific symbol, or where a specific symbol is accepted as parameter, etc.. + +Each backlink is a list of breadcrumbs that represent the navigation, from the root page down to the given section. + +The available styles for rendering backlinks are **`flat`** and **`tree`**. + +- **`flat`** will render backlinks as a single-layer list. This can lead to repetition of breadcrumbs. +- **`tree`** will combine backlinks into a tree, to remove repetition of breadcrumbs. + +WARNING: **Global-only option.** For now, the option only works when set globally in `mkdocs.yml`. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + backlinks: tree +``` + + + [](){#option-extensions} ## `extensions` diff --git a/mkdocs.yml b/mkdocs.yml index 396f738f..ae518a4c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -160,6 +160,7 @@ plugins: - https://mkdocstrings.github.io/griffe/objects.inv - https://python-markdown.github.io/objects.inv options: + backlinks: tree docstring_options: ignore_init_summary: true docstring_section_style: list diff --git a/src/mkdocstrings_handlers/python/config.py b/src/mkdocstrings_handlers/python/config.py index a1ecdcd8..af2ac7f7 100644 --- a/src/mkdocstrings_handlers/python/config.py +++ b/src/mkdocstrings_handlers/python/config.py @@ -386,6 +386,14 @@ class PythonInputOptions: ), ] = "brief" + backlinks: Annotated[ + Literal["flat", "tree", False], + Field( + group="general", + description="Whether to render backlinks, and how.", + ), + ] = False + docstring_options: Annotated[ GoogleStyleOptions | NumpyStyleOptions | SphinxStyleOptions | AutoStyleOptions | None, Field( diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index d9db6669..69f228f1 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -301,6 +301,7 @@ def update_env(self, config: Any) -> None: # noqa: ARG002 self.env.filters["as_functions_section"] = rendering.do_as_functions_section self.env.filters["as_classes_section"] = rendering.do_as_classes_section self.env.filters["as_modules_section"] = rendering.do_as_modules_section + self.env.filters["backlink_tree"] = rendering.do_backlink_tree self.env.globals["AutorefsHook"] = rendering.AutorefsHook self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates() diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index 4b8a8ee4..0a670b90 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -8,11 +8,12 @@ import subprocess import sys import warnings +from collections import defaultdict from dataclasses import replace from functools import lru_cache from pathlib import Path from re import Match, Pattern -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, TypeVar from griffe import ( Alias, @@ -28,11 +29,11 @@ ) from jinja2 import TemplateNotFound, pass_context, pass_environment from markupsafe import Markup -from mkdocs_autorefs import AutorefsHookInterface +from mkdocs_autorefs import AutorefsHookInterface, Backlink, BacklinkCrumb from mkdocstrings import get_logger if TYPE_CHECKING: - from collections.abc import Iterator, Sequence + from collections.abc import Iterable, Iterator, Sequence from griffe import Attribute, Class, Function, Module from jinja2 import Environment, Template @@ -210,10 +211,15 @@ def do_format_attribute( signature = str(attribute_path).strip() if annotations and attribute.annotation: - annotation = template.render(context.parent, expression=attribute.annotation, signature=True) + annotation = template.render( + context.parent, + expression=attribute.annotation, + signature=True, + backlink_type="returned-by", + ) signature += f": {annotation}" if attribute.value: - value = template.render(context.parent, expression=attribute.value, signature=True) + value = template.render(context.parent, expression=attribute.value, signature=True, backlink_type="used-by") signature += f" = {value}" signature = do_format_code(signature, line_length) @@ -725,3 +731,45 @@ def get_context(self) -> AutorefsHookInterface.Context: filepath=str(filepath), lineno=lineno, ) + + +T = TypeVar("T") +Tree = dict[T, "Tree"] +CompactTree = dict[tuple[T, ...], "CompactTree"] +_rtree = lambda: defaultdict(_rtree) # type: ignore[has-type,var-annotated] # noqa: E731 + + +def _tree(data: Iterable[tuple[T, ...]]) -> Tree: + new_tree = _rtree() + for nav in data: + *path, leaf = nav + node = new_tree + for key in path: + node = node[key] + node[leaf] = _rtree() + return new_tree + + +def _compact_tree(tree: Tree) -> CompactTree: + new_tree = _rtree() + for key, value in tree.items(): + child = _compact_tree(value) + if len(child) == 1: + child_key, child_value = next(iter(child.items())) + new_key = (key, *child_key) + new_tree[new_key] = child_value + else: + new_tree[(key,)] = child + return new_tree + + +def do_backlink_tree(backlinks: list[Backlink]) -> CompactTree[BacklinkCrumb]: + """Build a tree of backlinks. + + Parameters: + backlinks: The list of backlinks. + + Returns: + A tree of backlinks. + """ + return _compact_tree(_tree(backlink.crumbs for backlink in backlinks)) diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html.jinja index c13bb641..3217b4f6 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html.jinja @@ -113,6 +113,10 @@ Context: {% include "docstring"|get_template with context %} {% endwith %} {% endblock docstring %} + + {% if config.backlinks %} + + {% endif %} {% endblock contents %}
diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/backlinks.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/backlinks.html.jinja new file mode 100644 index 00000000..2ab16038 --- /dev/null +++ b/src/mkdocstrings_handlers/python/templates/material/_base/backlinks.html.jinja @@ -0,0 +1,17 @@ +{#- Template for backlinks. + +This template renders backlinks. + +Context: + backlinks (Mapping[str, Iterable[str]]): The backlinks to render. + config (dict): The configuration options. + verbose_type (Mapping[str, str]): The verbose backlink types. + default_crumb (BacklinkCrumb): A default, empty crumb. +-#} + +{% block logs scoped %} + {#- Logging block. + + This block can be used to log debug messages, deprecation messages, warnings, etc. + -#} +{% endblock logs %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja index e8f89fa3..a07a36be 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/class.html.jinja @@ -130,7 +130,11 @@ Context: {% if config.show_bases and class.bases %}

Bases: {% for expression in class.bases -%} - {% include "expression"|get_template with context %}{% if not loop.last %}, {% endif %} + + {%- with backlink_type = "subclassed-by" -%} + {%- include "expression"|get_template with context -%} + {%- endwith -%} + {% if not loop.last %}, {% endif %} {% endfor -%}

{% endif %} @@ -159,6 +163,10 @@ Context: {% endif %} {% endblock docstring %} + {% if config.backlinks %} + + {% endif %} + {% block summary scoped %} {#- Summary block. diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/other_parameters.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/other_parameters.html.jinja index 1689dcb8..8b64af1b 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/other_parameters.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/other_parameters.html.jinja @@ -36,7 +36,7 @@ Context: {{ parameter.name }} {% if parameter.annotation %} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -60,7 +60,7 @@ Context:
  • {{ parameter.name }} {% if parameter.annotation %} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} ({% include "expression"|get_template with context %}) {% endwith %} {% endif %} @@ -94,7 +94,7 @@ Context: {% if parameter.annotation %} {{ lang.t("TYPE:") }} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/parameters.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/parameters.html.jinja index d4e9acb8..ae8fb7a3 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/parameters.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/parameters.html.jinja @@ -51,7 +51,7 @@ Context: {% if parameter.annotation %} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -63,7 +63,7 @@ Context: {% if parameter.default %} - {% with expression = parameter.default %} + {% with expression = parameter.default, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} {% else %} @@ -96,10 +96,10 @@ Context: {{ parameter.name }} {% endif %} {% if parameter.annotation %} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} ({% include "expression"|get_template with context %} {%- if parameter.default %}, {{ lang.t("default:") }} - {% with expression = parameter.default %} + {% with expression = parameter.default, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %}) @@ -149,7 +149,7 @@ Context: {% if parameter.annotation %} {{ lang.t("TYPE:") }} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} @@ -157,7 +157,7 @@ Context: {% if parameter.default %} {{ lang.t("DEFAULT:") }} - {% with expression = parameter.default %} + {% with expression = parameter.default, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/raises.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/raises.html.jinja index d734e94a..1e1d5006 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/raises.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/raises.html.jinja @@ -34,7 +34,7 @@ Context: {% if raises.annotation %} - {% with expression = raises.annotation %} + {% with expression = raises.annotation, backlink_type = "raised-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -57,7 +57,7 @@ Context: {% for raises in section.value %}
  • {% if raises.annotation %} - {% with expression = raises.annotation %} + {% with expression = raises.annotation, backlink_type = "raised-by" %} {% include "expression"|get_template with context %} {% endwith %} – @@ -84,7 +84,7 @@ Context: - {% with expression = raises.annotation %} + {% with expression = raises.annotation, backlink_type = "raised-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/receives.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/receives.html.jinja index 3b618c66..a447b216 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/receives.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/receives.html.jinja @@ -37,7 +37,7 @@ Context: {% if name_column %}{% if receives.name %}{{ receives.name }}{% endif %}{% endif %} {% if receives.annotation %} - {% with expression = receives.annotation %} + {% with expression = receives.annotation, backlink_type = "received-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -61,7 +61,7 @@ Context:
  • {% if receives.name %}{{ receives.name }}{% endif %} {% if receives.annotation %} - {% with expression = receives.annotation %} + {% with expression = receives.annotation, backlink_type = "received-by" %} {% if receives.name %} ({% endif %} {% include "expression"|get_template with context %} {% if receives.name %}){% endif %} @@ -93,7 +93,7 @@ Context: {{ receives.name }} {% elif receives.annotation %} - {% with expression = receives.annotation %} + {% with expression = receives.annotation, backlink_type = "received-by" %} {% include "expression"|get_template with context %} {% endwith %} @@ -107,7 +107,7 @@ Context:

    {{ lang.t("TYPE:") }} - {% with expression = receives.annotation %} + {% with expression = receives.annotation, backlink_type = "received-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/returns.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/returns.html.jinja index af61fce5..30540e66 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/returns.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/returns.html.jinja @@ -37,7 +37,7 @@ Context: {% if name_column %}{% if returns.name %}{{ returns.name }}{% endif %}{% endif %} {% if returns.annotation %} - {% with expression = returns.annotation %} + {% with expression = returns.annotation, backlink_type = "returned-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -61,7 +61,7 @@ Context:

  • {% if returns.name %}{{ returns.name }}{% endif %} {% if returns.annotation %} - {% with expression = returns.annotation %} + {% with expression = returns.annotation, backlink_type = "returned-by" %} {% if returns.name %} ({% endif %} {% include "expression"|get_template with context %} {% if returns.name %}){% endif %} @@ -93,7 +93,7 @@ Context: {{ returns.name }} {% elif returns.annotation %} - {% with expression = returns.annotation %} + {% with expression = returns.annotation, backlink_type = "returned-by" %} {% include "expression"|get_template with context %} {% endwith %} @@ -107,7 +107,7 @@ Context:

    {{ lang.t("TYPE:") }} - {% with expression = returns.annotation %} + {% with expression = returns.annotation, backlink_type = "returned-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/warns.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/warns.html.jinja index 782c0cdf..814e3020 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/warns.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/warns.html.jinja @@ -34,7 +34,7 @@ Context: {% if warns.annotation %} - {% with expression = warns.annotation %} + {% with expression = warns.annotation, backlink_type = "emitted-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -57,7 +57,7 @@ Context: {% for warns in section.value %}

  • {% if warns.annotation %} - {% with expression = warns.annotation %} + {% with expression = warns.annotation, backlink_type = "emitted-by" %} {% include "expression"|get_template with context %} {% endwith %} – @@ -84,7 +84,7 @@ Context: - {% with expression = warns.annotation %} + {% with expression = warns.annotation, backlink_type = "emitted-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/yields.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/yields.html.jinja index 6d3cfa14..01b8b9e5 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/yields.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/yields.html.jinja @@ -37,7 +37,7 @@ Context: {% if name_column %}{% if yields.name %}{{ yields.name }}{% endif %}{% endif %} {% if yields.annotation %} - {% with expression = yields.annotation %} + {% with expression = yields.annotation, backlink_type = "yielded-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} @@ -61,7 +61,7 @@ Context:
  • {% if yields.name %}{{ yields.name }}{% endif %} {% if yields.annotation %} - {% with expression = yields.annotation %} + {% with expression = yields.annotation, backlink_type = "yielded-by" %} {% if yields.name %} ({% endif %} {% include "expression"|get_template with context %} {% if yields.name %}){% endif %} @@ -93,7 +93,7 @@ Context: {{ yields.name }} {% elif yields.annotation %} - {% with expression = yields.annotation %} + {% with expression = yields.annotation, backlink_type = "yielded-by" %} {% include "expression"|get_template with context %} {% endwith %} @@ -107,7 +107,7 @@ Context:

    {{ lang.t("TYPE:") }}: - {% with expression = yields.annotation %} + {% with expression = yields.annotation, backlink_type = "yielded-by" %} {% include "expression"|get_template with context %} {% endwith %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/expression.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/expression.html.jinja index 781d46c7..d49e43be 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/expression.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/expression.html.jinja @@ -11,7 +11,7 @@ which is a tree-like structure representing a Python expression. -#} {% endblock logs %} -{%- macro crossref(name, annotation_path) -%} +{%- macro crossref(name, annotation_path, backlink_type="") -%} {#- Output a cross-reference. This macro outputs a cross-reference to the given name. @@ -35,11 +35,11 @@ which is a tree-like structure representing a Python expression. {{ prefix }} {%- if not signature -%} {#- Always render cross-references outside of signatures. We don't need to stash them. -#} - {{ title }} + {{ title }} {%- elif config.signature_crossrefs -%} {#- We're in a signature and cross-references are enabled, we must render one and stash it. -#} {%- filter stash_crossref(length=title|length) -%} - {{ title }} + {{ title }} {%- endfilter -%} {%- else -%} {#- We're in a signature but cross-references are disabled, we just render the title. -#} @@ -72,7 +72,7 @@ which is a tree-like structure representing a Python expression. {%- endif -%} {%- endmacro -%} -{%- macro render(expression, annotations_path) -%} +{%- macro render(expression, annotations_path, backlink_type="") -%} {#- Render an expression. Parameters: @@ -85,13 +85,13 @@ which is a tree-like structure representing a Python expression. {%- if expression is string -%} {%- if signature -%}{{ expression|safe }}{%- else -%}{{ expression }}{%- endif -%} {%- elif expression.classname == "ExprName" -%} - {{ crossref(expression, annotations_path) }} + {{ crossref(expression, annotations_path, backlink_type) }} {%- elif config.unwrap_annotated and expression.classname == "ExprSubscript" and expression.canonical_path in ("typing.Annotated", "typing_extensions.Annotated") -%} {{ render(expression.slice.elements[0], annotations_path) }} {%- elif expression.classname == "ExprAttribute" -%} {%- if annotations_path == "brief" -%} {%- if expression.last.is_enum_value -%} - {{ crossref(expression.last.parent, "brief") }}.value + {{ crossref(expression.last.parent, "brief", backlink_type) }}.value {%- else -%} {{ render(expression.last, "brief") }} {%- endif -%} @@ -116,4 +116,4 @@ which is a tree-like structure representing a Python expression. {%- endif -%} {%- endmacro -%} -{{ render(expression, config.annotations_path) }} +{{ render(expression, config.annotations_path, backlink_type|default("")) }} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja index b475cf1b..4adcf3bd 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/function.html.jinja @@ -129,6 +129,10 @@ Context: {% endwith %} {% endblock docstring %} + {% if config.backlinks %} + + {% endif %} + {% block source scoped %} {#- Source block. diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/module.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/module.html.jinja index fa2d2e6a..d49dc76d 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/module.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/module.html.jinja @@ -97,6 +97,10 @@ Context: {% endwith %} {% endblock docstring %} + {% if config.backlinks %} + + {% endif %} + {% block summary scoped %} {#- Summary block. diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/signature.html.jinja b/src/mkdocstrings_handlers/python/templates/material/_base/signature.html.jinja index ce5c3f04..e414fbe3 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/signature.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/material/_base/signature.html.jinja @@ -49,7 +49,9 @@ Context: {%- set ns.equal = " = " -%} {%- if config.separate_signature -%} {%- with expression = parameter.annotation -%} - {%- set ns.annotation -%}: {% include "expression"|get_template with context %}{%- endset -%} + {%- set ns.annotation -%}: {% with backlink_type = "used-by" -%} + {%- include "expression"|get_template with context -%} + {%- endwith -%}{%- endset -%} {%- endwith -%} {%- else -%} {%- set ns.annotation = ": " + parameter.annotation|safe -%} @@ -102,7 +104,7 @@ Context: {%- if ns.default -%} {{ ns.equal }} {%- if config.signature_crossrefs and config.separate_signature -%} - {%- with expression = parameter.default -%} + {%- with expression = parameter.default, backlink_type = "used-by" -%} {%- include "expression"|get_template with context -%} {%- endwith -%} {%- else -%} @@ -121,7 +123,9 @@ Context: and function.annotation and not (config.merge_init_into_class and function.name == "__init__" ) %} -> {% if config.separate_signature and config.signature_crossrefs -%} - {%- with expression = function.annotation %}{% include "expression"|get_template with context %}{%- endwith -%} + {%- with expression = function.annotation, backlink_type = "returned-by" -%} + {%- include "expression"|get_template with context -%} + {%- endwith -%} {%- else -%} {{ function.annotation|safe }} {%- endif -%} diff --git a/src/mkdocstrings_handlers/python/templates/material/backlinks.html.jinja b/src/mkdocstrings_handlers/python/templates/material/backlinks.html.jinja new file mode 100644 index 00000000..08ba4922 --- /dev/null +++ b/src/mkdocstrings_handlers/python/templates/material/backlinks.html.jinja @@ -0,0 +1 @@ +{% extends "_base/backlinks.html.jinja" %} diff --git a/src/mkdocstrings_handlers/python/templates/material/style.css b/src/mkdocstrings_handlers/python/templates/material/style.css index 7e819d8b..e5e150ec 100644 --- a/src/mkdocstrings_handlers/python/templates/material/style.css +++ b/src/mkdocstrings_handlers/python/templates/material/style.css @@ -41,6 +41,28 @@ font-weight: bold; } +/* Backlinks crumb separator. */ +.doc-backlink-crumb { + display: inline-flex; + gap: .2rem; + white-space: nowrap; + align-items: center; + vertical-align: middle; +} +.doc-backlink-crumb:not(:first-child)::before { + background-color: var(--md-default-fg-color--lighter); + content: ""; + display: inline; + height: 1rem; + --md-path-icon: url('data:image/svg+xml;charset=utf-8,'); + -webkit-mask-image: var(--md-path-icon); + mask-image: var(--md-path-icon); + width: 1rem; +} +.doc-backlink-crumb.last { + font-weight: bold; +} + /* Symbols in Navigation and ToC. */ :root, :host, [data-md-color-scheme="default"] { @@ -82,7 +104,8 @@ code.doc-symbol { font-weight: bold; } -code.doc-symbol-parameter { +code.doc-symbol-parameter, +a code.doc-symbol-parameter { color: var(--doc-symbol-parameter-fg-color); background-color: var(--doc-symbol-parameter-bg-color); } @@ -91,7 +114,8 @@ code.doc-symbol-parameter::after { content: "param"; } -code.doc-symbol-attribute { +code.doc-symbol-attribute, +a code.doc-symbol-attribute { color: var(--doc-symbol-attribute-fg-color); background-color: var(--doc-symbol-attribute-bg-color); } @@ -100,7 +124,8 @@ code.doc-symbol-attribute::after { content: "attr"; } -code.doc-symbol-function { +code.doc-symbol-function, +a code.doc-symbol-function { color: var(--doc-symbol-function-fg-color); background-color: var(--doc-symbol-function-bg-color); } @@ -109,7 +134,8 @@ code.doc-symbol-function::after { content: "func"; } -code.doc-symbol-method { +code.doc-symbol-method, +a code.doc-symbol-method { color: var(--doc-symbol-method-fg-color); background-color: var(--doc-symbol-method-bg-color); } @@ -118,7 +144,8 @@ code.doc-symbol-method::after { content: "meth"; } -code.doc-symbol-class { +code.doc-symbol-class, +a code.doc-symbol-class { color: var(--doc-symbol-class-fg-color); background-color: var(--doc-symbol-class-bg-color); } @@ -127,7 +154,8 @@ code.doc-symbol-class::after { content: "class"; } -code.doc-symbol-module { +code.doc-symbol-module, +a code.doc-symbol-module { color: var(--doc-symbol-module-fg-color); background-color: var(--doc-symbol-module-bg-color); } diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/other_parameters.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/other_parameters.html.jinja index beb4f678..657b21b4 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/other_parameters.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/other_parameters.html.jinja @@ -32,7 +32,7 @@ Context:

  • {{ parameter.name }} {% if parameter.annotation %} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} ({% include "expression"|get_template with context %}) {% endwith %} {% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/parameters.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/parameters.html.jinja index 295ab082..6017a8cb 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/parameters.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/parameters.html.jinja @@ -32,10 +32,10 @@ Context:
  • {{ parameter.name }} {% if parameter.annotation %} - {% with expression = parameter.annotation %} + {% with expression = parameter.annotation, backlink_type = "used-by" %} ({% include "expression"|get_template with context %} {%- if parameter.default %}, {{ lang.t("default:") }} - {% with expression = parameter.default %} + {% with expression = parameter.default, backlink_type = "used-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %}) diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/raises.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/raises.html.jinja index 7fa8cd86..11f695e8 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/raises.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/raises.html.jinja @@ -31,7 +31,7 @@ Context: {% for raises in section.value %}
  • {% if raises.annotation %} - {% with expression = raises.annotation %} + {% with expression = raises.annotation, backlink_type = "raised-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/receives.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/receives.html.jinja index 9ee189bc..aec9a858 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/receives.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/receives.html.jinja @@ -32,7 +32,7 @@ Context:
  • {% if receives.name %}{{ receives.name }}{% endif %} {% if receives.annotation %} - {% with expression = receives.annotation %} + {% with expression = receives.annotation, backlink_type = "received-by" %} {% if receives.name %}({% endif %} {% include "expression"|get_template with context %} {% if receives.name %}){% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/returns.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/returns.html.jinja index 2dbd21af..3f8bbba4 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/returns.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/returns.html.jinja @@ -32,7 +32,7 @@ Context:
  • {% if returns.name %}{{ returns.name }}{% endif %} {% if returns.annotation %} - {% with expression = returns.annotation %} + {% with expression = returns.annotation, backlink_type = "returned-by" %} {% if returns.name %}({% endif %} {% include "expression"|get_template with context %} {% if returns.name %}){% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/warns.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/warns.html.jinja index 61f3c839..1d465ec9 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/warns.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/warns.html.jinja @@ -31,7 +31,7 @@ Context: {% for warns in section.value %}
  • {% if warns.annotation %} - {% with expression = warns.annotation %} + {% with expression = warns.annotation, backlink_type = "emitted-by" %} {% include "expression"|get_template with context %} {% endwith %} {% endif %} diff --git a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/yields.html.jinja b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/yields.html.jinja index 0fa6fcbc..70c782b3 100644 --- a/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/yields.html.jinja +++ b/src/mkdocstrings_handlers/python/templates/readthedocs/_base/docstring/yields.html.jinja @@ -32,7 +32,7 @@ Context:
  • {% if yields.name %}{{ yields.name }}{% endif %} {% if yields.annotation %} - {% with expression = yields.annotation %} + {% with expression = yields.annotation, backlink_type = "yielded-by" %} {% if yields.name %}({% endif %} {% include "expression"|get_template with context %} {% if yields.name %}){% endif %} diff --git a/tests/snapshots/__init__.py b/tests/snapshots/__init__.py index 4469afed..712cdafe 100644 --- a/tests/snapshots/__init__.py +++ b/tests/snapshots/__init__.py @@ -13,7 +13,7 @@ ("separate_signature", True), ("show_signature_annotations", True), ("signature_crossrefs", True), - ): external("955e5111f426*.html"), + ): external("261a38d7a86b*.html"), ( ("separate_signature", False), ("show_signature_annotations", True), @@ -54,28 +54,20 @@ ("inherited_members", ("method1",)), ("members", False), ): external("ab0ddac637b5*.html"), - (("filters", None), ("inherited_members", True), ("members", True)): external( - "0b1372d7f7c0*.html", - ), - (("filters", ()), ("inherited_members", False), ("members", True)): external( - "59a9e1ffb2f0*.html", - ), + (("filters", None), ("inherited_members", True), ("members", True)): external("c0f102dbd7d4*.html"), + (("filters", ()), ("inherited_members", False), ("members", True)): external("fca72854c849*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", ()), ("members", ("module_attribute",)), ): external("6d12192d6b4d*.html"), - (("filters", ()), ("inherited_members", ()), ("members", False)): external( - "366b0537fe06*.html", - ), + (("filters", ()), ("inherited_members", ()), ("members", False)): external("366b0537fe06*.html"), ( ("filters", ()), ("inherited_members", ("method1",)), ("members", ("module_attribute",)), ): external("e90c3e0c85dd*.html"), - (("filters", ()), ("inherited_members", True), ("members", True)): external( - "e8be7a9b1410*.html", - ), + (("filters", ()), ("inherited_members", True), ("members", True)): external("722165bce3ad*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ("method1",)), @@ -85,15 +77,13 @@ ("filters", ()), ("inherited_members", ("method1",)), ("members", True), - ): external("d540895f6bf9*.html"), - (("filters", ()), ("inherited_members", False), ("members", False)): external( - "5cf0130e3b4f*.html", - ), + ): external("cd51e40cc0dd*.html"), + (("filters", ()), ("inherited_members", False), ("members", False)): external("5cf0130e3b4f*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", True), ("members", True), - ): external("7c988c9e13ef*.html"), + ): external("34b16654e7ba*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", False), @@ -119,17 +109,13 @@ ("inherited_members", False), ("members", ("module_attribute",)), ): external("5a9c10410801*.html"), - (("filters", ()), ("inherited_members", False), ("members", ())): external( - "fba0d78ae23e*.html", - ), + (("filters", ()), ("inherited_members", False), ("members", ())): external("fba0d78ae23e*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ("method1",)), ("members", None), ): external("cfcd41685591*.html"), - (("filters", ()), ("inherited_members", False), ("members", None)): external( - "a2c5be9bd5d1*.html", - ), + (("filters", ()), ("inherited_members", False), ("members", None)): external("eac5bee59a9e*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ()), @@ -145,9 +131,7 @@ ("inherited_members", ("method1",)), ("members", ()), ): external("4f60da13e2d4*.html"), - (("filters", ()), ("inherited_members", True), ("members", ())): external( - "c915eb92fd5d*.html", - ), + (("filters", ()), ("inherited_members", True), ("members", ())): external("c915eb92fd5d*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ()), @@ -157,21 +141,15 @@ ("filters", ("!module_attribute",)), ("inherited_members", ("method1",)), ("members", None), - ): external("3d072a22b951*.html"), - (("filters", None), ("inherited_members", False), ("members", False)): external( - "9bd282a6f2fe*.html", - ), + ): external("fe1cd23642d4*.html"), + (("filters", None), ("inherited_members", False), ("members", False)): external("9bd282a6f2fe*.html"), ( ("filters", None), ("inherited_members", ()), ("members", ("module_attribute",)), ): external("166b8dfab738*.html"), - (("filters", None), ("inherited_members", ()), ("members", False)): external( - "44e42f27bfe3*.html", - ), - (("filters", None), ("inherited_members", False), ("members", None)): external( - "f7711b8af768*.html", - ), + (("filters", None), ("inherited_members", ()), ("members", False)): external("44e42f27bfe3*.html"), + (("filters", None), ("inherited_members", False), ("members", None)): external("0f046dea611f*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", True), @@ -182,14 +160,12 @@ ("inherited_members", True), ("members", False), ): external("f3f3acb6b51b*.html"), - (("filters", None), ("inherited_members", ()), ("members", True)): external( - "347d4ffe2cb3*.html", - ), + (("filters", None), ("inherited_members", ()), ("members", True)): external("dcf34c2f7269*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", True), ("members", None), - ): external("ba51e100acd4*.html"), + ): external("8733f7fb7b6d*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", False), @@ -215,12 +191,8 @@ ("inherited_members", False), ("members", ()), ): external("d5a6bf59c663*.html"), - (("filters", None), ("inherited_members", ()), ("members", None)): external( - "88855b028417*.html", - ), - (("filters", ()), ("inherited_members", True), ("members", None)): external( - "981438492e38*.html", - ), + (("filters", None), ("inherited_members", ()), ("members", None)): external("fd291f98ca28*.html"), + (("filters", ()), ("inherited_members", True), ("members", None)): external("14bca0e5703b*.html"), ( ("filters", ()), ("inherited_members", False), @@ -230,25 +202,23 @@ ("filters", None), ("inherited_members", ("method1",)), ("members", None), - ): external("ae74b5980f9b*.html"), + ): external("43d819f94dc7*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", True), ("members", ()), ): external("95f8e480937f*.html"), - (("filters", None), ("inherited_members", False), ("members", True)): external( - "831198033381*.html", - ), + (("filters", None), ("inherited_members", False), ("members", True)): external("f4150843096a*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", True), ("members", True), - ): external("052c34f22e4c*.html"), + ): external("3c21330afd65*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", False), ("members", None), - ): external("cdc8126d78b6*.html"), + ): external("d55652702606*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ("method1",)), @@ -259,9 +229,7 @@ ("inherited_members", True), ("members", ("module_attribute",)), ): external("96cf94f4822a*.html"), - (("filters", None), ("inherited_members", True), ("members", ())): external( - "ce06da7f07b3*.html", - ), + (("filters", None), ("inherited_members", True), ("members", ())): external("ce06da7f07b3*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", ()), @@ -271,15 +239,13 @@ ("filters", None), ("inherited_members", ("method1",)), ("members", True), - ): external("7d5fe6653919*.html"), + ): external("75b69b702f3b*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", True), ("members", False), ): external("d726cb8367d9*.html"), - (("filters", None), ("inherited_members", False), ("members", ())): external( - "fb770e6537bc*.html", - ), + (("filters", None), ("inherited_members", False), ("members", ())): external("fb770e6537bc*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", True), @@ -294,15 +260,13 @@ ("filters", ("module_attribute",)), ("inherited_members", ()), ("members", True), - ): external("46e56f39b10d*.html"), + ): external("13334b5b4fcf*.html"), ( ("filters", ()), ("inherited_members", ()), ("members", ("module_attribute",)), ): external("388a13d71284*.html"), - (("filters", None), ("inherited_members", True), ("members", False)): external( - "3f5d794823a4*.html", - ), + (("filters", None), ("inherited_members", True), ("members", False)): external("3f5d794823a4*.html"), ( ("filters", ()), ("inherited_members", True), @@ -317,7 +281,7 @@ ("filters", ("module_attribute",)), ("inherited_members", False), ("members", True), - ): external("052e71e7e9d5*.html"), + ): external("cd3e45851714*.html"), ( ("filters", None), ("inherited_members", ("method1",)), @@ -327,7 +291,7 @@ ("filters", ("!module_attribute",)), ("inherited_members", ()), ("members", True), - ): external("b4b490164ab1*.html"), + ): external("84193b3c9f5d*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", ("method1",)), @@ -342,21 +306,15 @@ ("filters", ("!module_attribute",)), ("inherited_members", ()), ("members", None), - ): external("728c13446301*.html"), - (("filters", None), ("inherited_members", ()), ("members", ())): external( - "f77f1c850398*.html", - ), + ): external("3935bcf6d71b*.html"), + (("filters", None), ("inherited_members", ()), ("members", ())): external("f77f1c850398*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", False), ("members", True), - ): external("0fac4f5e7f45*.html"), - (("filters", None), ("inherited_members", True), ("members", None)): external( - "cc19537fdba4*.html", - ), - (("filters", ()), ("inherited_members", ()), ("members", None)): external( - "e6a9b76f268c*.html", - ), + ): external("fe25ab760039*.html"), + (("filters", None), ("inherited_members", True), ("members", None)): external("ea914f1afa9d*.html"), + (("filters", ()), ("inherited_members", ()), ("members", None)): external("19f98a747c01*.html"), ( ("filters", ("!module_attribute",)), ("inherited_members", ()), @@ -366,36 +324,30 @@ ("filters", ("!module_attribute",)), ("inherited_members", ("method1",)), ("members", True), - ): external("0c2924ff976f*.html"), + ): external("9720526cf5e4*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ()), ("members", ("module_attribute",)), ): external("f6e292b8358a*.html"), - (("filters", ()), ("inherited_members", True), ("members", False)): external( - "b0a9b08f1f72*.html", - ), - (("filters", ()), ("inherited_members", ()), ("members", True)): external( - "fb65efbbfc3e*.html", - ), + (("filters", ()), ("inherited_members", True), ("members", False)): external("b0a9b08f1f72*.html"), + (("filters", ()), ("inherited_members", ()), ("members", True)): external("027ef7afeffc*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", False), ("members", False), ): external("710706687213*.html"), - (("filters", ()), ("inherited_members", ()), ("members", ())): external( - "11598fec2d07*.html", - ), + (("filters", ()), ("inherited_members", ()), ("members", ())): external("11598fec2d07*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ("method1",)), ("members", True), - ): external("a1167b14f5a7*.html"), + ): external("e8608b0de174*.html"), ( ("filters", ()), ("inherited_members", ("method1",)), ("members", None), - ): external("f848d4a9e516*.html"), + ): external("e5dc372374af*.html"), ( ("filters", ("module_attribute",)), ("inherited_members", ()), diff --git a/tests/snapshots/external/fb65efbbfc3ef9c2a06e6f539f8a75bec4276e61254539632a1d5f8f2c6c3452.html b/tests/snapshots/external/027ef7afeffc56219a09298c7db30f473c4dfdda12d99a171e9c76098c316067.html similarity index 97% rename from tests/snapshots/external/fb65efbbfc3ef9c2a06e6f539f8a75bec4276e61254539632a1d5f8f2c6c3452.html rename to tests/snapshots/external/027ef7afeffc56219a09298c7db30f473c4dfdda12d99a171e9c76098c316067.html index 3cec9af8..ccfa8d64 100644 --- a/tests/snapshots/external/fb65efbbfc3ef9c2a06e6f539f8a75bec4276e61254539632a1d5f8f2c6c3452.html +++ b/tests/snapshots/external/027ef7afeffc56219a09298c7db30f473c4dfdda12d99a171e9c76098c316067.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/f7711b8af7689b331209f8c034c8cc3a2ec894372644a8eaee597418e9b55b3c.html b/tests/snapshots/external/0f046dea611f6c9e90b8eaed720f22af372394971808e2a5d1b3a12286f1ec76.html similarity index 97% rename from tests/snapshots/external/f7711b8af7689b331209f8c034c8cc3a2ec894372644a8eaee597418e9b55b3c.html rename to tests/snapshots/external/0f046dea611f6c9e90b8eaed720f22af372394971808e2a5d1b3a12286f1ec76.html index 522fd1c1..332a5c53 100644 --- a/tests/snapshots/external/f7711b8af7689b331209f8c034c8cc3a2ec894372644a8eaee597418e9b55b3c.html +++ b/tests/snapshots/external/0f046dea611f6c9e90b8eaed720f22af372394971808e2a5d1b3a12286f1ec76.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/46e56f39b10d1e8ee4017bc11457bf76d169fc80b3d3e465213671b7f6e548eb.html b/tests/snapshots/external/13334b5b4fcf7267539b9eb99ca2ab79c66766ec6f35383f4bfcb6a8d9e2a116.html similarity index 94% rename from tests/snapshots/external/46e56f39b10d1e8ee4017bc11457bf76d169fc80b3d3e465213671b7f6e548eb.html rename to tests/snapshots/external/13334b5b4fcf7267539b9eb99ca2ab79c66766ec6f35383f4bfcb6a8d9e2a116.html index 36f35fb4..a7eb7dce 100644 --- a/tests/snapshots/external/46e56f39b10d1e8ee4017bc11457bf76d169fc80b3d3e465213671b7f6e548eb.html +++ b/tests/snapshots/external/13334b5b4fcf7267539b9eb99ca2ab79c66766ec6f35383f4bfcb6a8d9e2a116.html @@ -78,7 +78,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/981438492e387bc82b23f09e3c5e0b452db5a1ffd88e52479db2b52a170fd8f9.html b/tests/snapshots/external/14bca0e5703be9cab876200d88cccd1d728d1bdfef7cbfac751af212e00a8663.html similarity index 98% rename from tests/snapshots/external/981438492e387bc82b23f09e3c5e0b452db5a1ffd88e52479db2b52a170fd8f9.html rename to tests/snapshots/external/14bca0e5703be9cab876200d88cccd1d728d1bdfef7cbfac751af212e00a8663.html index 574ec87c..b2490df7 100644 --- a/tests/snapshots/external/981438492e387bc82b23f09e3c5e0b452db5a1ffd88e52479db2b52a170fd8f9.html +++ b/tests/snapshots/external/14bca0e5703be9cab876200d88cccd1d728d1bdfef7cbfac751af212e00a8663.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/e6a9b76f268cde81a129e7273038db0ff3fcd73530442a30c48cf01dcbc30aaa.html b/tests/snapshots/external/19f98a747c015a074f3d3362d03ed72f9da9db3aefe969a0d78c4052e7594372.html similarity index 97% rename from tests/snapshots/external/e6a9b76f268cde81a129e7273038db0ff3fcd73530442a30c48cf01dcbc30aaa.html rename to tests/snapshots/external/19f98a747c015a074f3d3362d03ed72f9da9db3aefe969a0d78c4052e7594372.html index dbcf8c57..9058ed13 100644 --- a/tests/snapshots/external/e6a9b76f268cde81a129e7273038db0ff3fcd73530442a30c48cf01dcbc30aaa.html +++ b/tests/snapshots/external/19f98a747c015a074f3d3362d03ed72f9da9db3aefe969a0d78c4052e7594372.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/955e5111f4262f280b0787a22dfa46c9ea93c80bc49e1a1de100349341d93fb9.html b/tests/snapshots/external/261a38d7a86b5e959b6d1c165108301963d0170c2189d5aa18bb7c7eade84ea4.html similarity index 64% rename from tests/snapshots/external/955e5111f4262f280b0787a22dfa46c9ea93c80bc49e1a1de100349341d93fb9.html rename to tests/snapshots/external/261a38d7a86b5e959b6d1c165108301963d0170c2189d5aa18bb7c7eade84ea4.html index ee00ece7..2170787f 100644 --- a/tests/snapshots/external/955e5111f4262f280b0787a22dfa46c9ea93c80bc49e1a1de100349341d93fb9.html +++ b/tests/snapshots/external/261a38d7a86b5e959b6d1c165108301963d0170c2189d5aa18bb7c7eade84ea4.html @@ -36,7 +36,7 @@

    -
    __init__(a: int, b: str) -> None
    +        
    __init__(a: int, b: str) -> None
     
    @@ -56,7 +56,7 @@

    -
    method1(a: int, b: str) -> None
    +        
    method1(a: int, b: str) -> None
     
    @@ -79,7 +79,7 @@

    -
    module_function(a: int, b: str) -> None
    +     
    module_function(a: int, b: str) -> None
     
    diff --git a/tests/snapshots/external/7c988c9e13efeadd20b911a95cc69973b715cceacdadefc540109aad3c274bde.html b/tests/snapshots/external/34b16654e7baa8e16315cef2919f2eafa51ba39ec28c4c970fe7ea8e2c79f9d2.html similarity index 98% rename from tests/snapshots/external/7c988c9e13efeadd20b911a95cc69973b715cceacdadefc540109aad3c274bde.html rename to tests/snapshots/external/34b16654e7baa8e16315cef2919f2eafa51ba39ec28c4c970fe7ea8e2c79f9d2.html index 10b98188..8357168e 100644 --- a/tests/snapshots/external/7c988c9e13efeadd20b911a95cc69973b715cceacdadefc540109aad3c274bde.html +++ b/tests/snapshots/external/34b16654e7baa8e16315cef2919f2eafa51ba39ec28c4c970fe7ea8e2c79f9d2.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/728c1344630190ac84514ebd8e5ae2d95ba8685e16d2c79749f675b5b2a6cea5.html b/tests/snapshots/external/3935bcf6d71b58daa0e4512cbf3f53e19516885fb65d0bd760c12aadd021507f.html similarity index 97% rename from tests/snapshots/external/728c1344630190ac84514ebd8e5ae2d95ba8685e16d2c79749f675b5b2a6cea5.html rename to tests/snapshots/external/3935bcf6d71b58daa0e4512cbf3f53e19516885fb65d0bd760c12aadd021507f.html index d6ac202a..793f43a4 100644 --- a/tests/snapshots/external/728c1344630190ac84514ebd8e5ae2d95ba8685e16d2c79749f675b5b2a6cea5.html +++ b/tests/snapshots/external/3935bcf6d71b58daa0e4512cbf3f53e19516885fb65d0bd760c12aadd021507f.html @@ -235,7 +235,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/052c34f22e4c711b1f13f53085cdd5e8edcfae4bdc1d8cb7f2ff76cd1c46cce5.html b/tests/snapshots/external/3c21330afd6529769164afe388e9385a9fddb3ae628124965e0c7b81932a0c63.html similarity index 94% rename from tests/snapshots/external/052c34f22e4c711b1f13f53085cdd5e8edcfae4bdc1d8cb7f2ff76cd1c46cce5.html rename to tests/snapshots/external/3c21330afd6529769164afe388e9385a9fddb3ae628124965e0c7b81932a0c63.html index e1a7d15c..26cb9e39 100644 --- a/tests/snapshots/external/052c34f22e4c711b1f13f53085cdd5e8edcfae4bdc1d8cb7f2ff76cd1c46cce5.html +++ b/tests/snapshots/external/3c21330afd6529769164afe388e9385a9fddb3ae628124965e0c7b81932a0c63.html @@ -78,7 +78,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/ae74b5980f9b6996ed6e112d53168fde16c32d92bed42fb3193f98e0e3f04602.html b/tests/snapshots/external/43d819f94dc7cafe9ed60ce604bab9a938f42a115dc534cb72d12e15e998e96d.html similarity index 98% rename from tests/snapshots/external/ae74b5980f9b6996ed6e112d53168fde16c32d92bed42fb3193f98e0e3f04602.html rename to tests/snapshots/external/43d819f94dc7cafe9ed60ce604bab9a938f42a115dc534cb72d12e15e998e96d.html index 34123ecf..ddce47f6 100644 --- a/tests/snapshots/external/ae74b5980f9b6996ed6e112d53168fde16c32d92bed42fb3193f98e0e3f04602.html +++ b/tests/snapshots/external/43d819f94dc7cafe9ed60ce604bab9a938f42a115dc534cb72d12e15e998e96d.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/e8be7a9b1410e40dac79fe0ee29d3036e707a177b2ba2bdad25a6998bec570b7.html b/tests/snapshots/external/722165bce3ada19df43b169ea982ab4908d94cd1bf19b777e1e6bc22e8aa02a5.html similarity index 98% rename from tests/snapshots/external/e8be7a9b1410e40dac79fe0ee29d3036e707a177b2ba2bdad25a6998bec570b7.html rename to tests/snapshots/external/722165bce3ada19df43b169ea982ab4908d94cd1bf19b777e1e6bc22e8aa02a5.html index 3dbd9879..7c65c72b 100644 --- a/tests/snapshots/external/e8be7a9b1410e40dac79fe0ee29d3036e707a177b2ba2bdad25a6998bec570b7.html +++ b/tests/snapshots/external/722165bce3ada19df43b169ea982ab4908d94cd1bf19b777e1e6bc22e8aa02a5.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/7d5fe66539191786245991395e77d8ba0bbb22330cb08eaec2e84159bde4159b.html b/tests/snapshots/external/75b69b702f3b5fa3bc0d30091297b0a09a8915eb7f0e1f7be1ce99f5d59d9514.html similarity index 98% rename from tests/snapshots/external/7d5fe66539191786245991395e77d8ba0bbb22330cb08eaec2e84159bde4159b.html rename to tests/snapshots/external/75b69b702f3b5fa3bc0d30091297b0a09a8915eb7f0e1f7be1ce99f5d59d9514.html index 23e38eeb..fa970eca 100644 --- a/tests/snapshots/external/7d5fe66539191786245991395e77d8ba0bbb22330cb08eaec2e84159bde4159b.html +++ b/tests/snapshots/external/75b69b702f3b5fa3bc0d30091297b0a09a8915eb7f0e1f7be1ce99f5d59d9514.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/b4b490164ab1a724cac7aba25bbc69a33e7dd44500e9337718cd96da1bb56325.html b/tests/snapshots/external/84193b3c9f5d84fef33daa61fb61aa9a3e66171d312de8d7f836c69f0bc069b0.html similarity index 97% rename from tests/snapshots/external/b4b490164ab1a724cac7aba25bbc69a33e7dd44500e9337718cd96da1bb56325.html rename to tests/snapshots/external/84193b3c9f5d84fef33daa61fb61aa9a3e66171d312de8d7f836c69f0bc069b0.html index 9f5cbef9..07120341 100644 --- a/tests/snapshots/external/b4b490164ab1a724cac7aba25bbc69a33e7dd44500e9337718cd96da1bb56325.html +++ b/tests/snapshots/external/84193b3c9f5d84fef33daa61fb61aa9a3e66171d312de8d7f836c69f0bc069b0.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/ba51e100acd4f6ad91f1ef484aa5f1bd537e661588b1742d93d0a6543cc3592c.html b/tests/snapshots/external/8733f7fb7b6d28b15bbe736f29c7fd030467c0ccfa2cbc6a68616e06c6dc6a9b.html similarity index 98% rename from tests/snapshots/external/ba51e100acd4f6ad91f1ef484aa5f1bd537e661588b1742d93d0a6543cc3592c.html rename to tests/snapshots/external/8733f7fb7b6d28b15bbe736f29c7fd030467c0ccfa2cbc6a68616e06c6dc6a9b.html index b18eb50e..cb43eee6 100644 --- a/tests/snapshots/external/ba51e100acd4f6ad91f1ef484aa5f1bd537e661588b1742d93d0a6543cc3592c.html +++ b/tests/snapshots/external/8733f7fb7b6d28b15bbe736f29c7fd030467c0ccfa2cbc6a68616e06c6dc6a9b.html @@ -235,7 +235,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/0c2924ff976fa0e32ba66558a4f9e1eff4cd66196506a37977cdb33325a50718.html b/tests/snapshots/external/9720526cf5e4c44f27695c59764bb1e05e428834744442f43527ebf2b8acfb35.html similarity index 98% rename from tests/snapshots/external/0c2924ff976fa0e32ba66558a4f9e1eff4cd66196506a37977cdb33325a50718.html rename to tests/snapshots/external/9720526cf5e4c44f27695c59764bb1e05e428834744442f43527ebf2b8acfb35.html index 5fb3da58..6d460dd2 100644 --- a/tests/snapshots/external/0c2924ff976fa0e32ba66558a4f9e1eff4cd66196506a37977cdb33325a50718.html +++ b/tests/snapshots/external/9720526cf5e4c44f27695c59764bb1e05e428834744442f43527ebf2b8acfb35.html @@ -268,7 +268,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/0b1372d7f7c057905f665ad506f3dd3bee62fb9b1c8b2a39991550e7845c2b02.html b/tests/snapshots/external/c0f102dbd7d4de76de40c06a8205a642465f5fde9a37b4b969aa01f161ef25a4.html similarity index 98% rename from tests/snapshots/external/0b1372d7f7c057905f665ad506f3dd3bee62fb9b1c8b2a39991550e7845c2b02.html rename to tests/snapshots/external/c0f102dbd7d4de76de40c06a8205a642465f5fde9a37b4b969aa01f161ef25a4.html index 89a3ea1e..e9f375b2 100644 --- a/tests/snapshots/external/0b1372d7f7c057905f665ad506f3dd3bee62fb9b1c8b2a39991550e7845c2b02.html +++ b/tests/snapshots/external/c0f102dbd7d4de76de40c06a8205a642465f5fde9a37b4b969aa01f161ef25a4.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/052e71e7e9d5bec710fb2d36b009122c48eca0a19d0611df530e607f5bacdf6f.html b/tests/snapshots/external/cd3e458517147c43c360525140aa1b9a81682634aaf2674ffd4cceb7fc44aba6.html similarity index 94% rename from tests/snapshots/external/052e71e7e9d5bec710fb2d36b009122c48eca0a19d0611df530e607f5bacdf6f.html rename to tests/snapshots/external/cd3e458517147c43c360525140aa1b9a81682634aaf2674ffd4cceb7fc44aba6.html index 6866b45f..e196b599 100644 --- a/tests/snapshots/external/052e71e7e9d5bec710fb2d36b009122c48eca0a19d0611df530e607f5bacdf6f.html +++ b/tests/snapshots/external/cd3e458517147c43c360525140aa1b9a81682634aaf2674ffd4cceb7fc44aba6.html @@ -78,7 +78,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/d540895f6bf91c8c8e4abc02f40529a61c6cec71b18da2e4f02206ec18b901ef.html b/tests/snapshots/external/cd51e40cc0ddf1d42b7c6bf7560ead2501370ee9d67499b74afc83e258caff8e.html similarity index 98% rename from tests/snapshots/external/d540895f6bf91c8c8e4abc02f40529a61c6cec71b18da2e4f02206ec18b901ef.html rename to tests/snapshots/external/cd51e40cc0ddf1d42b7c6bf7560ead2501370ee9d67499b74afc83e258caff8e.html index 2ad1c277..a52964c3 100644 --- a/tests/snapshots/external/d540895f6bf91c8c8e4abc02f40529a61c6cec71b18da2e4f02206ec18b901ef.html +++ b/tests/snapshots/external/cd51e40cc0ddf1d42b7c6bf7560ead2501370ee9d67499b74afc83e258caff8e.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/cdc8126d78b690d11c09e3128df0f8d65379375a6bd390da30f5676bf2289cf2.html b/tests/snapshots/external/d556527026068280df9b77db277472320842cb1ae6099ac3cf558031afda6d2e.html similarity index 97% rename from tests/snapshots/external/cdc8126d78b690d11c09e3128df0f8d65379375a6bd390da30f5676bf2289cf2.html rename to tests/snapshots/external/d556527026068280df9b77db277472320842cb1ae6099ac3cf558031afda6d2e.html index 158c1ca5..5a87832a 100644 --- a/tests/snapshots/external/cdc8126d78b690d11c09e3128df0f8d65379375a6bd390da30f5676bf2289cf2.html +++ b/tests/snapshots/external/d556527026068280df9b77db277472320842cb1ae6099ac3cf558031afda6d2e.html @@ -235,7 +235,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/347d4ffe2cb3f2ca3f0d1f3f09cffa96645eb2af29983e75d807fccff96d8f75.html b/tests/snapshots/external/dcf34c2f72697f7a4700e4a1f048d601f374eab35eea68c9beb8bab8fc269aed.html similarity index 97% rename from tests/snapshots/external/347d4ffe2cb3f2ca3f0d1f3f09cffa96645eb2af29983e75d807fccff96d8f75.html rename to tests/snapshots/external/dcf34c2f72697f7a4700e4a1f048d601f374eab35eea68c9beb8bab8fc269aed.html index 9cd4b2fe..2d79edd5 100644 --- a/tests/snapshots/external/347d4ffe2cb3f2ca3f0d1f3f09cffa96645eb2af29983e75d807fccff96d8f75.html +++ b/tests/snapshots/external/dcf34c2f72697f7a4700e4a1f048d601f374eab35eea68c9beb8bab8fc269aed.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/f848d4a9e516beeb1b1719630e34aa243a093ccd362a63e33dbd6202ae8ab75d.html b/tests/snapshots/external/e5dc372374af6f90a5d456d8683aacdf81104137ce91bd6d4121827f8d989d96.html similarity index 98% rename from tests/snapshots/external/f848d4a9e516beeb1b1719630e34aa243a093ccd362a63e33dbd6202ae8ab75d.html rename to tests/snapshots/external/e5dc372374af6f90a5d456d8683aacdf81104137ce91bd6d4121827f8d989d96.html index 19c29b39..d9ae307d 100644 --- a/tests/snapshots/external/f848d4a9e516beeb1b1719630e34aa243a093ccd362a63e33dbd6202ae8ab75d.html +++ b/tests/snapshots/external/e5dc372374af6f90a5d456d8683aacdf81104137ce91bd6d4121827f8d989d96.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/a1167b14f5a71a283817bf5866d2bb0bd08bf23dc054c6f7938a04f42feab99d.html b/tests/snapshots/external/e8608b0de174402ca18f88ed58849312158c22f5bfdc845d2da02055fe14853c.html similarity index 95% rename from tests/snapshots/external/a1167b14f5a71a283817bf5866d2bb0bd08bf23dc054c6f7938a04f42feab99d.html rename to tests/snapshots/external/e8608b0de174402ca18f88ed58849312158c22f5bfdc845d2da02055fe14853c.html index bb9001d8..795378be 100644 --- a/tests/snapshots/external/a1167b14f5a71a283817bf5866d2bb0bd08bf23dc054c6f7938a04f42feab99d.html +++ b/tests/snapshots/external/e8608b0de174402ca18f88ed58849312158c22f5bfdc845d2da02055fe14853c.html @@ -80,7 +80,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/cc19537fdba4a26b10c60d5586b0eb7ef0264a783a3c47d1114d21fa8cfa3947.html b/tests/snapshots/external/ea914f1afa9de4b5eddc9792c2b6a5d8de367274278976092bb824e99e523ca5.html similarity index 98% rename from tests/snapshots/external/cc19537fdba4a26b10c60d5586b0eb7ef0264a783a3c47d1114d21fa8cfa3947.html rename to tests/snapshots/external/ea914f1afa9de4b5eddc9792c2b6a5d8de367274278976092bb824e99e523ca5.html index f93ae024..68c7d720 100644 --- a/tests/snapshots/external/cc19537fdba4a26b10c60d5586b0eb7ef0264a783a3c47d1114d21fa8cfa3947.html +++ b/tests/snapshots/external/ea914f1afa9de4b5eddc9792c2b6a5d8de367274278976092bb824e99e523ca5.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/a2c5be9bd5d1f0db3ff64b44353c1760f5eb69d7db6401da2f28518d0e8065c4.html b/tests/snapshots/external/eac5bee59a9ee0a64602fd6bb8f4f54cb5f3543aa321169921326288a61f556c.html similarity index 97% rename from tests/snapshots/external/a2c5be9bd5d1f0db3ff64b44353c1760f5eb69d7db6401da2f28518d0e8065c4.html rename to tests/snapshots/external/eac5bee59a9ee0a64602fd6bb8f4f54cb5f3543aa321169921326288a61f556c.html index 4738a584..7c90168c 100644 --- a/tests/snapshots/external/a2c5be9bd5d1f0db3ff64b44353c1760f5eb69d7db6401da2f28518d0e8065c4.html +++ b/tests/snapshots/external/eac5bee59a9ee0a64602fd6bb8f4f54cb5f3543aa321169921326288a61f556c.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/83119803338105f101311992d31947e4fcaf2c5a6c68cad6355d8611c1cc2e3f.html b/tests/snapshots/external/f4150843096a1371b097478f8d67062e3d45ab9f6a8f97e79ae62d32abc5e22a.html similarity index 97% rename from tests/snapshots/external/83119803338105f101311992d31947e4fcaf2c5a6c68cad6355d8611c1cc2e3f.html rename to tests/snapshots/external/f4150843096a1371b097478f8d67062e3d45ab9f6a8f97e79ae62d32abc5e22a.html index c9c637e4..cce2be49 100644 --- a/tests/snapshots/external/83119803338105f101311992d31947e4fcaf2c5a6c68cad6355d8611c1cc2e3f.html +++ b/tests/snapshots/external/f4150843096a1371b097478f8d67062e3d45ab9f6a8f97e79ae62d32abc5e22a.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/59a9e1ffb2f0807b594a933444c78753a06f359527ea4adac85c72a7812b21d3.html b/tests/snapshots/external/fca72854c849dc68c3ad072a41c32f926f95c6e88775f3e2eeaa63138d99837c.html similarity index 97% rename from tests/snapshots/external/59a9e1ffb2f0807b594a933444c78753a06f359527ea4adac85c72a7812b21d3.html rename to tests/snapshots/external/fca72854c849dc68c3ad072a41c32f926f95c6e88775f3e2eeaa63138d99837c.html index ad60041c..d6c51063 100644 --- a/tests/snapshots/external/59a9e1ffb2f0807b594a933444c78753a06f359527ea4adac85c72a7812b21d3.html +++ b/tests/snapshots/external/fca72854c849dc68c3ad072a41c32f926f95c6e88775f3e2eeaa63138d99837c.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/88855b0284174733b57edd2043e0e8cd6a1a0223055f08b80031452eb05d9484.html b/tests/snapshots/external/fd291f98ca28b8f15b5a8ed6a2608bacf5b5322599bcbf0544ef8e9c0a27870b.html similarity index 97% rename from tests/snapshots/external/88855b0284174733b57edd2043e0e8cd6a1a0223055f08b80031452eb05d9484.html rename to tests/snapshots/external/fd291f98ca28b8f15b5a8ed6a2608bacf5b5322599bcbf0544ef8e9c0a27870b.html index 540a2f6a..91614cf0 100644 --- a/tests/snapshots/external/88855b0284174733b57edd2043e0e8cd6a1a0223055f08b80031452eb05d9484.html +++ b/tests/snapshots/external/fd291f98ca28b8f15b5a8ed6a2608bacf5b5322599bcbf0544ef8e9c0a27870b.html @@ -264,7 +264,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/3d072a22b9513eecb51c6a5f39b978c1c1d3ef56a572031a307fe1cad1f17eff.html b/tests/snapshots/external/fe1cd23642d405d0b2a4d29ec4a2125f55b54f90c2440ee2d856540415e77745.html similarity index 97% rename from tests/snapshots/external/3d072a22b9513eecb51c6a5f39b978c1c1d3ef56a572031a307fe1cad1f17eff.html rename to tests/snapshots/external/fe1cd23642d405d0b2a4d29ec4a2125f55b54f90c2440ee2d856540415e77745.html index f950f69b..dce4e148 100644 --- a/tests/snapshots/external/3d072a22b9513eecb51c6a5f39b978c1c1d3ef56a572031a307fe1cad1f17eff.html +++ b/tests/snapshots/external/fe1cd23642d405d0b2a4d29ec4a2125f55b54f90c2440ee2d856540415e77745.html @@ -237,7 +237,7 @@

    Bases: - + Class diff --git a/tests/snapshots/external/0fac4f5e7f455b351c60268567bfcbd0259b652d0534259efea7815aa15b1122.html b/tests/snapshots/external/fe25ab7600392b4fd3a1438fb54337041719faac884123527bab9a92e3a51be5.html similarity index 97% rename from tests/snapshots/external/0fac4f5e7f455b351c60268567bfcbd0259b652d0534259efea7815aa15b1122.html rename to tests/snapshots/external/fe25ab7600392b4fd3a1438fb54337041719faac884123527bab9a92e3a51be5.html index 47cfb56f..40ebfa36 100644 --- a/tests/snapshots/external/0fac4f5e7f455b351c60268567bfcbd0259b652d0534259efea7815aa15b1122.html +++ b/tests/snapshots/external/fe25ab7600392b4fd3a1438fb54337041719faac884123527bab9a92e3a51be5.html @@ -266,7 +266,7 @@

    Bases: - + Class diff --git a/tests/test_handler.py b/tests/test_handler.py index 4d8b4f3d..1a907469 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -263,7 +263,7 @@ def test_deduplicate_summary_sections(handler: PythonHandler, section: str, code """Assert summary sections are deduplicated.""" summary_section = section.lower() summary_section = "functions" if summary_section == "methods" else summary_section - with temporary_visited_module(code, docstring_parser="google") as module: # type: ignore[arg-type] + with temporary_visited_module(code, docstring_parser="google") as module: if summary_section == "modules": module.set_member("a", Module("A", docstring=Docstring("A."))) module.set_member("b", Module("B", docstring=Docstring("B."))) From b094406506c7634ccefc3408d0044d66e4835452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Mar 2025 19:02:30 +0100 Subject: [PATCH 16/53] chore: Prepare release 1.16.5 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff775169..ec6a2c4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,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.16.5](https://github.com/mkdocstrings/python/releases/tag/1.16.5) - 2025-03-10 + +[Compare with 1.16.4](https://github.com/mkdocstrings/python/compare/1.16.4...1.16.5) + +### Code Refactoring + +- Prepare backlinks support ([56bf627](https://github.com/mkdocstrings/python/commit/56bf627b9483a12228b769ae4690b84733061ea5) by Timothée Mazzucotelli). [Issue-153](https://github.com/mkdocstrings/python/issues/153), [PR-252](https://github.com/mkdocstrings/python/pull/252) + ## [1.16.4](https://github.com/mkdocstrings/python/releases/tag/1.16.4) - 2025-03-10 [Compare with 1.16.3](https://github.com/mkdocstrings/python/compare/1.16.3...1.16.4) From c8417f45845c678a8346461f868cb034280ff522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Mar 2025 19:05:07 +0100 Subject: [PATCH 17/53] docs: Rename import to inventories in mkdocs.yml --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index ae518a4c..516a0613 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -153,7 +153,7 @@ plugins: handlers: python: paths: [src, docs/snippets] - import: + inventories: - https://docs.python.org/3/objects.inv - https://mkdocstrings.github.io/objects.inv - https://mkdocstrings.github.io/autorefs/objects.inv From bb187573ed33d41ee94fa2afd8de479d3215fb07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 10 Mar 2025 22:16:31 +0100 Subject: [PATCH 18/53] docs: Add backlink screenshots --- docs/usage/configuration/general.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/usage/configuration/general.md b/docs/usage/configuration/general.md index 77b3f3f9..973658c1 100644 --- a/docs/usage/configuration/general.md +++ b/docs/usage/configuration/general.md @@ -90,7 +90,17 @@ plugins: backlinks: tree ``` - +/// admonition | Preview + type: preview + +//// tab | Flat +![backlinks-flat](https://github.com/user-attachments/assets/f7a15b01-f194-4c55-8281-50adbb4a74af) +//// + +//// tab | Tree +![backlinks-tree](https://github.com/user-attachments/assets/3457db21-45e1-4e03-bd8c-2e9e75dc778b) +//// +/// [](){#option-extensions} ## `extensions` From 138562ccfaae0223802d956c54c5b19b084f95f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 12 Mar 2025 15:11:31 +0100 Subject: [PATCH 19/53] chore: Template upgrade --- .copier-answers.yml | 6 +- .github/FUNDING.yml | 3 - .github/ISSUE_TEMPLATE/1-bug.md | 2 +- .github/workflows/ci.yml | 22 ++- .github/workflows/release.yml | 2 +- CODE_OF_CONDUCT.md | 99 +++------- CONTRIBUTING.md | 54 ++---- config/ruff.toml | 11 +- config/vscode/launch.json | 12 +- docs/.overrides/partials/path-item.html | 22 +++ docs/changelog.md | 4 + docs/code_of_conduct.md | 4 + docs/contributing.md | 4 + docs/credits.md | 3 +- docs/index.md | 1 + docs/insiders/index.md | 147 ++++----------- docs/insiders/installation.md | 57 ++---- docs/license.md | 1 + docs/reference/api.md | 7 + docs/usage/customization.md | 4 +- duties.py | 39 +++- mkdocs.yml | 34 ++-- pyproject.toml | 25 +-- scripts/gen_credits.py | 4 +- scripts/gen_ref_nav.py | 37 ---- scripts/get_version.py | 3 +- scripts/griffe_extensions.py | 8 +- scripts/insiders.py | 39 +--- scripts/make.py | 2 - scripts/mkdocs_hooks.py | 20 +- src/mkdocstrings_handlers/python/__init__.py | 17 +- .../python/_internal/__init__.py | 0 .../python/{ => _internal}/config.py | 4 +- .../python/{ => _internal}/debug.py | 30 ++- .../python/{ => _internal}/handler.py | 8 +- .../python/{ => _internal}/rendering.py | 0 tests/conftest.py | 2 +- tests/test_api.py | 178 ++++++++++++++++++ 38 files changed, 476 insertions(+), 439 deletions(-) create mode 100644 docs/.overrides/partials/path-item.html create mode 100644 docs/reference/api.md delete mode 100644 scripts/gen_ref_nav.py create mode 100644 src/mkdocstrings_handlers/python/_internal/__init__.py rename src/mkdocstrings_handlers/python/{ => _internal}/config.py (99%) rename src/mkdocstrings_handlers/python/{ => _internal}/debug.py (80%) rename src/mkdocstrings_handlers/python/{ => _internal}/handler.py (98%) rename src/mkdocstrings_handlers/python/{ => _internal}/rendering.py (100%) create mode 100644 tests/test_api.py diff --git a/.copier-answers.yml b/.copier-answers.yml index a7f91ade..b2cee68e 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ -# Changes here will be overwritten by Copier -_commit: 1.2.3 +# Changes here will be overwritten by Copier. +_commit: 1.4.0 _src_path: gh:mkdocstrings/handler-template author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli @@ -7,7 +7,7 @@ author_username: pawamoy copyright_date: '2021' copyright_holder: Timothée Mazzucotelli copyright_holder_email: dev@pawamoy.fr -copyright_license: ISC License +copyright_license: ISC insiders: true insiders_email: insiders@pawamoy.fr insiders_repository_name: mkdocstrings-python diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a502284a..812789e6 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,5 +1,2 @@ github: pawamoy -ko_fi: pawamoy polar: pawamoy -custom: -- https://www.paypal.me/pawamoy diff --git a/.github/ISSUE_TEMPLATE/1-bug.md b/.github/ISSUE_TEMPLATE/1-bug.md index 0df6e967..a0e35e01 100644 --- a/.github/ISSUE_TEMPLATE/1-bug.md +++ b/.github/ISSUE_TEMPLATE/1-bug.md @@ -50,7 +50,7 @@ PASTE TRACEBACK HERE redacting sensitive information. --> ```bash -python -m mkdocstrings_handlers.python.debug # | xclip -selection clipboard +python -m mkdocstrings_handlers.python._internal.debug # | xclip -selection clipboard ``` PASTE MARKDOWN OUTPUT HERE diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 594d1c42..32767fde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: python-version: "3.12" - name: Setup uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: pyproject.toml @@ -55,6 +55,12 @@ jobs: - name: Check for breaking changes in the API run: make check-api + - name: Store objects inventory for tests + uses: actions/upload-artifact@v4 + with: + name: objects.inv + path: site/objects.inv + exclude-test-jobs: runs-on: ubuntu-latest outputs: @@ -81,7 +87,9 @@ jobs: tests: - needs: exclude-test-jobs + needs: + - quality + - exclude-test-jobs strategy: max-parallel: 4 matrix: @@ -117,16 +125,22 @@ jobs: allow-prereleases: true - name: Setup uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v5 with: enable-cache: true cache-dependency-glob: pyproject.toml - cache-suffix: py${{ matrix.python-version }} + cache-suffix: ${{ matrix.resolution }} - name: Install dependencies env: UV_RESOLUTION: ${{ matrix.resolution }} run: make setup + - name: Download objects inventory + uses: actions/download-artifact@v4 + with: + name: objects.inv + path: site/ + - name: Run the test suite run: make test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9388125b..3ef68f27 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: with: python-version: "3.12" - name: Setup uv - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v5 - name: Build dists if: github.repository_owner == 'pawamoy-insiders' run: uv tool run --from build pyproject-build diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 255e0eed..2d46305a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,128 +2,79 @@ ## Our Pledge -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contributes to a positive environment for our -community include: +Examples of behavior that contributes to a positive environment for our community include: * Demonstrating empathy and kindness toward other people * Being respectful of differing opinions, viewpoints, and experiences * Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the overall - community +* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or advances of - any kind +* The use of sexualized language or imagery, and sexual attention or advances of any kind * Trolling, insulting or derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or email address, - without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +* Publishing others' private information, such as a physical or email address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. +Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -dev@pawamoy.fr. -All complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at dev@pawamoy.fr. All complaints will be reviewed and investigated promptly and fairly. -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. +All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: +Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. +**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. +**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning -**Community Impact**: A violation through a single incident or series of -actions. +**Community Impact**: A violation through a single incident or series of actions. -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. +**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. +**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. +**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. +**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. -**Consequence**: A permanent ban from any sort of public interaction within the -community. +**Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. +Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. +For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e3dc294..920ae3a9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,6 @@ # Contributing -Contributions are welcome, and they are greatly appreciated! -Every little bit helps, and credit will always be given. +Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. ## Environment setup @@ -14,11 +13,7 @@ cd python make setup ``` -> NOTE: -> If it fails for some reason, -> you'll need to install -> [uv](https://github.com/astral-sh/uv) -> manually. +> NOTE: If it fails for some reason, you'll need to install [uv](https://github.com/astral-sh/uv) manually. > > You can install it with: > @@ -26,8 +21,7 @@ make setup > curl -LsSf https://astral.sh/uv/install.sh | sh > ``` > -> Now you can try running `make setup` again, -> or simply `uv sync`. +> Now you can try running `make setup` again, or simply `uv sync`. You now have the dependencies installed. @@ -35,15 +29,10 @@ 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. +If you work in VSCode, we provide [an action to configure VSCode](https://pawamoy.github.io/copier-uv/work/#vscode-setup) for the project. ## Development @@ -62,17 +51,13 @@ As usual: 1. go to http://localhost:8000 and check that everything looks good 1. follow our [commit message convention](#commit-message-convention) -If you are unsure about how to fix or ignore a warning, -just let the continuous integration fail, -and we will help you during review. +If you are unsure about how to fix or ignore a warning, just let the continuous integration fail, and we will help you during review. Don't bother updating the changelog, we will take care of this. ## Commit message convention -Commit messages must follow our convention based on the -[Angular style](https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message) -or the [Karma convention](https://karma-runner.github.io/4.0/dev/git-commit-msg.html): +Commit messages must follow our convention based on the [Angular style](https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message) or the [Karma convention](https://karma-runner.github.io/4.0/dev/git-commit-msg.html): ``` [(scope)]: Subject @@ -80,10 +65,7 @@ or the [Karma convention](https://karma-runner.github.io/4.0/dev/git-commit-msg. [Body] ``` -**Subject and body must be valid Markdown.** -Subject must have proper casing (uppercase for first letter -if it makes sense), but no dot at the end, and no punctuation -in general. +**Subject and body must be valid Markdown.** Subject must have proper casing (uppercase for first letter if it makes sense), but no dot at the end, and no punctuation in general. Scope and body are optional. Type can be: @@ -99,9 +81,7 @@ Scope and body are optional. Type can be: - `style`: A change in code style/format. - `tests`: About tests. -If you write a body, please add trailers at the end -(for example issues and PR references, or co-authors), -without relying on GitHub's flavored Markdown: +If you write a body, please add trailers at the end (for example issues and PR references, or co-authors), without relying on GitHub's flavored Markdown: ``` Body. @@ -110,16 +90,9 @@ Issue #10: https://github.com/namespace/project/issues/10 Related to PR namespace/other-project#15: https://github.com/namespace/other-project/pull/15 ``` -These "trailers" must appear at the end of the body, -without any blank lines between them. The trailer title -can contain any character except colons `:`. -We expect a full URI for each trailer, not just GitHub autolinks -(for example, full GitHub URLs for commits and issues, -not the hash or the #issue-number). +These "trailers" must appear at the end of the body, without any blank lines between them. The trailer title can contain any character except colons `:`. We expect a full URI for each trailer, not just GitHub autolinks (for example, full GitHub URLs for commits and issues, not the hash or the #issue-number). -We do not enforce a line length on commit messages summary and body, -but please avoid very long summaries, and very long lines in the body, -unless they are part of code blocks that must not be wrapped. +We do not enforce a line length on commit messages summary and body, but please avoid very long summaries, and very long lines in the body, unless they are part of code blocks that must not be wrapped. ## Pull requests guidelines @@ -144,5 +117,4 @@ And force-push: git push -f ``` -If this seems all too complicated, you can push or force-push each new commit, -and we will squash them ourselves if needed, before merging. +If this seems all too complicated, you can push or force-push each new commit, and we will squash them ourselves if needed, before merging. diff --git a/config/ruff.toml b/config/ruff.toml index 4c91b364..04af7c19 100644 --- a/config/ruff.toml +++ b/config/ruff.toml @@ -47,17 +47,24 @@ ignore = [ ] [lint.per-file-ignores] -"src/*/cli.py" = [ +"src/**/cli.py" = [ "T201", # Print statement ] "src/*/debug.py" = [ "T201", # Print statement ] +"!src/*/*.py" = [ + "D100", # Missing docstring in public module +] +"!src/**.py" = [ + "D101", # Missing docstring in public class + "D103", # Missing docstring in public function +] "scripts/*.py" = [ "INP001", # File is part of an implicit namespace package "T201", # Print statement ] -"tests/*.py" = [ +"tests/**.py" = [ "ARG005", # Unused lambda argument "FBT001", # Boolean positional arg in function definition "PLR2004", # Magic value used in comparison diff --git a/config/vscode/launch.json b/config/vscode/launch.json index e3288388..9d632bf0 100644 --- a/config/vscode/launch.json +++ b/config/vscode/launch.json @@ -7,7 +7,17 @@ "request": "launch", "program": "${file}", "console": "integratedTerminal", - "justMyCode": false + "justMyCode": false, + "args": "${command:pickArgs}" + }, + { + "name": "run", + "type": "debugpy", + "request": "launch", + "module": "python", + "console": "integratedTerminal", + "justMyCode": false, + "args": "${command:pickArgs}" }, { "name": "docs", diff --git a/docs/.overrides/partials/path-item.html b/docs/.overrides/partials/path-item.html new file mode 100644 index 00000000..a9c95446 --- /dev/null +++ b/docs/.overrides/partials/path-item.html @@ -0,0 +1,22 @@ +{# Fix breadcrumbs for when mkdocs-section-index is used. #} +{# See https://github.com/squidfunk/mkdocs-material/issues/7614. #} + + +{% macro render_content(nav_item) %} + + {{ nav_item.title }} + +{% endmacro %} + + +{% macro render(nav_item, ref=nav_item) %} + {% if nav_item.is_page %} +

  • + + {{ render_content(ref) }} + +
  • + {% elif nav_item.children %} + {{ render(nav_item.children | first, ref) }} + {% endif %} +{% endmacro %} diff --git a/docs/changelog.md b/docs/changelog.md index 786b75d5..0536cbbe 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1 +1,5 @@ +--- +title: Changelog +--- + --8<-- "CHANGELOG.md" diff --git a/docs/code_of_conduct.md b/docs/code_of_conduct.md index 01f2ea20..002b2a04 100644 --- a/docs/code_of_conduct.md +++ b/docs/code_of_conduct.md @@ -1 +1,5 @@ +--- +title: Code of Conduct +--- + --8<-- "CODE_OF_CONDUCT.md" diff --git a/docs/contributing.md b/docs/contributing.md index ea38c9bf..61935e5d 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1 +1,5 @@ +--- +title: Contributing +--- + --8<-- "CONTRIBUTING.md" diff --git a/docs/credits.md b/docs/credits.md index f758db87..f6ab1aa2 100644 --- a/docs/credits.md +++ b/docs/credits.md @@ -1,10 +1,9 @@ --- +title: Credits hide: - toc --- - ```python exec="yes" --8<-- "scripts/gen_credits.py" ``` - diff --git a/docs/index.md b/docs/index.md index 8e6f2fb4..82377e21 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,5 @@ --- +title: Overview hide: - feedback --- diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 288075b1..f184961f 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -1,56 +1,30 @@ +--- +title: Insiders +--- + # Insiders -*mkdocstrings-python* follows the **sponsorware** release strategy, which means -that new features are first exclusively released to sponsors as part of -[Insiders][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]! +*mkdocstrings-python* 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-python Insiders* is a private fork of *mkdocstrings-python*, 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 made collaborators of this -repository. +*mkdocstrings-python Insiders* is a private fork of *mkdocstrings-python*, 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-python*. +[^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-python*. -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-python* and released for general availability, making them available -to all users. Bugfixes are always released in tandem. +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-python* 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. +[^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] +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-python*, - you can be sure that bugs are fixed quickly and new features are added - regularly. +[^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-python*, 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]! +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? @@ -92,50 +66,23 @@ else: ``` -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](https://pawamoy.github.io/backlog/). +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. +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. -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. -**Important**: If you're sponsoring **[@pawamoy][github sponsor profile]** -through a GitHub organization, please send a short email -to insiders@pawamoy.fr with the name of your -organization and the GitHub account of the individual -that should be added as a collaborator.[^4] +**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] - [^4]: - It's currently not possible to grant access to each member of an - organization, as GitHub only allows for adding users. Thus, after - sponsoring, please send an email to insiders@pawamoy.fr, stating which - account should become a collaborator of the Insiders repository. We're - working on a solution which will make access to organizations much simpler. - To ensure that access is not tied to a particular individual GitHub account, - 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 added to - the list of collaborators, the bot account can create a private fork of the - private Insiders GitHub repository, and grant access to all members of the - organizations. - - [^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](https://github.com/sponsors/pawamoy){ .md-button .md-button--primary } +[^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 }
    @@ -148,23 +95,14 @@ You can cancel your sponsorship anytime.[^5]
    - If you sponsor publicly, you're automatically added here with a link to - your profile and avatar to show your support for *mkdocstrings-python*. - 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. + If you sponsor publicly, you're automatically added here with a link to your profile and avatar to show your support for *mkdocstrings-python*. 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. +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(): @@ -174,9 +112,7 @@ for goal in goals.values(): ### 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. +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(): @@ -188,47 +124,28 @@ for goal in goals.values(): ### Compatibility -> We're building an open source project and want to allow outside collaborators -to use *mkdocstrings-python* locally without having access to Insiders. -Is this still possible? +> We're building an open source project and want to allow outside collaborators to use *mkdocstrings-python* locally without having access to Insiders. Is this still possible? -Yes. Insiders is compatible with *mkdocstrings-python*. 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. +Yes. Insiders is compatible with *mkdocstrings-python*. 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). +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-python*? - -Yes. Whether you're an individual or a company, you may use *mkdocstrings-python -Insiders* precisely under the same terms as *mkdocstrings-python*, which are given -by the [ISC License][license]. However, we kindly ask you to respect our -**fair use policy**: +> Are we allowed to use Insiders under the same terms and conditions as *mkdocstrings-python*? -- 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. +Yes. Whether you're an individual or a company, you may use *mkdocstrings-python Insiders* precisely under the same terms as *mkdocstrings-python*, which are given by the [ISC license][license]. However, we kindly ask you to respect our **fair use policy**: -- If you cancel your subscription, you're automatically removed as a - collaborator and 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]. +- 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 diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md index 3588e691..3e20e5d7 100644 --- a/docs/insiders/installation.md +++ b/docs/insiders/installation.md @@ -4,62 +4,38 @@ title: Getting started with Insiders # Getting started with Insiders -*mkdocstrings-python Insiders* is a compatible drop-in replacement for *mkdocstrings-python*, -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. - - [become an eligible sponsor]: index.md#how-to-become-a-sponsor +*mkdocstrings-python Insiders* is a compatible drop-in replacement for *mkdocstrings-python*, 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 PyPI Insiders - -[PyPI Insiders](https://pawamoy.github.io/pypi-insiders/) -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.). +### with the `insiders` tool -See [how to install it](https://pawamoy.github.io/pypi-insiders/#installation) -and [how to use it](https://pawamoy.github.io/pypi-insiders/#usage). +[`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](index.md#terms).** +**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-python Insiders* can be installed with `pip` [using SSH][using ssh]: +*mkdocstrings-python Insiders* can be installed with `pip` [using SSH][install-pip-ssh]: ```bash pip install git+ssh://git@github.com/pawamoy-insiders/mkdocstrings-python.git ``` - [using ssh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh - Or using HTTPS: ```bash pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/mkdocstrings-python.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] for -> your GitHub account. It will give you access to the Insiders repository, -> programmatically, from the command line or GitHub Actions workflows: +>? 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] +> 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 > -> [personal access token]: https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token -> [Generate a new token]: https://github.com/settings/tokens/new -> [scopes]: https://docs.github.com/en/developers/apps/scopes-for-oauth-apps#available-scopes -> -> Note that the personal access -> token must be kept secret at all times, as it allows the owner to access your -> private repositories. +> 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 @@ -77,12 +53,15 @@ pip install -e mkdocstrings-python ## Upgrading -When upgrading Insiders, you should always check the version of *mkdocstrings-python* -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`. +When upgrading Insiders, you should always check the version of *mkdocstrings-python* 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. +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. - [changelog]: ./changelog.md +[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/license.md b/docs/license.md index e81c0edf..5b25a00f 100644 --- a/docs/license.md +++ b/docs/license.md @@ -1,4 +1,5 @@ --- +title: License hide: - feedback --- diff --git a/docs/reference/api.md b/docs/reference/api.md new file mode 100644 index 00000000..d198a9ee --- /dev/null +++ b/docs/reference/api.md @@ -0,0 +1,7 @@ +--- +title: API reference +hide: +- navigation +--- + +# ::: mkdocstrings_handlers.python diff --git a/docs/usage/customization.md b/docs/usage/customization.md index 9e13da66..8239c2e9 100644 --- a/docs/usage/customization.md +++ b/docs/usage/customization.md @@ -283,7 +283,7 @@ for filepath in sorted(path for path in Path(basedir).rglob("*") if "_base" not ) ``` -See them [in the repository](https://github.com/mkdocstrings/python/tree/master/src/mkdocstrings_handlers/python/templates/). +See them [in the repository](https://github.com/mkdocstrings/python/tree/main/src/mkdocstrings_handlers/python/templates/). See the general *mkdocstrings* documentation to learn how to override them: https://mkdocstrings.github.io/theming/#templates. Each one of these templates extends a base version in `theme/_base`. Example: @@ -439,4 +439,4 @@ div.doc-contents:not(.first) { padding-left: 25px; border-left: .05rem solid rgba(200, 200, 200, 0.2); } -``` \ No newline at end of file +``` diff --git a/duties.py b/duties.py index 9e516ce5..18282747 100644 --- a/duties.py +++ b/duties.py @@ -3,11 +3,13 @@ from __future__ import annotations import os +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 +from typing import TYPE_CHECKING, Any, Callable from duty import duty, tools @@ -26,15 +28,30 @@ MULTIRUN = os.environ.get("MULTIRUN", "0") == "1" -def pyprefix(title: str) -> str: # noqa: D103 +def pyprefix(title: str) -> str: if MULTIRUN: prefix = f"(python{sys.version_info.major}.{sys.version_info.minor})" return f"{prefix:14}{title}" 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]: # noqa: D103 +def material_insiders() -> Iterator[bool]: if "+insiders" in pkgversion("mkdocs-material"): os.environ["MATERIAL_INSIDERS"] = "true" try: @@ -45,6 +62,12 @@ def material_insiders() -> Iterator[bool]: # noqa: D103 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: + return next(filter(bool, map(changelog_version_re.match, file))).group(1) # type: ignore[union-attr] + + @duty def changelog(ctx: Context, bump: str = "") -> None: """Update the changelog in-place with latest commits. @@ -53,6 +76,7 @@ def changelog(ctx: Context, bump: str = "") -> None: bump: Bump option passed to git-changelog. """ ctx.run(tools.git_changelog(bump=bump or None), title="Updating changelog") + ctx.run(tools.yore.check(bump=bump or _get_changelog_version()), title="Checking legacy code") @duty(pre=["check-quality", "check-types", "check-docs", "check-api"]) @@ -85,6 +109,7 @@ def check_docs(ctx: Context) -> None: def check_types(ctx: Context) -> None: """Check that the code is correctly typed.""" os.environ["MYPYPATH"] = "src" + os.environ["FORCE_COLOR"] = "1" ctx.run( tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"), title=pyprefix("Type-checking"), @@ -176,6 +201,7 @@ 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(): @@ -189,18 +215,13 @@ 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. Parameters: version: The new version number to use. """ - origin = ctx.run("git config --get remote.origin.url", silent=True) - if "pawamoy-insiders/mkdocstrings-python" in origin: - ctx.run( - lambda: False, - title="Not releasing from insiders repository (do that from public repo instead!)", - ) if not (version := (version or input("> Version to release: ")).strip()): ctx.run("false", title="A version must be provided") ctx.run("git add pyproject.toml CHANGELOG.md", title="Staging files", pty=PTY) diff --git a/mkdocs.yml b/mkdocs.yml index 516a0613..4841722d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -37,9 +37,7 @@ nav: - Advanced: - Customization: usage/customization.md - Extensions: usage/extensions.md -# defer to gen-files + literate-nav -- API reference: - - mkdocstrings-python: reference/ +- API reference: reference/api.md - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md @@ -63,7 +61,8 @@ theme: - content.code.copy - content.tooltips - navigation.footer - - navigation.indexes + - navigation.instant.preview + - navigation.path - navigation.sections - navigation.tabs - navigation.tabs.sticky @@ -140,15 +139,11 @@ markdown_extensions: permalink: "¤" plugins: -- autorefs - search +- autorefs - markdown-exec -- gen-files: - scripts: - - scripts/gen_ref_nav.py -- literate-nav: - nav_file: SUMMARY.md -# - coverage +- section-index +- coverage - mkdocstrings: handlers: python: @@ -164,8 +159,7 @@ plugins: docstring_options: ignore_init_summary: true docstring_section_style: list - extensions: - - scripts/griffe_extensions.py + extensions: [scripts/griffe_extensions.py] filters: ["!^_"] heading_level: 1 inherited_members: true @@ -186,12 +180,26 @@ plugins: signature_crossrefs: true summary: true unwrap_annotated: true +- llmstxt: + files: + - output: llms-full.txt + inputs: + - index.md + - reference/**.md - git-revision-date-localized: enabled: !ENV [DEPLOY, false] enable_creation_date: true type: timeago - minify: minify_html: !ENV [DEPLOY, false] +- redirects: + redirect_maps: + reference/mkdocstrings_handlers/python/index.md: reference/api.md + reference/mkdocstrings_handlers/python/config.md: reference/api.md#mkdocstrings_handlers.python.config + reference/mkdocstrings_handlers/python/debug.md: reference/api.md#mkdocstrings_handlers.python.debug + reference/mkdocstrings_handlers/python/handler.md: reference/api.md#mkdocstrings_handlers.python.handler + reference/mkdocstrings_handlers/python/rendering.md: reference/api.md#mkdocstrings_handlers.python.rendering + - group: enabled: !ENV [MATERIAL_INSIDERS, false] plugins: diff --git a/pyproject.toml b/pyproject.toml index cb429367..8d7244e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,8 @@ build-backend = "pdm.backend" name = "mkdocstrings-python" description = "A Python handler for mkdocstrings." authors = [{name = "Timothée Mazzucotelli", email = "dev@pawamoy.fr"}] -license = {text = "ISC"} +license = "ISC" +license-files = ["LICENSE"] readme = "README.md" requires-python = ">=3.9" keywords = [] @@ -78,14 +79,14 @@ data = [ ] [dependency-groups] -dev = [ - # maintenance +maintain = [ "build>=1.2", "git-changelog>=2.5", "twine>=5.1", - - # ci - "duty>=1.4", + "yore>=0.3.3", +] +ci = [ + "duty>=1.6", "ruff>=0.4", "pytest>=8.2", "pytest-cov>=5.0", @@ -96,19 +97,18 @@ dev = [ "mypy>=1.10", "types-markdown>=3.6", "types-pyyaml>=6.0", - - # docs - "black>=24.4", +] + docs = [ "markdown-callouts>=0.4", "markdown-exec>=1.8", "mkdocs>=1.6", "mkdocs-coverage>=1.0", - "mkdocs-gen-files>=0.5", "mkdocs-git-revision-date-localized-plugin>=1.2", - "mkdocs-literate-nav>=0.6", + "mkdocs-llmstxt>=0.1", "mkdocs-material>=9.5", "pydantic>=2.10", "mkdocs-minify-plugin>=0.8", + "mkdocs-section-index>=0.3", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", ] @@ -116,3 +116,6 @@ dev = [ [tool.inline-snapshot] storage-dir = "tests/snapshots" format-command = "ruff format --config config/ruff.toml --stdin-filename {filename}" + +[tool.uv] +default-groups = ["maintain", "ci", "docs"] diff --git a/scripts/gen_credits.py b/scripts/gen_credits.py index 85240535..6a81e239 100644 --- a/scripts/gen_credits.py +++ b/scripts/gen_credits.py @@ -1,4 +1,4 @@ -"""Script to generate the project's credits.""" +# Script to generate the project's credits. from __future__ import annotations @@ -27,7 +27,7 @@ pyproject = tomllib.load(pyproject_file) project = pyproject["project"] project_name = project["name"] -devdeps = [dep for dep in pyproject["dependency-groups"]["dev"] if not dep.startswith("-e")] +devdeps = [dep for group in pyproject["dependency-groups"].values() for dep in group if not dep.startswith("-e")] PackageMetadata = dict[str, Union[str, Iterable[str]]] Metadata = dict[str, PackageMetadata] diff --git a/scripts/gen_ref_nav.py b/scripts/gen_ref_nav.py deleted file mode 100644 index 6939e864..00000000 --- a/scripts/gen_ref_nav.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Generate the code reference pages and navigation.""" - -from pathlib import Path - -import mkdocs_gen_files - -nav = mkdocs_gen_files.Nav() -mod_symbol = '' - -root = Path(__file__).parent.parent -src = root / "src" - -for path in sorted(src.rglob("*.py")): - module_path = path.relative_to(src).with_suffix("") - doc_path = path.relative_to(src).with_suffix(".md") - full_doc_path = Path("reference", doc_path) - - parts = tuple(module_path.parts) - - if parts[-1] == "__init__": - parts = parts[:-1] - doc_path = doc_path.with_name("index.md") - full_doc_path = full_doc_path.with_name("index.md") - elif parts[-1].startswith("_"): - continue - - nav_parts = [f"{mod_symbol} {part}" for part in parts] - nav[tuple(nav_parts)] = doc_path.as_posix() - - with mkdocs_gen_files.open(full_doc_path, "w") as fd: - ident = ".".join(parts) - fd.write(f"---\ntitle: {ident}\n---\n\n::: {ident}") - - mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path.relative_to(root)) - -with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: - nav_file.writelines(nav.build_literate_nav()) diff --git a/scripts/get_version.py b/scripts/get_version.py index f4a30a8c..6734e5b6 100644 --- a/scripts/get_version.py +++ b/scripts/get_version.py @@ -1,4 +1,4 @@ -"""Get current project version from Git tags or changelog.""" +# Get current project version from Git tags or changelog. import re from contextlib import suppress @@ -13,7 +13,6 @@ def get_version() -> str: - """Get current project version from Git tags or changelog.""" scm_version = get_version_from_scm(_root) or _default_scm_version if scm_version.version <= Version("0.1"): # Missing Git tags? with suppress(OSError, StopIteration): # noqa: SIM117 diff --git a/scripts/griffe_extensions.py b/scripts/griffe_extensions.py index 7d283054..eb50f5f2 100644 --- a/scripts/griffe_extensions.py +++ b/scripts/griffe_extensions.py @@ -1,4 +1,4 @@ -"""Custom extensions for Griffe.""" +# Custom extensions for Griffe. from __future__ import annotations @@ -7,7 +7,7 @@ import griffe -logger = griffe.get_logger("griffe_extensions") +_logger = griffe.get_logger("griffe_extensions") class CustomFields(griffe.Extension): @@ -28,14 +28,14 @@ def on_attribute_instance( except AttributeError: return - if field.canonical_path == "mkdocstrings_handlers.python.config.Field": + if field.canonical_path == "mkdocstrings_handlers.python._internal.config._Field": description = next( attr.value for attr in field.arguments if isinstance(attr, griffe.ExprKeyword) and attr.name == "description" ) if not isinstance(description, str): - logger.warning(f"Field description of {attr.path} is not a static string") + _logger.warning(f"Field description of {attr.path} is not a static string") description = str(description) attr.docstring = griffe.Docstring( diff --git a/scripts/insiders.py b/scripts/insiders.py index a7da99bc..6535a31e 100644 --- a/scripts/insiders.py +++ b/scripts/insiders.py @@ -1,4 +1,4 @@ -"""Functions related to Insiders funding goals.""" +# Functions related to Insiders funding goals. from __future__ import annotations @@ -23,7 +23,7 @@ logger = logging.getLogger(f"mkdocs.logs.{__name__}") -def human_readable_amount(amount: int) -> str: # noqa: D103 +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:]}" @@ -32,16 +32,12 @@ def human_readable_amount(amount: int) -> str: # noqa: D103 @dataclass class Project: - """Class representing an Insiders project.""" - name: str url: str @dataclass class Feature: - """Class representing an Insiders feature.""" - name: str ref: str | None since: date | None @@ -68,8 +64,6 @@ def render(self, rel_base: str = "..", *, badge: bool = False) -> None: # noqa: @dataclass class Goal: - """Class representing an Insiders goal.""" - name: str amount: int features: list[Feature] @@ -94,16 +88,6 @@ def render(self, rel_base: str = "..") -> None: # noqa: D102 def load_goals(data: str, funding: int = 0, project: Project | None = None) -> dict[int, Goal]: - """Load goals from JSON data. - - Parameters: - data: The JSON data. - funding: The current total funding, per month. - origin: The origin of the data (URL). - - Returns: - A dictionaries of goals, keys being their target monthly amount. - """ goals_data = yaml.safe_load(data)["goals"] return { amount: Goal( @@ -151,15 +135,6 @@ def _load_goals(source: str | tuple[str, str, str], funding: int = 0) -> dict[in def funding_goals(source: str | list[str | tuple[str, str, str]], funding: int = 0) -> dict[int, Goal]: - """Load funding goals from a given data source. - - Parameters: - source: The data source (local file path or URL). - funding: The current total funding, per month. - - Returns: - A dictionaries of goals, keys being their target monthly amount. - """ if isinstance(source, str): return _load_goals_from_disk(source, funding) goals = {} @@ -174,18 +149,10 @@ def funding_goals(source: str | list[str | tuple[str, str, str]], funding: int = def feature_list(goals: Iterable[Goal]) -> list[Feature]: - """Extract feature list from funding goals. - - Parameters: - goals: A list of funding goals. - - Returns: - A list of features. - """ return list(chain.from_iterable(goal.features for goal in goals)) -def load_json(url: str) -> str | list | dict: # noqa: D103 +def load_json(url: str) -> str | list | dict: with urlopen(url) as response: # noqa: S310 return json.loads(response.read().decode()) diff --git a/scripts/make.py b/scripts/make.py index a3b9e751..5a7fb4c2 100755 --- a/scripts/make.py +++ b/scripts/make.py @@ -1,6 +1,4 @@ #!/usr/bin/env python3 -"""Management commands.""" - from __future__ import annotations import os diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 805055e0..5e5ded2b 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -1,4 +1,4 @@ -"""Generate a JSON schema of the Python handler configuration.""" +# Generate a JSON schema of the Python handler configuration. import json from dataclasses import dataclass, fields @@ -8,22 +8,22 @@ from mkdocs.config.defaults import MkDocsConfig from mkdocs.plugins import get_plugin_logger -from mkdocstrings_handlers.python.config import PythonInputConfig, PythonInputOptions +from mkdocstrings_handlers.python import PythonInputConfig, PythonInputOptions # TODO: Update when Pydantic supports Python 3.14 (sources and duties as well). try: from pydantic import TypeAdapter except ImportError: - TypeAdapter = None # type: ignore[assignment,misc] + TypeAdapter = None -logger = get_plugin_logger(__name__) +_logger = get_plugin_logger(__name__) def on_post_build(config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG001 """Write `schema.json` to the site directory.""" if TypeAdapter is None: - logger.info("Pydantic is not installed, skipping JSON schema generation") + _logger.info("Pydantic is not installed, skipping JSON schema generation") return @dataclass @@ -35,12 +35,12 @@ class PythonHandlerSchema: schema["$schema"] = "https://json-schema.org/draft-07/schema" with open(join(config.site_dir, "schema.json"), "w") as file: json.dump(schema, file, indent=2) - logger.debug("Generated JSON schema") + _logger.debug("Generated JSON schema") autorefs = config["plugins"]["autorefs"] - for field in fields(PythonInputConfig): + for field in fields(PythonInputConfig): # type: ignore[arg-type] if f"setting-{field.name}" not in autorefs._primary_url_map: - logger.warning(f"Handler setting `{field.name}` is not documented") - for field in fields(PythonInputOptions): + _logger.warning(f"Handler setting `{field.name}` is not documented") + for field in fields(PythonInputOptions): # type: ignore[arg-type] if f"option-{field.name}" not in autorefs._primary_url_map: - logger.warning(f"Configuration option `{field.name}` is not documented") + _logger.warning(f"Configuration option `{field.name}` is not documented") diff --git a/src/mkdocstrings_handlers/python/__init__.py b/src/mkdocstrings_handlers/python/__init__.py index 0432a90d..aa9edfcc 100644 --- a/src/mkdocstrings_handlers/python/__init__.py +++ b/src/mkdocstrings_handlers/python/__init__.py @@ -1,5 +1,18 @@ """Python handler for mkdocstrings.""" -from mkdocstrings_handlers.python.handler import get_handler +from mkdocstrings_handlers.python._internal.config import ( + PythonConfig, + PythonInputConfig, + PythonInputOptions, + PythonOptions, +) +from mkdocstrings_handlers.python._internal.handler import PythonHandler, get_handler -__all__ = ["get_handler"] +__all__ = [ + "PythonConfig", + "PythonHandler", + "PythonInputConfig", + "PythonInputOptions", + "PythonOptions", + "get_handler", +] diff --git a/src/mkdocstrings_handlers/python/_internal/__init__.py b/src/mkdocstrings_handlers/python/_internal/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/mkdocstrings_handlers/python/config.py b/src/mkdocstrings_handlers/python/_internal/config.py similarity index 99% rename from src/mkdocstrings_handlers/python/config.py rename to src/mkdocstrings_handlers/python/_internal/config.py index af2ac7f7..47209e79 100644 --- a/src/mkdocstrings_handlers/python/config.py +++ b/src/mkdocstrings_handlers/python/_internal/config.py @@ -54,7 +54,7 @@ _base_url = "https://mkdocstrings.github.io/python/usage" - def Field( # noqa: N802, D103 + def Field( # noqa: N802 *args: Any, description: str, group: Literal["general", "headings", "members", "docstrings", "signatures"] | None = None, @@ -75,7 +75,7 @@ def _add_markdown_description(schema: dict[str, Any]) -> None: except ImportError: from dataclasses import dataclass # type: ignore[no-redef] - def Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: D103, N802 + def Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: N802 pass diff --git a/src/mkdocstrings_handlers/python/debug.py b/src/mkdocstrings_handlers/python/_internal/debug.py similarity index 80% rename from src/mkdocstrings_handlers/python/debug.py rename to src/mkdocstrings_handlers/python/_internal/debug.py index e44f2be5..5fff669f 100644 --- a/src/mkdocstrings_handlers/python/debug.py +++ b/src/mkdocstrings_handlers/python/_internal/debug.py @@ -1,5 +1,3 @@ -"""Debugging utilities.""" - from __future__ import annotations import os @@ -10,7 +8,7 @@ @dataclass -class Variable: +class _Variable: """Dataclass describing an environment variable.""" name: str @@ -20,7 +18,7 @@ class Variable: @dataclass -class Package: +class _Package: """Dataclass describing a Python package.""" name: str @@ -30,7 +28,7 @@ class Package: @dataclass -class Environment: +class _Environment: """Dataclass to store environment information.""" interpreter_name: str @@ -41,9 +39,9 @@ class Environment: """Path to Python executable.""" platform: str """Operating System.""" - packages: list[Package] + packages: list[_Package] """Installed packages.""" - variables: list[Variable] + variables: list[_Variable] """Environment variables.""" @@ -58,7 +56,7 @@ def _interpreter_name_version() -> tuple[str, str]: return "", "0.0.0" -def get_version(dist: str = "mkdocstrings-python") -> str: +def _get_version(dist: str = "mkdocstrings-python") -> str: """Get version of the given distribution. Parameters: @@ -73,28 +71,28 @@ def get_version(dist: str = "mkdocstrings-python") -> str: return "0.0.0" -def get_debug_info() -> Environment: +def _get_debug_info() -> _Environment: """Get debug/environment information. Returns: Environment information. """ py_name, py_version = _interpreter_name_version() - packages = ["mkdocs", "mkdocstrings", "mkdocstrings-python", "griffe"] + packages = ["mkdocstrings-python"] variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("MKDOCSTRINGS_PYTHON")]] - return Environment( + return _Environment( interpreter_name=py_name, interpreter_version=py_version, interpreter_path=sys.executable, platform=platform.platform(), - variables=[Variable(var, val) for var in variables if (val := os.getenv(var))], - packages=[Package(pkg, get_version(pkg)) for pkg in packages], + variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))], + packages=[_Package(pkg, _get_version(pkg)) for pkg in packages], ) -def print_debug_info() -> None: +def _print_debug_info() -> None: """Print debug/environment information.""" - info = get_debug_info() + info = _get_debug_info() print(f"- __System__: {info.platform}") print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})") print("- __Environment variables__:") @@ -106,4 +104,4 @@ def print_debug_info() -> None: if __name__ == "__main__": - print_debug_info() + _print_debug_info() diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py similarity index 98% rename from src/mkdocstrings_handlers/python/handler.py rename to src/mkdocstrings_handlers/python/_internal/handler.py index 69f228f1..ecf99f0a 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/_internal/handler.py @@ -40,7 +40,7 @@ from contextlib import contextmanager @contextmanager - def chdir(path: str) -> Iterator[None]: # noqa: D103 + def chdir(path: str) -> Iterator[None]: old_wd = os.getcwd() os.chdir(path) try: @@ -197,7 +197,7 @@ def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions: object.__setattr__(opts, key, value) return opts - def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: # noqa: D102 + def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: module_name = identifier.split(".", 1)[0] unknown_module = module_name not in self._modules_collection reapply = True @@ -261,7 +261,7 @@ def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: # return doc_object - def render(self, data: CollectorItem, options: PythonOptions) -> str: # noqa: D102 (ignore missing docstring) + def render(self, data: CollectorItem, options: PythonOptions) -> str: template_name = rendering.do_get_template(self.env, data) template = self.env.get_template(template_name) @@ -305,7 +305,7 @@ def update_env(self, config: Any) -> None: # noqa: ARG002 self.env.globals["AutorefsHook"] = rendering.AutorefsHook self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates() - def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: D102 (ignore missing docstring) + def get_aliases(self, identifier: str) -> tuple[str, ...]: if "(" in identifier: identifier, parameter = identifier.split("(", 1) parameter.removesuffix(")") diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/_internal/rendering.py similarity index 100% rename from src/mkdocstrings_handlers/python/rendering.py rename to src/mkdocstrings_handlers/python/_internal/rendering.py diff --git a/tests/conftest.py b/tests/conftest.py index 5b2dd33f..926c4b83 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,7 +17,7 @@ from mkdocs.config.defaults import MkDocsConfig from mkdocstrings import MkdocstringsPlugin - from mkdocstrings_handlers.python.handler import PythonHandler + from mkdocstrings_handlers.python import PythonHandler # -------------------------------------------- diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 00000000..494b3820 --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,178 @@ +"""Tests for our own API exposition.""" + +from __future__ import annotations + +from collections import defaultdict +from pathlib import Path +from typing import TYPE_CHECKING + +import griffe +import pytest +from mkdocstrings import Inventory + +from mkdocstrings_handlers import python + +if TYPE_CHECKING: + from collections.abc import Iterator + + +@pytest.fixture(name="loader", scope="module") +def _fixture_loader() -> griffe.GriffeLoader: + loader = griffe.GriffeLoader() + loader.load("mkdocstrings_handlers.python") + loader.resolve_aliases() + return loader + + +@pytest.fixture(name="internal_api", scope="module") +def _fixture_internal_api(loader: griffe.GriffeLoader) -> griffe.Module: + return loader.modules_collection["mkdocstrings_handlers.python._internal"] + + +@pytest.fixture(name="public_api", scope="module") +def _fixture_public_api(loader: griffe.GriffeLoader) -> griffe.Module: + return loader.modules_collection["mkdocstrings_handlers.python"] + + +def _yield_public_objects( + obj: griffe.Module | griffe.Class, + *, + modules: bool = False, + modulelevel: bool = True, + inherited: bool = False, + special: bool = False, +) -> Iterator[griffe.Object | griffe.Alias]: + for member in obj.all_members.values() if inherited else obj.members.values(): + try: + if member.is_module: + if member.is_alias or not member.is_public: + continue + if modules: + yield member + yield from _yield_public_objects( + member, # type: ignore[arg-type] + modules=modules, + modulelevel=modulelevel, + inherited=inherited, + special=special, + ) + elif member.is_public and (special or not member.is_special): + yield member + else: + continue + if member.is_class and not modulelevel: + yield from _yield_public_objects( + member, # type: ignore[arg-type] + modules=modules, + modulelevel=False, + inherited=inherited, + special=special, + ) + except (griffe.AliasResolutionError, griffe.CyclicAliasError): + continue + + +@pytest.fixture(name="modulelevel_internal_objects", scope="module") +def _fixture_modulelevel_internal_objects(internal_api: griffe.Module) -> list[griffe.Object | griffe.Alias]: + return list(_yield_public_objects(internal_api, modulelevel=True)) + + +@pytest.fixture(name="internal_objects", scope="module") +def _fixture_internal_objects(internal_api: griffe.Module) -> list[griffe.Object | griffe.Alias]: + return list(_yield_public_objects(internal_api, modulelevel=False, special=True)) + + +@pytest.fixture(name="public_objects", scope="module") +def _fixture_public_objects(public_api: griffe.Module) -> list[griffe.Object | griffe.Alias]: + return list(_yield_public_objects(public_api, modulelevel=False, inherited=True, special=True)) + + +@pytest.fixture(name="inventory", scope="module") +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.") + with inventory_file.open("rb") as file: + return Inventory.parse_sphinx(file) + + +def test_exposed_objects(modulelevel_internal_objects: list[griffe.Object | griffe.Alias]) -> None: + """All public objects in the internal API are exposed under `mkdocstrings_handlers.python`.""" + not_exposed = [ + obj.path + for obj in modulelevel_internal_objects + if obj.name not in python.__all__ or not hasattr(python, obj.name) + ] + assert not not_exposed, "Objects not exposed:\n" + "\n".join(sorted(not_exposed)) + + +def test_unique_names(modulelevel_internal_objects: list[griffe.Object | griffe.Alias]) -> None: + """All internal objects have unique names.""" + names_to_paths = defaultdict(list) + for obj in modulelevel_internal_objects: + names_to_paths[obj.name].append(obj.path) + non_unique = [paths for paths in names_to_paths.values() if len(paths) > 1] + assert not non_unique, "Non-unique names:\n" + "\n".join(str(paths) for paths in non_unique) + + +def test_single_locations(public_api: griffe.Module) -> None: + """All objects have a single public location.""" + + def _public_path(obj: griffe.Object | griffe.Alias) -> bool: + return obj.is_public and (obj.parent is None or _public_path(obj.parent)) + + multiple_locations = {} + for obj_name in python.__all__: + obj = public_api[obj_name] + if obj.aliases and ( + public_aliases := [path for path, alias in obj.aliases.items() if path != obj.path and _public_path(alias)] + ): + multiple_locations[obj.path] = public_aliases + assert not multiple_locations, "Multiple public locations:\n" + "\n".join( + f"{path}: {aliases}" for path, aliases in multiple_locations.items() + ) + + +def test_api_matches_inventory(inventory: Inventory, public_objects: list[griffe.Object | griffe.Alias]) -> None: + """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 + ] + 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))) + + +def test_inventory_matches_api( + inventory: Inventory, + public_objects: list[griffe.Object | griffe.Alias], + loader: griffe.GriffeLoader, +) -> None: + """The inventory doesn't contain any additional Python object.""" + not_in_api = [] + public_api_paths = {obj.path for obj in public_objects} + public_api_paths.add("mkdocstrings_handlers") + public_api_paths.add("mkdocstrings_handlers.python") + for item in inventory.values(): + if item.domain == "py" and "(" not in item.name: + obj = loader.modules_collection[item.name] + 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) + msg = "Inventory objects not in public API (try running `make run mkdocs build`):\n{paths}" + assert not not_in_api, msg.format(paths="\n".join(sorted(not_in_api))) + + +def test_no_module_docstrings_in_internal_api(internal_api: griffe.Module) -> None: + """No module docstrings should be written in our internal API. + + The reasoning is that docstrings are addressed to users of the public API, + but internal modules are not exposed to users, so they should not have docstrings. + """ + + def _modules(obj: griffe.Module) -> Iterator[griffe.Module]: + for member in obj.modules.values(): + yield member + yield from _modules(member) + + for obj in _modules(internal_api): + assert not obj.docstring From 93a68d0d7afce38c78a8264189cfa812d737666c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Thu, 13 Mar 2025 17:01:48 +0100 Subject: [PATCH 20/53] refactor: Move modules into internal folder, expose API in top-level module Also synchronize docs (objects inventory) with public API. --- CHANGELOG.md | 2 +- docs/insiders/changelog.md | 2 +- docs/reference/api.md | 2 + mkdocs.yml | 2 +- pyproject.toml | 2 + scripts/mkdocs_hooks.py | 6 +- src/mkdocstrings_handlers/python/__init__.py | 52 +++++ .../python/_internal/config.py | 199 +++++++++--------- .../python/_internal/handler.py | 56 ++++- .../python/_internal/rendering.py | 31 +-- src/mkdocstrings_handlers/python/config.py | 17 ++ src/mkdocstrings_handlers/python/handler.py | 17 ++ src/mkdocstrings_handlers/python/rendering.py | 17 ++ tests/helpers.py | 2 +- tests/test_api.py | 14 +- tests/test_end_to_end.py | 2 +- tests/test_handler.py | 4 +- tests/test_rendering.py | 2 +- tests/test_themes.py | 2 +- 19 files changed, 299 insertions(+), 132 deletions(-) create mode 100644 src/mkdocstrings_handlers/python/config.py create mode 100644 src/mkdocstrings_handlers/python/handler.py create mode 100644 src/mkdocstrings_handlers/python/rendering.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ec6a2c4c..55060292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -589,7 +589,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Breaking changes -- The signature of the [`format_signature` filter](https://mkdocstrings.github.io/python/reference/mkdocstrings_handlers/python/rendering/#mkdocstrings_handlers.python.rendering.do_format_signature) has changed. +- The signature of the [`format_signature` filter][mkdocstrings_handlers.python.do_format_signature] has changed. If you override templates in your project to customize the output, make sure to update the following templates so that they use the new filter signature: diff --git a/docs/insiders/changelog.md b/docs/insiders/changelog.md index 25562a7d..2508ec26 100644 --- a/docs/insiders/changelog.md +++ b/docs/insiders/changelog.md @@ -81,6 +81,6 @@ - Add [cross-references for type annotations in signatures](../usage/configuration/signatures.md#signature_crossrefs). Make sure to update your local templates as the signature of the - [`format_signature` filter][mkdocstrings_handlers.python.rendering.do_format_signature] + [`format_signature` filter][mkdocstrings_handlers.python.do_format_signature] has changed. The templates that must be updated: `class.html`, `expression.html`, `function.html` and `signature.html`. diff --git a/docs/reference/api.md b/docs/reference/api.md index d198a9ee..587e99db 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -5,3 +5,5 @@ hide: --- # ::: mkdocstrings_handlers.python + options: + show_submodules: true diff --git a/mkdocs.yml b/mkdocs.yml index 4841722d..fbcbebba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -143,7 +143,7 @@ plugins: - autorefs - markdown-exec - section-index -- coverage +# - coverage - mkdocstrings: handlers: python: diff --git a/pyproject.toml b/pyproject.toml index 8d7244e2..efaf7c5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,6 +86,7 @@ maintain = [ "yore>=0.3.3", ] ci = [ + "blacK>=25.1", "duty>=1.6", "ruff>=0.4", "pytest>=8.2", @@ -108,6 +109,7 @@ ci = [ "mkdocs-material>=9.5", "pydantic>=2.10", "mkdocs-minify-plugin>=0.8", + "mkdocs-redirects>=1.2", "mkdocs-section-index>=0.3", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 5e5ded2b..739f93b3 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -14,7 +14,7 @@ try: from pydantic import TypeAdapter except ImportError: - TypeAdapter = None + TypeAdapter = None # type: ignore[assignment,misc] _logger = get_plugin_logger(__name__) @@ -38,9 +38,9 @@ class PythonHandlerSchema: _logger.debug("Generated JSON schema") autorefs = config["plugins"]["autorefs"] - for field in fields(PythonInputConfig): # type: ignore[arg-type] + for field in fields(PythonInputConfig): if f"setting-{field.name}" not in autorefs._primary_url_map: _logger.warning(f"Handler setting `{field.name}` is not documented") - for field in fields(PythonInputOptions): # type: ignore[arg-type] + for field in fields(PythonInputOptions): if f"option-{field.name}" not in autorefs._primary_url_map: _logger.warning(f"Configuration option `{field.name}` is not documented") diff --git a/src/mkdocstrings_handlers/python/__init__.py b/src/mkdocstrings_handlers/python/__init__.py index aa9edfcc..faa9b9f4 100644 --- a/src/mkdocstrings_handlers/python/__init__.py +++ b/src/mkdocstrings_handlers/python/__init__.py @@ -1,18 +1,70 @@ """Python handler for mkdocstrings.""" from mkdocstrings_handlers.python._internal.config import ( + AutoStyleOptions, + GoogleStyleOptions, + Inventory, + NumpyStyleOptions, + PerStyleOptions, PythonConfig, PythonInputConfig, PythonInputOptions, PythonOptions, + SphinxStyleOptions, + SummaryOption, ) from mkdocstrings_handlers.python._internal.handler import PythonHandler, get_handler +from mkdocstrings_handlers.python._internal.rendering import ( + AutorefsHook, + Order, + Tree, + do_as_attributes_section, + do_as_classes_section, + do_as_functions_section, + do_as_modules_section, + do_backlink_tree, + do_crossref, + do_filter_objects, + do_format_attribute, + do_format_code, + do_format_signature, + do_get_template, + do_multi_crossref, + do_order_members, + do_split_path, + do_stash_crossref, +) __all__ = [ + "AutoStyleOptions", + "AutorefsHook", + "GoogleStyleOptions", + "Inventory", + "NumpyStyleOptions", + "Order", + "PerStyleOptions", "PythonConfig", "PythonHandler", "PythonInputConfig", "PythonInputOptions", "PythonOptions", + "SphinxStyleOptions", + "SummaryOption", + "Tree", + "do_as_attributes_section", + "do_as_classes_section", + "do_as_functions_section", + "do_as_modules_section", + "do_backlink_tree", + "do_crossref", + "do_filter_objects", + "do_format_attribute", + "do_format_code", + "do_format_signature", + "do_get_template", + "do_multi_crossref", + "do_order_members", + "do_split_path", + "do_stash_crossref", "get_handler", ] diff --git a/src/mkdocstrings_handlers/python/_internal/config.py b/src/mkdocstrings_handlers/python/_internal/config.py index 47209e79..e1dd0746 100644 --- a/src/mkdocstrings_handlers/python/_internal/config.py +++ b/src/mkdocstrings_handlers/python/_internal/config.py @@ -1,4 +1,4 @@ -"""Configuration and options dataclasses.""" +# Configuration and options dataclasses. from __future__ import annotations @@ -16,7 +16,7 @@ from typing_extensions import Self -logger = get_logger(__name__) +_logger = get_logger(__name__) try: @@ -40,7 +40,7 @@ try: import eval_type_backport # noqa: F401 except ImportError: - logger.debug( + _logger.debug( "Pydantic needs the `eval-type-backport` package to be installed " "for modern type syntax to work on Python 3.9. " "Deactivating Pydantic validation for Python handler options.", @@ -54,7 +54,7 @@ _base_url = "https://mkdocstrings.github.io/python/usage" - def Field( # noqa: N802 + def _Field( # noqa: N802 *args: Any, description: str, group: Literal["general", "headings", "members", "docstrings", "signatures"] | None = None, @@ -75,7 +75,7 @@ def _add_markdown_description(schema: dict[str, Any]) -> None: except ImportError: from dataclasses import dataclass # type: ignore[no-redef] - def Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: N802 + def _Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: N802 pass @@ -96,7 +96,7 @@ class GoogleStyleOptions: ignore_init_summary: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to ignore the summary in `__init__` methods' docstrings.", @@ -105,7 +105,7 @@ class GoogleStyleOptions: returns_multiple_items: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse multiple items in `Yields` and `Returns` sections. @@ -118,7 +118,7 @@ class GoogleStyleOptions: returns_named_value: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse `Yields` and `Returns` section items as name and description, rather than type and description. @@ -131,7 +131,7 @@ class GoogleStyleOptions: returns_type_in_property_summary: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to parse the return type of properties at the beginning of their summary: `str: Summary of the property`.", @@ -140,7 +140,7 @@ class GoogleStyleOptions: receives_multiple_items: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse multiple items in `Receives` sections. @@ -153,7 +153,7 @@ class GoogleStyleOptions: receives_named_value: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="""Whether to parse `Receives` section items as name and description, rather than type and description. @@ -166,7 +166,7 @@ class GoogleStyleOptions: trim_doctest_flags: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to remove doctest flags from Python example blocks.", @@ -175,7 +175,7 @@ class GoogleStyleOptions: warn_unknown_params: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Warn about documented parameters not appearing in the signature.", @@ -190,7 +190,7 @@ class NumpyStyleOptions: ignore_init_summary: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to ignore the summary in `__init__` methods' docstrings.", @@ -199,7 +199,7 @@ class NumpyStyleOptions: trim_doctest_flags: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Whether to remove doctest flags from Python example blocks.", @@ -208,7 +208,7 @@ class NumpyStyleOptions: warn_unknown_params: Annotated[ bool, - Field( + _Field( group="docstrings", parent="docstring_options", description="Warn about documented parameters not appearing in the signature.", @@ -229,7 +229,7 @@ class PerStyleOptions: google: Annotated[ GoogleStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Google-style options.", @@ -238,7 +238,7 @@ class PerStyleOptions: numpy: Annotated[ NumpyStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Numpydoc-style options.", @@ -247,7 +247,7 @@ class PerStyleOptions: sphinx: Annotated[ SphinxStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Sphinx-style options.", @@ -273,7 +273,7 @@ class AutoStyleOptions: method: Annotated[ Literal["heuristics", "max_sections"], - Field( + _Field( group="docstrings", parent="docstring_options", description="The method to use to determine the docstring style.", @@ -282,7 +282,7 @@ class AutoStyleOptions: style_order: Annotated[ list[str], - Field( + _Field( group="docstrings", parent="docstring_options", description="The order of the docstring styles to try.", @@ -291,7 +291,7 @@ class AutoStyleOptions: default: Annotated[ str | None, - Field( + _Field( group="docstrings", parent="docstring_options", description="The default docstring style to use if no other style is detected.", @@ -300,7 +300,7 @@ class AutoStyleOptions: per_style_options: Annotated[ PerStyleOptions, - Field( + _Field( group="docstrings", parent="docstring_options", description="Per-style options.", @@ -322,7 +322,7 @@ class SummaryOption: attributes: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of attributes.", @@ -331,7 +331,7 @@ class SummaryOption: functions: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of functions (methods).", @@ -340,7 +340,7 @@ class SummaryOption: classes: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of classes.", @@ -349,7 +349,7 @@ class SummaryOption: modules: Annotated[ bool, - Field( + _Field( group="members", parent="summary", description="Whether to render summaries of modules.", @@ -364,7 +364,7 @@ class PythonInputOptions: allow_inspection: Annotated[ bool, - Field( + _Field( group="general", description="Whether to allow inspecting modules when visiting them is not possible.", ), @@ -372,7 +372,7 @@ class PythonInputOptions: force_inspection: Annotated[ bool, - Field( + _Field( group="general", description="Whether to force using dynamic analysis when loading data.", ), @@ -380,7 +380,7 @@ class PythonInputOptions: annotations_path: Annotated[ Literal["brief", "source", "full"], - Field( + _Field( group="signatures", description="The verbosity for annotations path: `brief` (recommended), `source` (as written in the source), or `full`.", ), @@ -388,7 +388,7 @@ class PythonInputOptions: backlinks: Annotated[ Literal["flat", "tree", False], - Field( + _Field( group="general", description="Whether to render backlinks, and how.", ), @@ -396,7 +396,7 @@ class PythonInputOptions: docstring_options: Annotated[ GoogleStyleOptions | NumpyStyleOptions | SphinxStyleOptions | AutoStyleOptions | None, - Field( + _Field( group="docstrings", description="""The options for the docstring parser. @@ -407,7 +407,7 @@ class PythonInputOptions: docstring_section_style: Annotated[ Literal["table", "list", "spacy"], - Field( + _Field( group="docstrings", description="The style used to render docstring sections.", ), @@ -415,7 +415,7 @@ class PythonInputOptions: docstring_style: Annotated[ Literal["auto", "google", "numpy", "sphinx"] | None, - Field( + _Field( group="docstrings", description="The docstring style to use: `auto`, `google`, `numpy`, `sphinx`, or `None`.", ), @@ -423,7 +423,7 @@ class PythonInputOptions: extensions: Annotated[ list[str | dict[str, Any]], - Field( + _Field( group="general", description="A list of Griffe extensions to load.", ), @@ -431,7 +431,7 @@ class PythonInputOptions: filters: Annotated[ list[str], - Field( + _Field( group="members", description="""A list of filters applied to filter objects based on their name. @@ -444,7 +444,7 @@ class PythonInputOptions: find_stubs_package: Annotated[ bool, - Field( + _Field( group="general", description="Whether to load stubs package (package-stubs) when extracting docstrings.", ), @@ -452,7 +452,7 @@ class PythonInputOptions: group_by_category: Annotated[ bool, - Field( + _Field( group="members", description="Group the object's children by categories: attributes, classes, functions, and modules.", ), @@ -460,7 +460,7 @@ class PythonInputOptions: heading: Annotated[ str, - Field( + _Field( group="headings", description="A custom string to override the autogenerated heading of the root object.", ), @@ -468,7 +468,7 @@ class PythonInputOptions: heading_level: Annotated[ int, - Field( + _Field( group="headings", description="The initial heading level to use.", ), @@ -476,7 +476,7 @@ class PythonInputOptions: inherited_members: Annotated[ bool | list[str], - Field( + _Field( group="members", description="""A boolean, or an explicit list of inherited members to render. @@ -488,7 +488,7 @@ class PythonInputOptions: line_length: Annotated[ int, - Field( + _Field( group="signatures", description="Maximum line length when formatting code/signatures.", ), @@ -496,7 +496,7 @@ class PythonInputOptions: members: Annotated[ list[str] | bool | None, - Field( + _Field( group="members", description="""A boolean, or an explicit list of members to render. @@ -509,7 +509,7 @@ class PythonInputOptions: members_order: Annotated[ Literal["alphabetical", "source"], - Field( + _Field( group="members", description="""The members ordering to use. @@ -521,7 +521,7 @@ class PythonInputOptions: merge_init_into_class: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to merge the `__init__` method into the class' signature and docstring.", ), @@ -529,7 +529,7 @@ class PythonInputOptions: modernize_annotations: Annotated[ bool, - Field( + _Field( group="signatures", description="Whether to modernize annotations, for example `Optional[str]` into `str | None`.", ), @@ -537,7 +537,7 @@ class PythonInputOptions: parameter_headings: Annotated[ bool, - Field( + _Field( group="headings", description="Whether to render headings for parameters (therefore showing parameters in the ToC).", ), @@ -545,7 +545,7 @@ class PythonInputOptions: preload_modules: Annotated[ list[str], - Field( + _Field( group="general", description="""Pre-load modules that are not specified directly in autodoc instructions (`::: identifier`). @@ -562,7 +562,7 @@ class PythonInputOptions: relative_crossrefs: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to enable the relative crossref syntax.", ), @@ -570,7 +570,7 @@ class PythonInputOptions: scoped_crossrefs: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to enable the scoped crossref ability.", ), @@ -578,7 +578,7 @@ class PythonInputOptions: show_overloads: Annotated[ bool, - Field( + _Field( group="signatures", description="Show the overloads of a function or method.", ), @@ -586,7 +586,7 @@ class PythonInputOptions: separate_signature: Annotated[ bool, - Field( + _Field( group="signatures", description="""Whether to put the whole signature in a code block below the heading. @@ -597,7 +597,7 @@ class PythonInputOptions: show_bases: Annotated[ bool, - Field( + _Field( group="general", description="Show the base classes of a class.", ), @@ -605,7 +605,7 @@ class PythonInputOptions: show_category_heading: Annotated[ bool, - Field( + _Field( group="headings", description="When grouped by categories, show a heading for each category.", ), @@ -613,7 +613,7 @@ class PythonInputOptions: show_docstring_attributes: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Attributes' section in the object's docstring.", ), @@ -621,7 +621,7 @@ class PythonInputOptions: show_docstring_classes: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Classes' section in the object's docstring.", ), @@ -629,7 +629,7 @@ class PythonInputOptions: show_docstring_description: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the textual block (including admonitions) in the object's docstring.", ), @@ -637,7 +637,7 @@ class PythonInputOptions: show_docstring_examples: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Examples' section in the object's docstring.", ), @@ -645,7 +645,7 @@ class PythonInputOptions: show_docstring_functions: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Functions' or 'Methods' sections in the object's docstring.", ), @@ -653,7 +653,7 @@ class PythonInputOptions: show_docstring_modules: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Modules' section in the object's docstring.", ), @@ -661,7 +661,7 @@ class PythonInputOptions: show_docstring_other_parameters: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Other Parameters' section in the object's docstring.", ), @@ -669,7 +669,7 @@ class PythonInputOptions: show_docstring_parameters: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Parameters' section in the object's docstring.", ), @@ -677,7 +677,7 @@ class PythonInputOptions: show_docstring_raises: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Raises' section in the object's docstring.", ), @@ -685,7 +685,7 @@ class PythonInputOptions: show_docstring_receives: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Receives' section in the object's docstring.", ), @@ -693,7 +693,7 @@ class PythonInputOptions: show_docstring_returns: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Returns' section in the object's docstring.", ), @@ -701,7 +701,7 @@ class PythonInputOptions: show_docstring_warns: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Warns' section in the object's docstring.", ), @@ -709,7 +709,7 @@ class PythonInputOptions: show_docstring_yields: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to display the 'Yields' section in the object's docstring.", ), @@ -717,7 +717,7 @@ class PythonInputOptions: show_if_no_docstring: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the object heading even if it has no docstring or children with docstrings.", ), @@ -725,7 +725,7 @@ class PythonInputOptions: show_inheritance_diagram: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the inheritance diagram of a class using Mermaid.", ), @@ -733,7 +733,7 @@ class PythonInputOptions: show_labels: Annotated[ bool, - Field( + _Field( group="docstrings", description="Whether to show labels of the members.", ), @@ -741,7 +741,7 @@ class PythonInputOptions: show_object_full_path: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the full Python path of every object.", ), @@ -749,7 +749,7 @@ class PythonInputOptions: show_root_full_path: Annotated[ bool, - Field( + _Field( group="docstrings", description="Show the full Python path for the root object heading.", ), @@ -757,7 +757,7 @@ class PythonInputOptions: show_root_heading: Annotated[ bool, - Field( + _Field( group="headings", description="""Show the heading of the object at the root of the documentation tree. @@ -768,7 +768,7 @@ class PythonInputOptions: show_root_members_full_path: Annotated[ bool, - Field( + _Field( group="headings", description="Show the full Python path of the root members.", ), @@ -776,7 +776,7 @@ class PythonInputOptions: show_root_toc_entry: Annotated[ bool, - Field( + _Field( group="headings", description="If the root heading is not shown, at least add a ToC entry for it.", ), @@ -784,7 +784,7 @@ class PythonInputOptions: show_signature_annotations: Annotated[ bool, - Field( + _Field( group="signatures", description="Show the type annotations in methods and functions signatures.", ), @@ -792,7 +792,7 @@ class PythonInputOptions: show_signature: Annotated[ bool, - Field( + _Field( group="signatures", description="Show methods and functions signatures.", ), @@ -800,7 +800,7 @@ class PythonInputOptions: show_source: Annotated[ bool, - Field( + _Field( group="general", description="Show the source code of this object.", ), @@ -808,7 +808,7 @@ class PythonInputOptions: show_submodules: Annotated[ bool, - Field( + _Field( group="members", description="When rendering a module, show its submodules recursively.", ), @@ -816,7 +816,7 @@ class PythonInputOptions: show_symbol_type_heading: Annotated[ bool, - Field( + _Field( group="headings", description="Show the symbol type in headings (e.g. mod, class, meth, func and attr).", ), @@ -824,7 +824,7 @@ class PythonInputOptions: show_symbol_type_toc: Annotated[ bool, - Field( + _Field( group="headings", description="Show the symbol type in the Table of Contents (e.g. mod, class, methd, func and attr).", ), @@ -832,7 +832,7 @@ class PythonInputOptions: signature_crossrefs: Annotated[ bool, - Field( + _Field( group="signatures", description="Whether to render cross-references for type annotations in signatures.", ), @@ -840,7 +840,7 @@ class PythonInputOptions: summary: Annotated[ bool | SummaryOption, - Field( + _Field( group="members", description="Whether to render summaries of modules, classes, functions (methods) and attributes.", ), @@ -848,7 +848,7 @@ class PythonInputOptions: toc_label: Annotated[ str, - Field( + _Field( group="headings", description="A custom string to override the autogenerated toc label of the root object.", ), @@ -856,7 +856,7 @@ class PythonInputOptions: unwrap_annotated: Annotated[ bool, - Field( + _Field( group="signatures", description="Whether to unwrap `Annotated` types to show only the type without the annotations.", ), @@ -864,7 +864,7 @@ class PythonInputOptions: extra: Annotated[ dict[str, Any], - Field( + _Field( group="general", description="Extra options.", ), @@ -937,7 +937,7 @@ class Inventory: url: Annotated[ str, - Field( + _Field( parent="inventories", description="The URL of the inventory.", ), @@ -945,7 +945,7 @@ class Inventory: base: Annotated[ str | None, - Field( + _Field( parent="inventories", description="The base URL of the inventory.", ), @@ -953,7 +953,7 @@ class Inventory: domains: Annotated[ list[str], - Field( + _Field( parent="inventories", description="The domains to load from the inventory.", ), @@ -971,27 +971,27 @@ class PythonInputConfig: inventories: Annotated[ list[str | Inventory], - Field(description="The inventories to load."), + _Field(description="The inventories to load."), ] = field(default_factory=list) paths: Annotated[ list[str], - Field(description="The paths in which to search for Python packages."), + _Field(description="The paths in which to search for Python packages."), ] = field(default_factory=lambda: ["."]) load_external_modules: Annotated[ bool | None, - Field(description="Whether to always load external modules/packages."), + _Field(description="Whether to always load external modules/packages."), ] = None options: Annotated[ PythonInputOptions, - Field(description="Configuration options for collecting and rendering objects."), + _Field(description="Configuration options for collecting and rendering objects."), ] = field(default_factory=PythonInputOptions) locale: Annotated[ str | None, - Field(description="The locale to use when translating template strings."), + _Field(description="The locale to use when translating template strings."), ] = None @classmethod @@ -1010,8 +1010,15 @@ def from_data(cls, **data: Any) -> Self: class PythonConfig(PythonInputConfig): # type: ignore[override,unused-ignore] """Python handler configuration.""" - inventories: list[Inventory] = field(default_factory=list) # type: ignore[assignment] - options: dict[str, Any] = field(default_factory=dict) # type: ignore[assignment] + inventories: Annotated[ + list[Inventory], + _Field(description="The object inventories to load."), + ] = field(default_factory=list) # type: ignore[assignment] + + options: Annotated[ + dict[str, Any], + _Field(description="Configuration options for collecting and rendering objects."), + ] = field(default_factory=dict) # type: ignore[assignment] @classmethod def coerce(cls, **data: Any) -> MutableMapping[str, Any]: diff --git a/src/mkdocstrings_handlers/python/_internal/handler.py b/src/mkdocstrings_handlers/python/_internal/handler.py index ecf99f0a..6dd067e7 100644 --- a/src/mkdocstrings_handlers/python/_internal/handler.py +++ b/src/mkdocstrings_handlers/python/_internal/handler.py @@ -1,4 +1,4 @@ -"""This module implements a handler for the Python language.""" +# This module implements a handler for the Python language. from __future__ import annotations @@ -24,8 +24,8 @@ from mkdocs.exceptions import PluginError from mkdocstrings import BaseHandler, CollectionError, CollectorItem, HandlerOptions, Inventory, get_logger -from mkdocstrings_handlers.python import rendering -from mkdocstrings_handlers.python.config import PythonConfig, PythonOptions +from mkdocstrings_handlers.python._internal import rendering +from mkdocstrings_handlers.python._internal.config import PythonConfig, PythonOptions if TYPE_CHECKING: from collections.abc import Iterator, Mapping, MutableMapping, Sequence @@ -49,7 +49,7 @@ def chdir(path: str) -> Iterator[None]: os.chdir(old_wd) -logger = get_logger(__name__) +_logger = get_logger(__name__) patch_loggers(get_logger) @@ -90,7 +90,9 @@ def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None: super().__init__(**kwargs) self.config = config + """The handler configuration.""" self.base_dir = base_dir + """The base directory of the project.""" # YORE: Bump 2: Replace block with `self.global_options = config.options`. global_extra, global_options = PythonOptions._extract_extra(config.options) @@ -98,12 +100,13 @@ def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None: _warn_extra_options(global_extra.keys()) # type: ignore[arg-type] self._global_extra = global_extra self.global_options = global_options + """The global configuration options (in `mkdocs.yml`).""" # Warn if user overrides base templates. if self.custom_templates: for theme_dir in base_dir.joinpath(self.custom_templates, "python").iterdir(): if theme_dir.joinpath("_base").is_dir(): - logger.warning( + _logger.warning( f"Overriding base template '{theme_dir.name}/_base/