From d5bf4e1ed0370853f968b210ad77913faf106eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Tue, 1 Apr 2025 14:05:31 +0200 Subject: [PATCH 1/6] docs: Update link to YAML idiosyncrasies --- docs/usage/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage/index.md b/docs/usage/index.md index ea9716cc..d208b4b0 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -16,7 +16,7 @@ The syntax is as follows: > Here are some resources that other users found useful to better > understand YAML's peculiarities. > -> - [YAML idiosyncrasies](https://docs.saltproject.io/en/3000/topics/troubleshooting/yaml_idiosyncrasies.html) +> - [YAML idiosyncrasies](https://salt-zh.readthedocs.io/en/latest/topics/troubleshooting/yaml_idiosyncrasies.html) > - [YAML multiline](https://yaml-multiline.info/) The `identifier` is a string identifying the object you want to document. From b1da3d02c4f432f603cbb0004bb35099327706db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 30 Jun 2025 22:37:58 +0200 Subject: [PATCH 2/6] ci: Ignore Ruff warnings --- duties.py | 2 +- src/mkdocstrings/_internal/handlers/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/duties.py b/duties.py index 6ee9b08d..b75d8b55 100644 --- a/duties.py +++ b/duties.py @@ -238,7 +238,7 @@ def coverage(ctx: Context) -> None: @duty -def test(ctx: Context, *cli_args: str, match: str = "") -> None: +def test(ctx: Context, *cli_args: str, match: str = "") -> None: # noqa: PT028 """Run the test suite. Parameters: diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index 3d5852c5..c87f473c 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -359,7 +359,7 @@ def get_templates_dir(self, handler: str | None = None) -> Path: """ handler = handler or self.name try: - import mkdocstrings_handlers + import mkdocstrings_handlers # noqa: PLC0415 except ModuleNotFoundError as error: raise ModuleNotFoundError(f"Handler '{handler}' not found, is it installed?") from error From 51f217f38af9475415f758866697158d9010967d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 30 Jun 2025 22:47:11 +0200 Subject: [PATCH 3/6] chore: Template upgrade --- .copier-answers.yml | 2 +- docs/css/mkdocstrings.css | 45 ++++++++++++++++++++++ docs/reference/{mkdocstrings.md => api.md} | 0 mkdocs.yml | 29 +++++++------- pyproject.toml | 4 +- tests/test_api.py | 6 ++- tests/test_extension.py | 1 - 7 files changed, 69 insertions(+), 18 deletions(-) rename docs/reference/{mkdocstrings.md => api.md} (100%) diff --git a/.copier-answers.yml b/.copier-answers.yml index bda5cd0c..fa6c4f9c 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier. -_commit: 1.7.1 +_commit: 1.8.4 _src_path: gh:pawamoy/copier-uv author_email: dev@pawamoy.fr author_fullname: Timothée Mazzucotelli diff --git a/docs/css/mkdocstrings.css b/docs/css/mkdocstrings.css index 55321aa3..3447549c 100644 --- a/docs/css/mkdocstrings.css +++ b/docs/css/mkdocstrings.css @@ -35,3 +35,48 @@ td code { li.md-nav__item:has(> a[href*="("]) { display: none; } + +/* Tree-like output for backlinks. */ +.doc-backlink-list { + --tree-clr: var(--md-default-fg-color); + --tree-font-size: 1rem; + --tree-item-height: 1; + --tree-offset: 1rem; + --tree-thickness: 1px; + --tree-style: solid; + display: grid; + list-style: none !important; +} + +.doc-backlink-list li > span:first-child { + text-indent: .3rem; +} +.doc-backlink-list li { + padding-inline-start: var(--tree-offset); + border-left: var(--tree-thickness) var(--tree-style) var(--tree-clr); + position: relative; + margin-left: 0 !important; + + &:last-child { + border-color: transparent; + } + &::before{ + content: ''; + position: absolute; + top: calc(var(--tree-item-height) / 2 * -1 * var(--tree-font-size) + var(--tree-thickness)); + left: calc(var(--tree-thickness) * -1); + width: calc(var(--tree-offset) + var(--tree-thickness) * 2); + height: calc(var(--tree-item-height) * var(--tree-font-size)); + border-left: var(--tree-thickness) var(--tree-style) var(--tree-clr); + border-bottom: var(--tree-thickness) var(--tree-style) var(--tree-clr); + } + &::after{ + content: ''; + position: absolute; + border-radius: 50%; + background-color: var(--tree-clr); + top: calc(var(--tree-item-height) / 2 * 1rem); + left: var(--tree-offset) ; + translate: calc(var(--tree-thickness) * -1) calc(var(--tree-thickness) * -1); + } +} diff --git a/docs/reference/mkdocstrings.md b/docs/reference/api.md similarity index 100% rename from docs/reference/mkdocstrings.md rename to docs/reference/api.md diff --git a/mkdocs.yml b/mkdocs.yml index 828a81a4..dc53327f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,7 +34,7 @@ nav: - Guides: - Recipes: recipes.md - Troubleshooting: troubleshooting.md -- API reference: reference/mkdocstrings.md +- API reference: reference/api.md - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md @@ -143,6 +143,7 @@ plugins: - https://markupsafe.palletsprojects.com/en/stable/objects.inv paths: [src] options: + backlinks: tree docstring_options: ignore_init_summary: true docstring_section_style: list @@ -161,16 +162,17 @@ plugins: signature_crossrefs: true summary: true - llmstxt: - files: - - output: llms-full.txt - inputs: + full_output: llms-full.txt + sections: + Usage: - index.md - usage/index.md - usage/handlers.md - usage/theming.md - recipes.md - troubleshooting.md - - reference/**.md + API: + - reference/api.md - git-revision-date-localized: enabled: !ENV [DEPLOY, false] enable_creation_date: true @@ -179,14 +181,15 @@ plugins: redirect_maps: theming.md: usage/theming.md handlers/overview.md: usage/handlers.md - reference/index.md: reference/mkdocstrings.md#mkdocstrings - reference/extension.md: reference/mkdocstrings.md#mkdocstrings.extension - reference/handlers/index.md: reference/mkdocstrings.md#mkdocstrings.handlers - reference/handlers/base.md: reference/mkdocstrings.md#mkdocstrings.handlers.base - reference/handlers/rendering.md: reference/mkdocstrings.md#mkdocstrings.handlers.rendering - reference/inventory.md: reference/mkdocstrings.md#mkdocstrings.inventory - reference/loggers.md: reference/mkdocstrings.md#mkdocstrings.loggers - reference/plugin.md: reference/mkdocstrings.md#mkdocstrings.plugin + reference/mkdocstrings.md: reference/api.md + reference/index.md: reference/api.md#mkdocstrings + reference/extension.md: reference/api.md#mkdocstrings.extension + reference/handlers/index.md: reference/api.md#mkdocstrings.handlers + reference/handlers/base.md: reference/api.md#mkdocstrings.handlers.base + reference/handlers/rendering.md: reference/api.md#mkdocstrings.handlers.rendering + reference/inventory.md: reference/api.md#mkdocstrings.inventory + reference/loggers.md: reference/api.md#mkdocstrings.loggers + reference/plugin.md: reference/api.md#mkdocstrings.plugin - minify: minify_html: !ENV [DEPLOY, false] - group: diff --git a/pyproject.toml b/pyproject.toml index 29aff0d9..4db21462 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ getter = "scripts.get_version:get_version" [tool.pdm.build] # Include as much as possible in the source distribution, to help redistributors. -excludes = ["**/.pytest_cache"] +excludes = ["**/.pytest_cache", "**/.mypy_cache"] source-includes = [ "config", "docs", @@ -112,7 +112,7 @@ ci = [ "mkdocs>=1.6", "mkdocs-coverage>=1.0", "mkdocs-git-revision-date-localized-plugin>=1.2", - "mkdocs-llmstxt>=0.1", + "mkdocs-llmstxt>=0.2", "mkdocs-material>=9.5", "mkdocs-minify-plugin>=0.8", "mkdocs-redirects>=1.2.1", diff --git a/tests/test_api.py b/tests/test_api.py index 4b13aac7..57f0ce20 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -155,7 +155,11 @@ def test_inventory_matches_api( public_api_paths = {obj.path for obj in public_objects} public_api_paths.add("mkdocstrings") for item in inventory.values(): - if item.domain == "py" and "(" not in item.name: + if ( + item.domain == "py" + and "(" not in item.name + and (item.name == "mkdocstrings" or item.name.startswith("mkdocstrings.")) + ): obj = loader.modules_collection[item.name] # YORE: Bump 1: Remove block. if any(obj.path.startswith(f"mkdocstrings.{module}") for module in deprecated_modules): diff --git a/tests/test_extension.py b/tests/test_extension.py index dd3d7028..c283f9c0 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -198,7 +198,6 @@ def test_removing_duplicated_headings(ext_markdown: Markdown) -> None: assert output.count(">Heading one<") == 1 assert output.count(">Heading two<") == 1 assert output.count(">Heading three<") == 1 - assert output.count('class="mkdocstrings') == 0 def _assert_contains_in_order(items: list[str], string: str) -> None: From 2b4ed541bc707e55d959092d950ebeecc4fbd136 Mon Sep 17 00:00:00 2001 From: Nyuan Zhang Date: Fri, 11 Jul 2025 00:19:09 +0800 Subject: [PATCH 4/6] feat: Add I18N support (translations) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-645: https://github.com/mkdocstrings/mkdocstrings/pull/645 Co-authored-by: Timothée Mazzucotelli --- docs/schema.json | 6 ++++++ docs/usage/index.md | 12 ++++++++++++ src/mkdocstrings/_internal/extension.py | 8 +++++++- src/mkdocstrings/_internal/handlers/base.py | 18 +++++++++++++++--- src/mkdocstrings/_internal/plugin.py | 15 ++++++++++++++- tests/test_plugin.py | 2 ++ 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/docs/schema.json b/docs/schema.json index bd646f88..66197827 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -28,6 +28,12 @@ "type": "string", "default": "python" }, + "locale": { + "title": "The locale to use for translations.", + "markdownDescription": "https://mkdocstrings.github.io/usage/#global-options", + "type": "string", + "default": null + }, "enable_inventory": { "title": "Whether to enable inventory file generation.", "markdownDescription": "https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories", diff --git a/docs/usage/index.md b/docs/usage/index.md index d208b4b0..64588fdf 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -113,6 +113,7 @@ The above is equivalent to: - `handlers`: The handlers' global configuration. - `enable_inventory`: Whether to enable inventory file generation. See [Cross-references to other projects / inventories](#cross-references-to-other-projects-inventories) +- `locale`: The locale used for translations. See [Internationalization](#internationalization-i18n). - `enabled` **(New in version 0.20)**: Whether to enable the plugin. Defaults to `true`. Can be used to reduce build times when doing local development. Especially useful when used with environment variables (see example below). @@ -124,6 +125,7 @@ The above is equivalent to: enabled: !ENV [ENABLE_MKDOCSTRINGS, true] custom_templates: templates default_handler: python + locale: en handlers: python: options: @@ -141,6 +143,16 @@ The above is equivalent to: Some handlers accept additional global configuration. Check the documentation for your handler of interest in [Handlers](handlers.md). +## Internationalization (I18N) + +Some handlers support multiple languages. + +If the handler supports localization, the locale it uses is determined by the following order of precedence: + +- `locale` in [global options](#global-options) +- `theme.language`: used by the [MkDocs Material theme](https://squidfunk.github.io/mkdocs-material/setup/changing-the-language/) +- `theme.locale` in [MkDocs configuration](https://www.mkdocs.org/user-guide/configuration/#theme) + ## Cross-references Cross-references are written as Markdown *reference-style* links: diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index 83421ff8..f67b5cfc 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -23,6 +23,8 @@ from __future__ import annotations import re +from functools import partial +from inspect import signature from typing import TYPE_CHECKING, Any from warnings import warn from xml.etree.ElementTree import Element @@ -197,8 +199,12 @@ def _process_block( self._updated_envs.add(handler_name) _logger.debug("Rendering templates") + if "locale" in signature(handler.render).parameters: + render = partial(handler.render, locale=self._handlers._locale) + else: + render = handler.render # type: ignore[assignment] try: - rendered = handler.render(data, options) + rendered = render(data, options) except TemplateNotFound as exc: _logger.error( # noqa: TRY400 "Template '%s' not found for '%s' handler and theme '%s'.", diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index c87f473c..b5641959 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -319,20 +319,29 @@ def collect(self, identifier: str, options: HandlerOptions) -> CollectorItem: """ raise NotImplementedError - def render(self, data: CollectorItem, options: HandlerOptions) -> str: + def render(self, data: CollectorItem, options: HandlerOptions, *, locale: str | None = None) -> str: """Render a template using provided data and configuration options. Arguments: data: The collected data to render. options: The final configuration options. + locale: The locale to use for translations, if any. Returns: The rendered template as HTML. """ raise NotImplementedError - def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]]) -> str: # noqa: ARG002 - """Render backlinks.""" + def render_backlinks(self, backlinks: Mapping[str, Iterable[Backlink]], *, locale: str | None = None) -> str: # noqa: ARG002 + """Render backlinks. + + Parameters: + backlinks: A mapping of identifiers to backlinks. + locale: The locale to use for translations, if any. + + Returns: + The rendered backlinks as HTML. + """ return "" def teardown(self) -> None: @@ -578,6 +587,7 @@ def __init__( custom_templates: str | None = None, mdx: Sequence[str | Extension] | None = None, mdx_config: Mapping[str, Any] | None = None, + locale: str = "en", tool_config: Any, ) -> None: """Initialize the object. @@ -591,6 +601,7 @@ def __init__( custom_templates: The path to custom templates. mdx: A list of Markdown extensions to use. mdx_config: Configuration for the Markdown extensions. + locale: The locale to use for translations. tool_config: Tool configuration to pass down to handlers. """ self._theme = theme @@ -600,6 +611,7 @@ def __init__( self._mdx = mdx or [] self._mdx_config = mdx_config or {} self._handlers: dict[str, BaseHandler] = {} + self._locale = locale self._tool_config = tool_config self.inventory: Inventory = Inventory(project=inventory_project, version=inventory_version) diff --git a/src/mkdocstrings/_internal/plugin.py b/src/mkdocstrings/_internal/plugin.py index afc94490..2af14a7a 100644 --- a/src/mkdocstrings/_internal/plugin.py +++ b/src/mkdocstrings/_internal/plugin.py @@ -15,6 +15,8 @@ import os import re +from functools import partial +from inspect import signature from re import Match from typing import TYPE_CHECKING, Any from warnings import catch_warnings, simplefilter @@ -73,6 +75,8 @@ class PluginConfig(Config): """Whether to enable object inventory creation.""" enabled = opt.Type(bool, default=True) """Whether to enable the plugin. Default is true. If false, *mkdocstrings* will not collect or render anything.""" + locale = opt.Optional(opt.Type(str)) + """The locale to use for translations.""" class MkdocstringsPlugin(BasePlugin[PluginConfig]): @@ -131,6 +135,9 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: return config _logger.debug("Adding extension to the list") + locale = self.config.locale or config.theme.get("language") or config.theme.get("locale") or "en" + locale = str(locale).replace("_", "-") + handlers = Handlers( default=self.config.default_handler, handlers_config=self.config.handlers, @@ -140,6 +147,7 @@ def on_config(self, config: MkDocsConfig) -> MkDocsConfig | None: mdx_config=config.mdx_configs, inventory_project=config.site_name, inventory_version="0.0.0", # TODO: Find a way to get actual version. + locale=locale, tool_config=config, ) @@ -234,7 +242,12 @@ def repl(match: Match) -> str: if not backlinks: return "" - return handler.render_backlinks(backlinks) + if "locale" in signature(handler.render_backlinks).parameters: + render_backlinks = partial(handler.render_backlinks, locale=self.handlers._locale) + else: + render_backlinks = handler.render_backlinks # type: ignore[assignment] + + return render_backlinks(backlinks) for file in files: if file.page and file.page.content: diff --git a/tests/test_plugin.py b/tests/test_plugin.py index af8a5594..acb9556c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -57,6 +57,7 @@ def test_plugin_default_config(tmp_path: Path) -> None: "custom_templates": None, "enable_inventory": None, "enabled": True, + "locale": None, } @@ -77,4 +78,5 @@ def test_plugin_config_custom_templates(tmp_path: Path) -> None: "custom_templates": str(template_dir), "enable_inventory": None, "enabled": True, + "locale": None, } From f856160b03b2c27e1d75fdf4f315c273cb9d9247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 21 Jul 2025 20:49:21 +0200 Subject: [PATCH 5/6] feat: Add `data-skip-inventory` boolean attribute for elements to skip registration in local inventory Issue-671: https://github.com/mkdocstrings/mkdocstrings/issues/671 PR-774: https://github.com/mkdocstrings/mkdocstrings/pull/774 --- src/mkdocstrings/_internal/extension.py | 9 +++++++++ src/mkdocstrings/_internal/handlers/base.py | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/src/mkdocstrings/_internal/extension.py b/src/mkdocstrings/_internal/extension.py index f67b5cfc..00b112d3 100644 --- a/src/mkdocstrings/_internal/extension.py +++ b/src/mkdocstrings/_internal/extension.py @@ -251,6 +251,15 @@ def _process_headings(self, handler: BaseHandler, element: Element) -> None: for heading in headings: rendered_id = heading.attrib["id"] + + skip_inventory = "data-skip-inventory" in heading.attrib + if skip_inventory: + _logger.debug( + "Skipping heading with id %r because data-skip-inventory is present", + rendered_id, + ) + continue + # The title is registered to be used as tooltip by autorefs. self._autorefs.register_anchor(page, rendered_id, title=heading.text, primary=True) diff --git a/src/mkdocstrings/_internal/handlers/base.py b/src/mkdocstrings/_internal/handlers/base.py index b5641959..60910436 100644 --- a/src/mkdocstrings/_internal/handlers/base.py +++ b/src/mkdocstrings/_internal/handlers/base.py @@ -459,6 +459,7 @@ def do_heading( role: str | None = None, hidden: bool = False, toc_label: str | None = None, + skip_inventory: bool = False, **attributes: str, ) -> Markup: """Render an HTML heading and register it for the table of contents. For use inside templates. @@ -469,6 +470,7 @@ def do_heading( role: An optional role for the object bound to this heading. hidden: If True, only register it for the table of contents, don't render anything. toc_label: The title to use in the table of contents ('data-toc-label' attribute). + skip_inventory: Flag element to not be registered in the inventory (by setting a `data-skip-inventory` attribute). **attributes: Any extra HTML attributes of the heading. Returns: @@ -488,6 +490,8 @@ def do_heading( if toc_label is None: toc_label = content.unescape() if isinstance(content, Markup) else content el.set("data-toc-label", toc_label) + if skip_inventory: + el.set("data-skip-inventory", "true") if role: el.set("data-role", role) if content: From 2be445f054c1191d308af7ee0cc881e359f5a4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 23 Jul 2025 01:48:33 +0200 Subject: [PATCH 6/6] chore: Prepare release 0.30.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed973133..658ae74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.30.0](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.30.0) - 2025-07-23 + +[Compare with 0.29.1](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.1...0.30.0) + +### Features + +- Add `data-skip-inventory` boolean attribute for elements to skip registration in local inventory ([f856160](https://github.com/mkdocstrings/mkdocstrings/commit/f856160b03b2c27e1d75fdf4f315c273cb9d9247) by Bartosz Sławecki). [Issue-671](https://github.com/mkdocstrings/mkdocstrings/issues/671), [PR-774](https://github.com/mkdocstrings/mkdocstrings/pull/774) +- Add I18N support (translations) ([2b4ed54](https://github.com/mkdocstrings/mkdocstrings/commit/2b4ed541bc707e55d959092d950ebeecc4fbd136) by Nyuan Zhang). [PR-645](https://github.com/mkdocstrings/mkdocstrings/pull/645), Co-authored-by: Timothée Mazzucotelli + ## [0.29.1](https://github.com/mkdocstrings/mkdocstrings/releases/tag/0.29.1) - 2025-03-31 [Compare with 0.29.0](https://github.com/mkdocstrings/mkdocstrings/compare/0.29.0...0.29.1)