diff --git a/CHANGELOG.md b/CHANGELOG.md index ec519312..d31fb5ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,35 @@ 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.14.2](https://github.com/mkdocstrings/python/releases/tag/1.14.2) - 2025-02-03 + +[Compare with 1.14.1](https://github.com/mkdocstrings/python/compare/1.14.1...1.14.2) + +### Bug Fixes + +- Deactivate Pydantic logic if v1 is installed instead of v2 ([c5ecd70](https://github.com/mkdocstrings/python/commit/c5ecd702b04417fa3d862806d608ea627b2e8ed9) by Timothée Mazzucotelli). [Issue-240](https://github.com/mkdocstrings/python/issues/240) + +## [1.14.1](https://github.com/mkdocstrings/python/releases/tag/1.14.1) - 2025-02-03 + +[Compare with 1.14.0](https://github.com/mkdocstrings/python/compare/1.14.0...1.14.1) + +### Bug Fixes + +- Fix type errors with options during collection and docstring parsing ([15ca6d8](https://github.com/mkdocstrings/python/commit/15ca6d8cbe8187ae2938b3cc3a6134d10c94a3aa) by Timothée Mazzucotelli). + +## [1.14.0](https://github.com/mkdocstrings/python/releases/tag/1.14.0) - 2025-02-03 + +[Compare with 1.13.0](https://github.com/mkdocstrings/python/compare/1.13.0...1.14.0) + +### Features + +- Add `heading` and `toc_label` options ([7cabacf](https://github.com/mkdocstrings/python/commit/7cabacf13735dbc5066793baf5820d61cd342dc8) by Yann Van Crombrugge). [Issue-mkdocstrings-725](https://github.com/mkdocstrings/mkdocstrings/issues/725), [PR-236](https://github.com/mkdocstrings/python/pull/236) +- Add `force_inspection` option to force dynamic analysis ([83823be](https://github.com/mkdocstrings/python/commit/83823be2146d6a2ecedc5fe9c0cfd84098d780ca) by Uchechukwu Orji). [Issue-94](https://github.com/mkdocstrings/python/issues/94), [PR-231](https://github.com/mkdocstrings/python/pull/231) + +### Code Refactoring + +- Use dataclasses for configuration/options and automate schema generation ([5ebeda6](https://github.com/mkdocstrings/python/commit/5ebeda6fce1b1bc7cb3f5e27a5a90ac394a3de0c) by Timothée Mazzucotelli). + ## [1.13.0](https://github.com/mkdocstrings/python/releases/tag/1.13.0) - 2024-12-26 [Compare with 1.12.2](https://github.com/mkdocstrings/python/compare/1.12.2...1.13.0) @@ -227,12 +256,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Release Insiders features of the $500/month funding goal ([bd30106](https://github.com/mkdocstrings/python/commit/bd301061fe9c647f9b91c2c9b4baa784c304eca7) by Timothée Mazzucotelli). The features and projects related to *mkdocstrings-python* are: - + - [Cross-references for type annotations in signatures](https://mkdocstrings.github.io/python/usage/configuration/signatures/#signature_crossrefs) - [Symbol types in headings and table of contents](https://mkdocstrings.github.io/python/usage/configuration/headings/#show_symbol_type_toc) - [`griffe-inherited-docstrings`](https://mkdocstrings.github.io/griffe-inherited-docstrings/), a Griffe extension for inheriting docstrings - [`griffe2md`](https://mkdocstrings.github.io/griffe2md/), a tool to output API docs to Markdown using Griffe - + See the complete list of features and projects here: https://pawamoy.github.io/insiders/#500-plasmavac-user-guide. @@ -464,7 +493,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. You can see how to use the filter in this commit's changes: [f686f4e4](https://github.com/mkdocstrings/python/commit/f686f4e4599cea64686d4ef4863b507dd096a513). - + **We take this as an opportunity to go out of beta and bump the version to 1.0.0. This will allow users to rely on semantic versioning.** diff --git a/config/pytest.ini b/config/pytest.ini index 9d75f5c6..4f43c18e 100644 --- a/config/pytest.ini +++ b/config/pytest.ini @@ -10,8 +10,7 @@ testpaths = # action:message_regex:warning_class:module_regex:line filterwarnings = error - # TODO: remove once pytest-xdist 4 is released + # TODO: Remove once pytest-xdist 4 is released. ignore:.*rsyncdir:DeprecationWarning:xdist - # TODO: remove once Griffe releases v1 - ignore:.*`get_logger`:DeprecationWarning:_griffe - ignore:.*`name`:DeprecationWarning:_griffe + # TODO: Remove once mkdocstrings stops setting fallback function. + ignore:.*fallback anchor function:DeprecationWarning:mkdocstrings diff --git a/docs/.glossary.md b/docs/.glossary.md index 917c95c4..e11a6781 100644 --- a/docs/.glossary.md +++ b/docs/.glossary.md @@ -6,7 +6,7 @@ [Griffe]: https://github.com/mkdocstrings/griffe [ReadTheDocs Sphinx theme]: https://sphinx-rtd-theme.readthedocs.io/en/stable/index.html [Spacy's documentation]: https://spacy.io/api/doc/ -[Black]: https://pypi.org/project/black/ +[Black]: https://pypi.org/project/black/ [Material for MkDocs]: https://squidfunk.github.io/mkdocs-material [Ruff]: https://docs.astral.sh/ruff diff --git a/docs/.overrides/main.html b/docs/.overrides/main.html index 1e956857..5bedfd03 100644 --- a/docs/.overrides/main.html +++ b/docs/.overrides/main.html @@ -1,13 +1,13 @@ {% extends "base.html" %} {% block announce %} - + Fund this project through sponsorship {% include ".icons/octicons/heart-fill-16.svg" %} — - + Follow @pawamoy on diff --git a/docs/.overrides/partials/comments.html b/docs/.overrides/partials/comments.html index 0dedc405..1d5c6051 100644 --- a/docs/.overrides/partials/comments.html +++ b/docs/.overrides/partials/comments.html @@ -31,7 +31,7 @@

Feedback

: "light" // Instruct Giscus to set theme - giscus.setAttribute("data-theme", theme) + giscus.setAttribute("data-theme", theme) } // Register event handlers after documented loaded diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 17a11ce4..288075b1 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -161,7 +161,7 @@ You can cancel your sponsorship anytime.[^5] 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: #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. @@ -221,7 +221,7 @@ by the [ISC License][license]. However, we kindly ask you to respect our - 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 + but please don't make the source code public, as it would counteract the sponsorware strategy. - If you cancel your subscription, you're automatically removed as a diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md index 0e4628ca..3588e691 100644 --- a/docs/insiders/installation.md +++ b/docs/insiders/installation.md @@ -42,21 +42,21 @@ Or using HTTPS: pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/mkdocstrings-python.git ``` ->? NOTE: **How to get a GitHub personal access token** +>? 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: -> +> > 1. Go to https://github.com/settings/tokens > 2. Click on [Generate a new token] > 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. diff --git a/docs/schema.json b/docs/schema.json deleted file mode 100644 index e1863d26..00000000 --- a/docs/schema.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft-07/schema", - "title": "Python handler for mkdocstrings.", - "type": "object", - "properties": { - "python": { - "markdownDescription": "https://mkdocstrings.github.io/python/", - "type": "object", - "properties": { - "import": { - "title": "Inventories to import.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/#global-only-options", - "type": "array", - "items": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "url": { - "title": "URL of the inventory file.", - "type": "string" - }, - "base_url": { - "title": "Base URL used to build references URLs.", - "type": "string" - }, - "domains": { - "title": "Domains to import from the inventory.", - "description": "If not defined it will only import 'py' domain.", - "type": "array", - "items": { - "type": "string" - } - } - } - } - ] - } - }, - "paths": { - "title": "Local absolute/relative paths (relative to mkdocs.yml) to search packages into.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/#paths", - "type": "array", - "items": { - "type": "string", - "format": "path" - } - }, - "load_external_modules": { - "title": "Load external modules to resolve aliases.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/#load_external_modules", - "type": "boolean", - "default": false - }, - "options": { - "title": "Options for collecting and rendering objects.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/#globallocal-options", - "type": "object", - "properties": { - "docstring_style": { - "title": "The docstring style to use when parsing docstrings.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#docstring_style", - "enum": [ - "google", - "numpy", - "sphinx" - ], - "default": "google" - }, - "docstring_options": { - "title": "The options for the docstring parser.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#docstring_options", - "default": null, - "items": { - "$ref": "https://raw.githubusercontent.com/mkdocstrings/griffe/master/docs/schema-docstrings-options.json" - } - }, - "show_root_heading": { - "title": "Show the heading of the object at the root of the documentation tree.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_root_heading", - "type": "boolean", - "default": false - }, - "show_root_toc_entry": { - "title": "If the root heading is not shown, at least add a ToC entry for it.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_root_toc_entry", - "type": "boolean", - "default": true - }, - "show_root_full_path": { - "title": "Show the full Python path for the root object heading.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_root_full_path", - "type": "boolean", - "default": true - }, - "show_root_members_full_path": { - "title": "Show the full Python path of the root members.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_root_members_full_path", - "type": "boolean", - "default": false - }, - "show_object_full_path": { - "title": "Show the full Python path of every object.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_object_full_path", - "type": "boolean", - "default": false - }, - "show_symbol_type_heading": { - "title": "Show the symbol type in headings (e.g. mod, class, func and attr).", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_symbol_type_heading", - "type": "boolean", - "default": false - }, - "show_symbol_type_toc": { - "title": "Show the symbol type in the Table of Contents (e.g. mod, class, func and attr).", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_symbol_type_toc", - "type": "boolean", - "default": false - }, - "show_category_heading": { - "title": "When grouped by categories, show a heading for each category.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#show_category_heading", - "type": "boolean", - "default": false - }, - "show_if_no_docstring": { - "title": "Show the object heading even if it has no docstring or children with docstrings.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_if_no_docstring", - "type": "boolean", - "default": false - }, - "show_signature": { - "title": "Show methods and functions signatures.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/signatures/#show_signature", - "type": "boolean", - "default": true - }, - "show_signature_annotations": { - "title": "Show the type annotations in methods and functions signatures.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/signatures/#show_signature_annotations", - "type": "boolean", - "default": false - }, - "separate_signature": { - "title": "Whether to put the whole signature in a code block below the heading. If a formatter (Black or Ruff) is installed, the signature is also formatted using it.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/signatures/#separate_signature", - "type": "boolean", - "default": false - }, - "line_length": { - "title": "Maximum line length when formatting code/signatures.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/signatures/#line_length", - "type": "integer", - "default": 60 - }, - "merge_init_into_class": { - "title": "Whether to merge the `__init__` method into the class' signature and docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#merge_init_into_class", - "type": "boolean", - "default": false - }, - "show_docstring_attributes": { - "title": "Whether to display the \"Attributes\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_attributes", - "type": "boolean", - "default": true - }, - "show_docstring_description": { - "title": "Whether to display the textual block (including admonitions) in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_description", - "type": "boolean", - "default": true - }, - "show_docstring_examples": { - "title": "Whether to display the \"Examples\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_examples", - "type": "boolean", - "default": true - }, - "show_docstring_other_parameters": { - "title": "Whether to display the \"Other Parameters\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_other_parameters", - "type": "boolean", - "default": true - }, - "show_docstring_parameters": { - "title": "Whether to display the \"Parameters\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_parameters", - "type": "boolean", - "default": true - }, - "show_docstring_raises": { - "title": "Whether to display the \"Raises\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_raises", - "type": "boolean", - "default": true - }, - "show_docstring_receives": { - "title": "Whether to display the \"Receives\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_receives", - "type": "boolean", - "default": true - }, - "show_docstring_returns": { - "title": "Whether to display the \"Returns\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_returns", - "type": "boolean", - "default": true - }, - "show_docstring_warns": { - "title": "Whether to display the \"Warns\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_warns", - "type": "boolean", - "default": true - }, - "show_docstring_yields": { - "title": "Whether to display the \"Yields\" section in the object's docstring.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#show_docstring_yields", - "type": "boolean", - "default": true - }, - "show_source": { - "title": "Show the source code of this object.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/general/#show_source", - "type": "boolean", - "default": true - }, - "show_bases": { - "title": "Show the base classes of a class.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/general/#show_bases", - "type": "boolean", - "default": true - }, - "show_submodules": { - "title": "When rendering a module, show its submodules recursively.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/members/#show_submodules", - "type": "boolean", - "default": false - }, - "group_by_category": { - "title": "Group the object's children by categories: attributes, classes, functions, and modules.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/members/#group_by_category", - "type": "boolean", - "default": true - }, - "heading_level": { - "title": "The initial heading level to use.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/headings/#heading_level", - "type": "integer", - "default": 2 - }, - "members_order": { - "title": "The members ordering to use.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/members/#members_order", - "enum": [ - "alphabetical", - "source" - ], - "default": "alphabetical" - }, - "docstring_section_style": { - "title": "The style used to render docstring sections.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/docstrings/#docstring_section_style", - "enum": [ - "list", - "spacy", - "table" - ], - "default": "table" - }, - "members": { - "title": "An explicit list of members to render.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/members/#members", - "type": [ - "boolean", - "array" - ], - "default": null - }, - "filters": { - "title": "A list of filters applied to filter objects based on their name. A filter starting with `!` will exclude matching objects instead of including them. The `members` option takes precedence over `filters` (filters will still be applied recursively to lower members in the hierarchy).", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/members/#filters", - "type": "array", - "default": [ - "!^_[^_]" - ] - }, - "annotations_path": { - "title": "The verbosity for annotations path.", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/signatures/#annotations_path", - "enum": [ - "brief", - "source" - ], - "default": "brief" - }, - "preload_modules": { - "title": "Pre-load modules. It permits to resolve aliases pointing to these modules (packages), and therefore render members of an object that are external to the given object (originating from another package).", - "markdownDescription": "https://mkdocstrings.github.io/python/usage/configuration/general/#preload_modules", - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false -} \ No newline at end of file diff --git a/docs/usage/configuration/docstrings.md b/docs/usage/configuration/docstrings.md index 0cf2dac1..ae925f23 100644 --- a/docs/usage/configuration/docstrings.md +++ b/docs/usage/configuration/docstrings.md @@ -1,5 +1,6 @@ # Docstrings options +[](){#option-docstring_style} ## `docstring_style` - **:octicons-package-24: Type [`str`][] :material-equal: `"google"`{ title="default value" }** @@ -29,6 +30,9 @@ plugins: docstring_style: numpy ``` +WARNING: **The style is applied to the specified object only, not its members.** Local `docstring_style` options (in `:::` instructions) will only be applied to the specified object, and not its members. Instead of changing the style when rendering, we strongly recommend to *set the right style as early as possible*, for example by using the [auto-style](https://mkdocstrings.github.io/griffe/reference/docstrings/#auto-style) (sponsors only), or with a custom Griffe extension + + /// admonition | Preview type: preview @@ -81,6 +85,7 @@ def greet(name: str) -> str: //// /// +[](){#option-docstring_options} ## `docstring_options` - **:octicons-package-24: Type [`dict`][] :material-equal: `{}`{ title="default value" }** @@ -155,6 +160,7 @@ ok //// /// +[](){#option-docstring_section_style} ## `docstring_section_style` - **:octicons-package-24: Type [`str`][] :material-equal: `"table"`{ title="default value" }** @@ -246,6 +252,7 @@ by reserving more horizontal space on the second column. //// /// +[](){#option-merge_init_into_class} ## `merge_init_into_class` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -317,6 +324,7 @@ class Thing: //// /// +[](){#option-relative_crossrefs} ## `relative_crossrefs` [:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — @@ -429,6 +437,7 @@ class Class: INFO: **There is an alternative, third-party Python handler that handles relative references: [mkdocstrings-python-xref](https://github.com/analog-garage/mkdocstrings-python-xref).** +[](){#option-scoped_crossrefs} ## `scoped_crossrefs` [:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — @@ -449,7 +458,7 @@ The following order is applied when resolving a name in a given scope: In practice, it means that the name is first looked up in members, then it is compared against the parent name (only if it's a class), then it is looked up in siblings. It continues climbing up the object tree until there's no parent, in which case it raises a name resolution error. -Cross-referencing an imported object will directly link to this object if the objects inventory of the project it comes from was [loaded][import]. You won't be able to cross-reference it within your own documentation with scoped references, if you happen to be rendering this external object too. In that case, you can use an absolute reference or a [relative][relative_crossrefs] one instead. +Cross-referencing an imported object will directly link to this object if the objects inventory of the project it comes from was [loaded][inventories]. You won't be able to cross-reference it within your own documentation with scoped references, if you happen to be rendering this external object too. In that case, you can use an absolute reference or a [relative][relative_crossrefs] one instead. Another limitation is that you won't be able to reference an external package if its name can be resolved in the current object's scope. @@ -538,6 +547,7 @@ class Class: /// +[](){#option-show_if_no_docstring} ## `show_if_no_docstring` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -607,6 +617,7 @@ class ClassWithoutDocstring: //// /// +[](){#option-show_docstring_attributes} ## `show_docstring_attributes` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -659,6 +670,7 @@ class Class: //// /// +[](){#option-show_docstring_functions} ## `show_docstring_functions` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -733,7 +745,7 @@ class Class: //// /// - +[](){#option-show_docstring_classes} ## `show_docstring_classes` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -792,6 +804,7 @@ class Class: //// /// +[](){#option-show_docstring_modules} ## `show_docstring_modules` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -848,6 +861,7 @@ Modules: //// /// +[](){#option-show_docstring_description} ## `show_docstring_description` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -911,6 +925,7 @@ class Class: //// /// +[](){#option-show_docstring_examples} ## `show_docstring_examples` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -964,6 +979,7 @@ hello //// /// +[](){#option-show_docstring_other_parameters} ## `show_docstring_other_parameters` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -1014,6 +1030,7 @@ def do_something(**kwargs): //// /// +[](){#option-show_docstring_parameters} ## `show_docstring_parameters` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -1064,6 +1081,7 @@ def do_something(whatever: int = 0): //// /// +[](){#option-show_docstring_raises} ## `show_docstring_raises` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -1115,6 +1133,7 @@ def raise_runtime_error(): //// /// +[](){#option-show_docstring_receives} ## `show_docstring_receives` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -1174,6 +1193,7 @@ def iter_skip( //// /// +[](){#option-show_docstring_returns} ## `show_docstring_returns` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -1225,6 +1245,7 @@ def rand() -> int: //// /// +[](){#option-show_docstring_warns} ## `show_docstring_warns` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -1276,6 +1297,7 @@ def warn(): //// /// +[](){#option-show_docstring_yields} ## `show_docstring_yields` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** diff --git a/docs/usage/configuration/general.md b/docs/usage/configuration/general.md index e2a6e169..3983a616 100644 --- a/docs/usage/configuration/general.md +++ b/docs/usage/configuration/general.md @@ -1,5 +1,6 @@ # General options +[](){#option-allow_inspection} ## `allow_inspection` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -18,6 +19,10 @@ and sometimes the collected data is inaccurate (depending on the tool that was used to compile the module) or too low-level/technical for API documentation. +See also [`force_inspection`](#force_inspection). + +WARNING: **Packages are loaded only once.** When mkdocstrings-python collects data from a Python package (thanks to [Griffe](https://mkdocstrings.github.io/griffe/)), it collects *the entire package* and *caches it*. Next time an object from the same package is rendered, the package is retrieved from the cache and not collected again. The `allow_inspection` option will therefore only have an effect the first time a package is collected, and will do nothing for objects rendered afterwards. + ```yaml title="in mkdocs.yml (global configuration)" plugins: - mkdocstrings: @@ -55,6 +60,230 @@ plugins: //// /// +[](){#option-extensions} +## `extensions` + +- **:octicons-package-24: Type list[str | dict[str, dict[str, Any]]] :material-equal: `[]`{ title="default value" }** + + +The `extensions` option lets you enable [Griffe extensions](https://mkdocstrings.github.io/griffe/extensions/), which enhance or modify the data collected from Python sources (or compiled modules). + +Elements in the list can be strings or dictionaries. + +Strings denote the path to an extension module, like `griffe_typingdoc`, or to an extension class directly, like `griffe_typingdoc.TypingDocExtension`. When using a module path, all extensions within that module will be loaded and enabled. Strings can also be the path to a Python module, and a class name separated with `:`, like `scripts/griffe_extensions.py` or `scripts/griffe_extensions.py:MyExtension`. + +Dictionaries have a single key, which is the module/class path (as a dot-separated qualifier or file path and colon-separated class name, like above), and its value is another dictionary specifying options that will be passed when to class constructors when instantiating extensions. + +WARNING: **Packages are loaded only once.** When mkdocstrings-python collects data from a Python package (thanks to [Griffe](https://mkdocstrings.github.io/griffe/)), it collects *the entire package* and *caches it*. Next time an object from the same package is rendered, the package is retrieved from the cache and not collected again. Only the extensions specified the first time the package is loaded will be used. You cannot use a different set of extensions for specific objects rendered afterwards, and you cannot deactivate extensions for objects rendered afterwards either. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + extensions: + - griffe_sphinx + - griffe_pydantic: {schema: true} + - scripts/exts.py:DynamicDocstrings: + paths: [mypkg.mymod.myobj] +``` + +```md title="or in docs/some_page.md (local configuration)" +::: your_package.your_module.your_func + options: + extensions: + - griffe_typingdoc +``` + +[](){#option-extra} +## `extra` + +- **:octicons-package-24: Type [`dict`][] :material-equal: `{}`{ title="default value" }** + + +The `extra` option lets you inject additional variables into the Jinja context used when rendering templates. You can then use this extra context in your [overridden templates][templates]. + +Local `extra` options will be merged into the global `extra` option: + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + extra: + hello: world +``` + +```md title="in docs/some_page.md (local configuration)" +::: your_package.your_module.your_func + options: + extra: + foo: bar +``` + +...will inject both `hello` and `foo` into the Jinja context when rendering `your_package.your_module.your_func`. + +> WARNING: Previously, extra options were supported directly under the `options` key. +> +> ```yaml +> plugins: +> - mkdocstrings: +> handlers: +> python: +> options: +> hello: world +> ``` +> +> Now that we introduced optional validation of options and automatic JSON schema generation thanks to Pydantic, we require extra options to be put under `options.extra`. Extra options directly under `options` are still supported, but deprecated, and will emit deprecation warnings. Support will be removed in a future version of mkdocstrings-python. + +[](){#option-find_stubs_package} +## `find_stubs_package` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + + +When looking for documentation specified in [autodoc instructions][autodoc syntax] (`::: identifier`), also look for +the stubs package as defined in [PEP 561](https://peps.python.org/pep-0561/) if it exists. This is useful when +most of your documentation is separately provided by such a package and not inline in your main package. + +WARNING: **Packages are loaded only once.** When mkdocstrings-python collects data from a Python package (thanks to [Griffe](https://mkdocstrings.github.io/griffe/)), it collects *the entire package* and *caches it*. Next time an object from the same package is rendered, the package is retrieved from the cache and not collected again. The `find_stubs_package` option will therefore only have an effect the first time a package is collected, and will do nothing for objects rendered afterwards. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + find_stubs_package: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: your_package.your_module.your_func + options: + find_stubs_package: true +``` + +```python title="your_package/your_module.py" + +def your_func(a, b): + # Function code + ... + +# rest of your code +``` + +```python title="your_package-stubs/your_module.pyi" + +def your_func(a: int, b: str): + """ + + """ + ... + +# rest of your code +``` + +/// admonition | Preview + type: preview + +//// tab | With find_stubs_package +

your_func

+

Function docstring

+//// + +//// tab | Without find_stubs_package +

your_func

+//// +/// + +[](){#option-force_inspection} +## `force_inspection` + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + + +Whether to force inspecting modules (importing them) even if their source code is available. + +This option is useful when you know that dynamic analysis (inspection) yields better results than static analysis. Do not use this blindly: the recommended approach is to write a Griffe extension that will improve extracted API data. See [How to selectively inspect objects](https://mkdocstrings.github.io/griffe/guide/users/how-to/selectively-inspect/). + +See also [`allow_inspection`](#allow_inspection). + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + force_inspection: false +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.object + options: + force_inspection: true +``` + +WARNING: **Packages are loaded only once.** When mkdocstrings-python collects data from a Python package (thanks to [Griffe](https://mkdocstrings.github.io/griffe/)), it collects *the entire package* and *caches it*. Next time an object from the same package is rendered, the package is retrieved from the cache and not collected again. The `force_inspection` option will therefore only have an effect the first time a package is collected, and will do nothing for objects rendered afterwards. + +[](){#option-preload_modules} +## `preload_modules` + +- **:octicons-package-24: Type list[str] | None :material-equal: `None`{ title="default value" }** + + +Pre-load modules that are not specified directly in [autodoc instructions][autodoc syntax] (`::: identifier`). +It is useful when you want to render documentation for a particular member of an object, +and this member is imported from another package than its parent. + +For an imported member to be rendered, +you need to add it to the [`__all__`][__all__] attribute of the importing module. +The package from which the imported object originates must be accessible to the handler +(see [Finding modules](../index.md#finding-modules)). + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + preload_modules: + - their_package +``` + +```md title="or in docs/some_page.md (local configuration)" +::: your_package.your_module + options: + preload_modules: + - their_package +``` + +```python title="your_package/your_module.py" +from their_package.their_module import their_object + +__all__ = ["their_object"] + +# rest of your code +``` + +/// admonition | Preview + type: preview + +//// tab | With preloaded modules +

your_module

+

Docstring of your module.

+

their_object

+

Docstring of their object.

+//// + +//// tab | Without preloaded modules +

your_module

+

Docstring of your module.

+//// +/// + +[](){#option-show_bases} ## `show_bases` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -92,6 +321,7 @@ plugins: //// /// +[](){#option-show_inheritance_diagram} ## `show_inheritance_diagram` [:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — @@ -177,6 +407,7 @@ Mixin2A --> Mixin2B because these classes do not exist in our documentation.* /// +[](){#option-show_source} ## `show_source` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -221,115 +452,3 @@ def some_function():

Docstring of the function.

//// /// - -## `preload_modules` - -- **:octicons-package-24: Type list[str] | None :material-equal: `None`{ title="default value" }** - - -Pre-load modules that are not specified directly in [autodoc instructions][autodoc syntax] (`::: identifier`). -It is useful when you want to render documentation for a particular member of an object, -and this member is imported from another package than its parent. - -For an imported member to be rendered, -you need to add it to the [`__all__`][__all__] attribute of the importing module. -The package from which the imported object originates must be accessible to the handler -(see [Finding modules](../index.md#finding-modules)). - -```yaml title="in mkdocs.yml (global configuration)" -plugins: -- mkdocstrings: - handlers: - python: - options: - preload_modules: - - their_package -``` - -```md title="or in docs/some_page.md (local configuration)" -::: your_package.your_module - options: - preload_modules: - - their_package -``` - -```python title="your_package/your_module.py" -from their_package.their_module import their_object - -__all__ = ["their_object"] - -# rest of your code -``` - -/// admonition | Preview - type: preview - -//// tab | With preloaded modules -

your_module

-

Docstring of your module.

-

their_object

-

Docstring of their object.

-//// - -//// tab | Without preloaded modules -

your_module

-

Docstring of your module.

-//// -/// - -## `find_stubs_package` - -- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** - - -When looking for documentation specified in [autodoc instructions][autodoc syntax] (`::: identifier`), also look for -the stubs package as defined in [PEP 561](https://peps.python.org/pep-0561/) if it exists. This is useful when -most of your documentation is separately provided by such a package and not inline in your main package. - -```yaml title="in mkdocs.yml (global configuration)" -plugins: -- mkdocstrings: - handlers: - python: - options: - find_stubs_package: true -``` - -```md title="or in docs/some_page.md (local configuration)" -::: your_package.your_module.your_func - options: - find_stubs_package: true -``` - -```python title="your_package/your_module.py" - -def your_func(a, b): - # Function code - ... - -# rest of your code -``` - -```python title="your_package-stubs/your_module.pyi" - -def your_func(a: int, b: str): - """ - - """ - ... - -# rest of your code -``` - -/// admonition | Preview - type: preview - -//// tab | With find_stubs_package -

your_func

-

Function docstring

-//// - -//// tab | Without find_stubs_package -

your_func

-//// -/// diff --git a/docs/usage/configuration/headings.md b/docs/usage/configuration/headings.md index 467779e4..b4314b77 100644 --- a/docs/usage/configuration/headings.md +++ b/docs/usage/configuration/headings.md @@ -1,5 +1,22 @@ # Headings options +[](){#option-heading} +## `heading` + +- **:octicons-package-24: Type [`str`][] :material-equal: `""`{ title="default value" }** + + +A custom string to use as the heading of the root object (i.e. the object specified directly after the identifier `:::`). This will override the default heading generated by the plugin. See also the [`toc_label` option][option-toc_label]. + +WARNING: **Not advised to be used as a global configuration option.** This option is not advised to be used as a global configuration option, as it will override the default heading for all objects. It is recommended to use it only in specific cases where you want to override the heading for a specific object. + +```md title="in docs/some_page.md (local configuration)" +::: path.to.module + options: + heading: "My fancy module" +``` + +[](){#option-heading_level} ## `heading_level` - **:octicons-package-24: Type [`int`][] :material-equal: `2`{ title="default value" }** @@ -57,6 +74,7 @@ plugins: //// /// +[](){#option-parameter_headings} ## `parameter_headings` [:octicons-tag-24: Insiders 1.6.0](../../insiders/changelog.md#1.6.0) @@ -179,6 +197,7 @@ To customize symbols, see [Customizing symbol types](../customization.md/#symbol /// +[](){#option-show_root_heading} ## `show_root_heading` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -242,6 +261,7 @@ plugins: //// /// +[](){#option-show_root_toc_entry} ## `show_root_toc_entry` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -286,19 +306,20 @@ More text. type: preview //// tab | With ToC entry -**Table of contents** -[Some heading](#permalink-to-some-heading){ title="#permalink-to-some-heading" } -[`object`](#permalink-to-object){ title="#permalink-to-object" } -[Other heading](#permalink-to-other-heading){ title="#permalink-to-other-heading" } +**Table of contents** +[Some heading](#permalink-to-some-heading){ title="#permalink-to-some-heading" } +[`object`](#permalink-to-object){ title="#permalink-to-object" } +[Other heading](#permalink-to-other-heading){ title="#permalink-to-other-heading" } //// //// tab | Without ToC entry -**Table of contents** -[Some heading](#permalink-to-some-heading){ title="#permalink-to-some-heading" } +**Table of contents** +[Some heading](#permalink-to-some-heading){ title="#permalink-to-some-heading" } [Other heading](#permalink-to-other-heading){ title="#permalink-to-other-heading" } //// /// +[](){#option-show_root_full_path} ## `show_root_full_path` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -344,6 +365,7 @@ plugins: //// /// +[](){#option-show_root_members_full_path} ## `show_root_members_full_path` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -392,6 +414,7 @@ plugins: //// /// +[](){#option-show_object_full_path} ## `show_object_full_path` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -400,7 +423,7 @@ plugins: Show the full Python path of every object. Same as for [`show_root_members_full_path`][], -but for every member, recursively. This option takes precedence over +but for every member, recursively. This option takes precedence over [`show_root_members_full_path`][]: `show_root_members_full_path` | `show_object_full_path` | Direct root members path @@ -445,6 +468,7 @@ plugins: //// /// +[](){#option-show_category_heading} ## `show_category_heading` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -454,7 +478,7 @@ When [grouped by categories][group_by_category], show a heading for each categor These category headings will appear in the table of contents, allowing you to link to them using their permalinks. -WARNING: **Not recommended with deeply nested object** +WARNING: **Not recommended with deeply nested objects.** When injecting documentation for deeply nested objects, you'll quickly run out of heading levels, and the objects at the bottom of the tree risk all getting documented @@ -510,6 +534,7 @@ plugins: //// /// +[](){#option-show_symbol_type_heading} ## `show_symbol_type_heading` [:octicons-tag-24: Insiders 1.1.0](../../insiders/changelog.md#1.1.0) @@ -574,6 +599,7 @@ plugins: //// /// +[](){#option-show_symbol_type_toc} ## `show_symbol_type_toc` [:octicons-tag-24: Insiders 1.1.0](../../insiders/changelog.md#1.1.0) @@ -637,3 +663,22 @@ plugins: //// /// + +[](){#option-toc_label} +## `toc_label` + +- **:octicons-package-24: Type [`str`][] :material-equal: `""`{ title="default value" }** + + +A custom string to use as the label in the Table of Contents for the root object (i.e. the one specified directly after the identifier `:::`). This will override the default label generated by the plugin. See also the [`heading` option][option-heading]. + +WARNING: **Not advised to be used as a global configuration option.** This option is not advised to be used as a global configuration option, as it will override the default label for all objects. It is recommended to use it only in specific cases where you want to override the label for a specific object. + +NOTE: **Use with/without `heading`.** If you use this option without specifying a custom `heading`, the default heading will be used in the page, but the label in the Table of Contents will be the one you specified. By providing both an option for `heading` and `toc_label`, we leave the customization entirely up to you. + +```md title="in docs/some_page.md (local configuration)" +::: path.to.module + options: + heading: "My fancy module" + toc_label: "My fancy module" +``` diff --git a/docs/usage/configuration/members.md b/docs/usage/configuration/members.md index 220a26fe..363f7e0a 100644 --- a/docs/usage/configuration/members.md +++ b/docs/usage/configuration/members.md @@ -1,5 +1,6 @@ # Members options +[](){#option-members} ## `members` - **:octicons-package-24: Type list[str] | @@ -95,6 +96,7 @@ this_attribute = 0 INFO: **The default behavior (with unspecified `members` or `members: null`) is to use [`filters`][].** +[](){#option-inherited_members} ## `inherited_members` - **:octicons-package-24: Type list[str] | @@ -259,6 +261,7 @@ class Main(Base): /// +[](){#option-members_order} ## `members_order` - **:octicons-package-24: Type [`str`][] :material-equal: `"alphabetical"`{ title="default value" }** @@ -329,6 +332,7 @@ def function_c(): //// /// +[](){#option-filters} ## `filters` - **:octicons-package-24: Type list[str] | None :material-equal: `["!^_[^_]"]`{ title="default value" }** @@ -427,6 +431,7 @@ Here are some common filters that you might to want to use. - `["!^_[^_]"]`: exclude all private/protected objects, keep special ones (default filters) /// +[](){#option-group_by_category} ## `group_by_category` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -496,6 +501,7 @@ def function_d(): //// /// +[](){#option-show_submodules} ## `show_submodules` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -550,6 +556,7 @@ package //// /// +[](){#option-summary} ## `summary` [:octicons-tag-24: Insiders 1.2.0](../../insiders/changelog.md#1.2.0) @@ -643,6 +650,7 @@ plugins: //// /// +[](){#option-show_labels} ## `show_labels` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** diff --git a/docs/usage/configuration/signatures.md b/docs/usage/configuration/signatures.md index 879db6b1..c97cb5a6 100644 --- a/docs/usage/configuration/signatures.md +++ b/docs/usage/configuration/signatures.md @@ -1,5 +1,6 @@ # Signatures options +[](){#option-annotations_path} ## `annotations_path` - **:octicons-package-24: Type [`str`][] :material-equal: `"brief"`{ title="default value" }** @@ -146,6 +147,7 @@ def convert(text: str, md: Markdown) -> Markup: //// /// +[](){#option-line_length} ## `line_length` - **:octicons-package-24: Type [`int`][] :material-equal: `60`{ title="default value" }** @@ -198,11 +200,12 @@ plugins: //// /// +[](){#option-modernize_annotations} ## `modernize_annotations` [:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — [:octicons-tag-24: Insiders 1.8.0](../../insiders/changelog.md#1.8.0) — -**This feature also requires +**This feature also requires [Griffe Insiders](https://mkdocstrings.github.io/griffe/insiders/) to be installed.** @@ -283,8 +286,7 @@ plugins: /// - - +[](){#option-show_signature} ## `show_signature` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -323,6 +325,7 @@ plugins: //// /// +[](){#option-show_signature_annotations} ## `show_signature_annotations` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -377,6 +380,7 @@ function(param1, param2=None) //// /// +[](){#option-separate_signature} ## `separate_signature` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -429,6 +433,7 @@ function(param1, param2=None) //// /// +[](){#option-signature_crossrefs} ## `signature_crossrefs` [:octicons-tag-24: Insiders 1.0.0](../../insiders/changelog.md#1.0.0) @@ -476,6 +481,7 @@ plugins: //// /// +[](){#option-unwrap_annotated} ## `unwrap_annotated` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** diff --git a/docs/usage/customization.md b/docs/usage/customization.md index 907809c8..9e13da66 100644 --- a/docs/usage/customization.md +++ b/docs/usage/customization.md @@ -371,9 +371,9 @@ Available context: #### Docstring sections In `docstring/attributes.html`, -`docstring/functions.html`, -`docstring/classes.html`, -`docstring/modules.html`, +`docstring/functions.html`, +`docstring/classes.html`, +`docstring/modules.html`, `docstring/other_parameters.html`, `docstring/parameters.html`, `docstring/raises.html`, diff --git a/docs/usage/docstrings/google.md b/docs/usage/docstrings/google.md index de35d46e..1c843a3b 100644 --- a/docs/usage/docstrings/google.md +++ b/docs/usage/docstrings/google.md @@ -17,11 +17,11 @@ For example: This admonition has a custom title! """ ``` - + === "Result" NOTE: It looks like a section, but it will be rendered as an admonition. - TIP: **You can even choose a title.** + TIP: **You can even choose a title.** This admonition has a custom title! See [Napoleon's documentation](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). diff --git a/docs/usage/index.md b/docs/usage/index.md index 57b9fdc1..84110936 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -1,6 +1,6 @@ # Usage -TIP: **This is the documentation for the NEW Python handler.** +TIP: **This is the documentation for the NEW Python handler.** To read the documentation for the LEGACY handler, go to the [legacy handler documentation](https://mkdocstrings.github.io/python-legacy). @@ -75,10 +75,11 @@ plugins: Some options are **global only**, and go directly under the handler's name. -#### `import` +[](){#setting-inventories} +#### `inventories` -This option is used to import Sphinx-compatible objects inventories from other -documentation sites. For example, you can import the standard library +This option is used to load Sphinx-compatible objects inventories from other +documentation sites. For example, you can load the standard library objects inventory like this: ```yaml title="mkdocs.yml" @@ -90,19 +91,19 @@ plugins: - https://docs.python-requests.org/en/master/objects.inv ``` -When importing an inventory, you enable automatic cross-references +When loading an inventory, you enable automatic cross-references to other documentation sites like the standard library docs -or any third-party package docs. Typically, you want to import +or any third-party package docs. Typically, you want to load the inventories of your project's dependencies, at least those -that are used in the public API. +that are used in the public API. See [*mkdocstrings*' documentation on inventories][inventories] for more details. [inventories]: https://mkdocstrings.github.io/usage/#cross-references-to-other-projects-inventories -Additionally, the Python handler accepts a `domains` option in the import items, -which allows to select the inventory domains to select. +Additionally, the Python handler accepts a `domains` option in the inventory options, +which allows to select the inventory domains to load. By default the Python handler only selects the `py` domain (for Python objects). You might find useful to also enable the [`std` domain][std domain]: @@ -113,29 +114,12 @@ plugins: - mkdocstrings: handlers: python: - import: + inventories: - url: https://docs.python-requests.org/en/master/objects.inv domains: [std, py] ``` -NOTE: The `import` option is common to *all* handlers, however -they might implement it differently, or not even implement it. - -#### `paths` - -This option is used to provide filesystem paths in which to search for Python modules. -Non-absolute paths are computed as relative to MkDocs configuration file. Example: - -```yaml title="mkdocs.yml" -plugins: -- mkdocstrings: - handlers: - python: - paths: [src] # search packages in the src folder -``` - -More details at [Finding modules](#finding-modules). - +[](){#setting-load_external_modules} #### `load_external_modules` This option allows resolving aliases (imports) to any external module. @@ -165,6 +149,28 @@ plugins: [__all__]: https://docs.python.org/3/tutorial/modules.html#importing-from-a-package +[](){#setting-locale} +#### `locale` + +The locale to use when translating template strings. The translation system is not fully ready yet, so we don't recommend setting the option for now. + +[](){#setting-paths} +#### `paths` + +This option is used to provide filesystem paths in which to search for Python modules. +Non-absolute paths are computed as relative to MkDocs configuration file. Example: + +```yaml title="mkdocs.yml" +plugins: +- mkdocstrings: + handlers: + python: + paths: [src] # search packages in the src folder +``` + +More details at [Finding modules](#finding-modules). + +[](){#setting-options} ### Global/local options The other options can be used both globally *and* locally, under the `options` key. @@ -199,13 +205,6 @@ in the following pages: - [Docstrings options](configuration/docstrings.md): options related to docstrings (parsing and rendering) - [Signature options](configuration/signatures.md): options related to signatures and type annotations -#### Options summary - -::: mkdocstrings_handlers.python.handler.PythonHandler.default_config - options: - show_root_heading: false - show_root_toc_entry: false - ## Finding modules There are multiple ways to tell the handler where to find your packages/modules. @@ -292,7 +291,7 @@ to make sure anyone can build your docs from any location on their filesystem. ### Using the PYTHONPATH environment variable -WARNING: **This method has limitations.** +WARNING: **This method has limitations.** This method might work for you, with your current setup, but not for others trying your build your docs with their own setup/environment. We recommend using the [`paths` method](#using-the-paths-option) instead. @@ -348,10 +347,10 @@ In Bash and other shells, you can run your command like this ```bash PYTHONPATH=src mkdocs build -f docs/mkdocs.yml ``` - + ### Installing your package in the current Python environment -WARNING: **This method has limitations.** +WARNING: **This method has limitations.** This method might work for you, with your current setup, but not for others trying your build your docs with their own setup/environment. We recommend using the [`paths` method](#using-the-paths-option) instead. diff --git a/duties.py b/duties.py index bd051334..9e516ce5 100644 --- a/duties.py +++ b/duties.py @@ -88,6 +88,8 @@ def check_types(ctx: Context) -> None: ctx.run( tools.mypy(*PY_SRC_LIST, config_file="config/mypy.ini"), title=pyprefix("Type-checking"), + # TODO: Update when Pydantic supports 3.14. + nofail=sys.version_info >= (3, 14), ) diff --git a/mkdocs.yml b/mkdocs.yml index 2d546126..396f738f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -13,6 +13,9 @@ validation: absolute_links: warn unrecognized_links: warn +hooks: +- scripts/mkdocs_hooks.py + nav: - Home: - Overview: index.md @@ -160,6 +163,8 @@ plugins: docstring_options: ignore_init_summary: true docstring_section_style: list + extensions: + - scripts/griffe_extensions.py filters: ["!^_"] heading_level: 1 inherited_members: true diff --git a/pyproject.toml b/pyproject.toml index c6f3cc50..b4a453e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,9 +30,10 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "mkdocstrings>=0.26", + "mkdocstrings>=0.28", "mkdocs-autorefs>=1.2", "griffe>=0.49", + "typing-extensions>=4.0; python_version < '3.11'", ] [project.urls] @@ -106,6 +107,7 @@ dev = [ "mkdocs-git-revision-date-localized-plugin>=1.2", "mkdocs-literate-nav>=0.6", "mkdocs-material>=9.5", + "pydantic>=2.10", "mkdocs-minify-plugin>=0.8", # YORE: EOL 3.10: Remove line. "tomli>=2.0; python_version < '3.11'", @@ -113,3 +115,4 @@ dev = [ [tool.inline-snapshot] storage-dir = "tests/snapshots" +format-command = "ruff format --config config/ruff.toml --stdin-filename {filename}" diff --git a/scripts/griffe_extensions.py b/scripts/griffe_extensions.py new file mode 100644 index 00000000..4ff0c8cc --- /dev/null +++ b/scripts/griffe_extensions.py @@ -0,0 +1,46 @@ +"""Custom extensions for Griffe.""" + +from __future__ import annotations + +import ast +from typing import Any + +import griffe + +logger = griffe.get_logger("griffe_extensions") + + +class CustomFields(griffe.Extension): + """Support our custom dataclass fields.""" + + def on_attribute_instance( + self, + *, + attr: griffe.Attribute, + agent: griffe.Visitor | griffe.Inspector, + **kwargs: Any, # noqa: ARG002 + ) -> None: + """Fetch descriptions from `Field` annotations.""" + if attr.docstring: + return + try: + field: griffe.ExprCall = attr.annotation.slice.elements[1] # type: ignore[union-attr] + except AttributeError: + return + + if field.canonical_path == "mkdocstrings_handler.python.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") + description = str(description) + + attr.docstring = griffe.Docstring( + ast.literal_eval(description), + parent=attr, + parser=agent.docstring_parser, + parser_options=agent.docstring_options, + ) diff --git a/scripts/insiders.py b/scripts/insiders.py index 849c6314..a7da99bc 100644 --- a/scripts/insiders.py +++ b/scripts/insiders.py @@ -26,7 +26,7 @@ def human_readable_amount(amount: int) -> str: # noqa: D103 str_amount = str(amount) if len(str_amount) >= 4: # noqa: PLR2004 - return f"{str_amount[:len(str_amount)-3]},{str_amount[-3:]}" + return f"{str_amount[: len(str_amount) - 3]},{str_amount[-3:]}" return str_amount diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py new file mode 100644 index 00000000..bfa74e5c --- /dev/null +++ b/scripts/mkdocs_hooks.py @@ -0,0 +1,41 @@ +"""Generate a JSON schema of the Python handler configuration.""" + +import json +from dataclasses import fields +from os.path import join +from typing import Any + +from mkdocs.config.defaults import MkDocsConfig +from mkdocs.plugins import get_plugin_logger + +from mkdocstrings_handlers.python.config 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] + + +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") + return + adapter = TypeAdapter(PythonInputConfig) + schema = adapter.json_schema() + 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") + + autorefs = config["plugins"]["autorefs"] + 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): + 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/config.py b/src/mkdocstrings_handlers/python/config.py new file mode 100644 index 00000000..5e444d27 --- /dev/null +++ b/src/mkdocstrings_handlers/python/config.py @@ -0,0 +1,999 @@ +"""Configuration and options dataclasses.""" + +from __future__ import annotations + +import re +import sys +from dataclasses import field, fields +from typing import TYPE_CHECKING, Annotated, Any, Literal + +# YORE: EOL 3.10: Replace block with line 2. +if sys.version_info >= (3, 11): + from typing import Self +else: + from typing_extensions import Self + +try: + # When Pydantic is available, use it to validate options (done automatically). + # Users can therefore opt into validation by installing Pydantic in development/CI. + # When building the docs to deploy them, Pydantic is not required anymore. + + # When building our own docs, Pydantic is always installed (see `docs` group in `pyproject.toml`) + # to allow automatic generation of a JSON Schema. The JSON Schema is then referenced by mkdocstrings, + # which is itself referenced by mkdocs-material's schema system. For example in VSCode: + # + # "yaml.schemas": { + # "https://squidfunk.github.io/mkdocs-material/schema.json": "mkdocs.yml" + # } + import pydantic + + if getattr(pydantic, "__version__", "1.").startswith("1."): + raise ImportError # noqa: TRY301 + + from inspect import cleandoc + + from pydantic import Field as BaseField + from pydantic.dataclasses import dataclass + + _base_url = "https://mkdocstrings.github.io/python/usage" + + def Field( # noqa: N802, D103 + *args: Any, + description: str, + group: Literal["general", "headings", "members", "docstrings", "signatures"] | None = None, + parent: str | None = None, + **kwargs: Any, + ) -> None: + def _add_markdown_description(schema: dict[str, Any]) -> None: + url = f"{_base_url}/{f'configuration/{group}/' if group else ''}#{parent or schema['title']}" + schema["markdownDescription"] = f"[DOCUMENTATION]({url})\n\n{schema['description']}" + + return BaseField( + *args, + description=cleandoc(description), + field_title_generator=lambda name, _: name, + json_schema_extra=_add_markdown_description, + **kwargs, + ) +except ImportError: + from dataclasses import dataclass # type: ignore[no-redef] + + def Field(*args: Any, **kwargs: Any) -> None: # type: ignore[misc] # noqa: D103, N802 + pass + + +if TYPE_CHECKING: + from collections.abc import MutableMapping + + +# YORE: EOL 3.9: Remove block. +_dataclass_options = {"frozen": True} +if sys.version_info >= (3, 10): + _dataclass_options["kw_only"] = True + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class GoogleStyleOptions: + """Google style docstring options.""" + + ignore_init_summary: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="Whether to ignore the summary in `__init__` methods' docstrings.", + ), + ] = False + + returns_multiple_items: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="""Whether to parse multiple items in `Yields` and `Returns` sections. + + When true, each item's continuation lines must be indented. + When false (single item), no further indentation is required. + """, + ), + ] = True + + returns_named_value: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="""Whether to parse `Yields` and `Returns` section items as name and description, rather than type and description. + + When true, type must be wrapped in parentheses: `(int): Description.`. Names are optional: `name (int): Description.`. + When false, parentheses are optional but the items cannot be named: `int: Description`. + """, + ), + ] = True + + returns_type_in_property_summary: Annotated[ + bool, + 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`.", + ), + ] = False + + receives_multiple_items: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="""Whether to parse multiple items in `Receives` sections. + + When true, each item's continuation lines must be indented. + When false (single item), no further indentation is required. + """, + ), + ] = True + + receives_named_value: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="""Whether to parse `Receives` section items as name and description, rather than type and description. + + When true, type must be wrapped in parentheses: `(int): Description.`. Names are optional: `name (int): Description.`. + When false, parentheses are optional but the items cannot be named: `int: Description`. + """, + ), + ] = True + + trim_doctest_flags: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="Whether to remove doctest flags from Python example blocks.", + ), + ] = True + + warn_unknown_params: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="Warn about documented parameters not appearing in the signature.", + ), + ] = True + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class NumpyStyleOptions: + """Numpy style docstring options.""" + + ignore_init_summary: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="Whether to ignore the summary in `__init__` methods' docstrings.", + ), + ] = False + + trim_doctest_flags: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="Whether to remove doctest flags from Python example blocks.", + ), + ] = True + + warn_unknown_params: Annotated[ + bool, + Field( + group="docstrings", + parent="docstring_options", + description="Warn about documented parameters not appearing in the signature.", + ), + ] = True + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class SphinxStyleOptions: + """Sphinx style docstring options.""" + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class PerStyleOptions: + """Per style options.""" + + google: Annotated[ + GoogleStyleOptions, + Field( + group="docstrings", + parent="docstring_options", + description="Google-style options.", + ), + ] = field(default_factory=GoogleStyleOptions) + + numpy: Annotated[ + NumpyStyleOptions, + Field( + group="docstrings", + parent="docstring_options", + description="Numpydoc-style options.", + ), + ] = field(default_factory=NumpyStyleOptions) + + sphinx: Annotated[ + SphinxStyleOptions, + Field( + group="docstrings", + parent="docstring_options", + description="Sphinx-style options.", + ), + ] = field(default_factory=SphinxStyleOptions) + + @classmethod + def from_data(cls, **data: Any) -> Self: + """Create an instance from a dictionary.""" + if "google" in data: + data["google"] = GoogleStyleOptions(**data["google"]) + if "numpy" in data: + data["numpy"] = NumpyStyleOptions(**data["numpy"]) + if "sphinx" in data: + data["sphinx"] = SphinxStyleOptions(**data["sphinx"]) + return cls(**data) + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class AutoStyleOptions: + """Auto style docstring options.""" + + method: Annotated[ + Literal["heuristics", "max_sections"], + Field( + group="docstrings", + parent="docstring_options", + description="The method to use to determine the docstring style.", + ), + ] = "heuristics" + + style_order: Annotated[ + list[str], + Field( + group="docstrings", + parent="docstring_options", + description="The order of the docstring styles to try.", + ), + ] = field(default_factory=lambda: ["sphinx", "google", "numpy"]) + + default: Annotated[ + str | None, + Field( + group="docstrings", + parent="docstring_options", + description="The default docstring style to use if no other style is detected.", + ), + ] = None + + per_style_options: Annotated[ + PerStyleOptions, + Field( + group="docstrings", + parent="docstring_options", + description="Per-style options.", + ), + ] = field(default_factory=PerStyleOptions) + + @classmethod + def from_data(cls, **data: Any) -> Self: + """Create an instance from a dictionary.""" + if "per_style_options" in data: + data["per_style_options"] = PerStyleOptions.from_data(**data["per_style_options"]) + return cls(**data) + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class SummaryOption: + """Summary option.""" + + attributes: Annotated[ + bool, + Field( + group="members", + parent="summary", + description="Whether to render summaries of attributes.", + ), + ] = False + + functions: Annotated[ + bool, + Field( + group="members", + parent="summary", + description="Whether to render summaries of functions (methods).", + ), + ] = False + + classes: Annotated[ + bool, + Field( + group="members", + parent="summary", + description="Whether to render summaries of classes.", + ), + ] = False + + modules: Annotated[ + bool, + Field( + group="members", + parent="summary", + description="Whether to render summaries of modules.", + ), + ] = False + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class PythonInputOptions: + """Accepted input options.""" + + allow_inspection: Annotated[ + bool, + Field( + group="general", + description="Whether to allow inspecting modules when visiting them is not possible.", + ), + ] = True + + force_inspection: Annotated[ + bool, + Field( + group="general", + description="Whether to force using dynamic analysis when loading data.", + ), + ] = False + + annotations_path: Annotated[ + Literal["brief", "source", "full"], + Field( + group="signatures", + description="The verbosity for annotations path: `brief` (recommended), `source` (as written in the source), or `full`.", + ), + ] = "brief" + + docstring_options: Annotated[ + GoogleStyleOptions | NumpyStyleOptions | SphinxStyleOptions | AutoStyleOptions | None, + Field( + group="docstrings", + description="""The options for the docstring parser. + + See [docstring parsers](https://mkdocstrings.github.io/griffe/reference/docstrings/) and their options in Griffe docs. + """, + ), + ] = None + + docstring_section_style: Annotated[ + Literal["table", "list", "spacy"], + Field( + group="docstrings", + description="The style used to render docstring sections.", + ), + ] = "table" + + docstring_style: Annotated[ + Literal["auto", "google", "numpy", "sphinx"] | None, + Field( + group="docstrings", + description="The docstring style to use: `auto`, `google`, `numpy`, `sphinx`, or `None`.", + ), + ] = "google" + + extensions: Annotated[ + list[str | dict[str, Any]], + Field( + group="general", + description="A list of Griffe extensions to load.", + ), + ] = field(default_factory=list) + + filters: Annotated[ + list[str], + Field( + group="members", + description="""A list of filters applied to filter objects based on their name. + + A filter starting with `!` will exclude matching objects instead of including them. + The `members` option takes precedence over `filters` (filters will still be applied recursively + to lower members in the hierarchy). + """, + ), + ] = field(default_factory=lambda: ["!^_[^_]"]) + + find_stubs_package: Annotated[ + bool, + Field( + group="general", + description="Whether to load stubs package (package-stubs) when extracting docstrings.", + ), + ] = False + + group_by_category: Annotated[ + bool, + Field( + group="members", + description="Group the object's children by categories: attributes, classes, functions, and modules.", + ), + ] = True + + heading: Annotated[ + str, + Field( + group="headings", + description="A custom string to override the autogenerated heading of the root object.", + ), + ] = "" + + heading_level: Annotated[ + int, + Field( + group="headings", + description="The initial heading level to use.", + ), + ] = 2 + + inherited_members: Annotated[ + bool | list[str], + Field( + group="members", + description="""A boolean, or an explicit list of inherited members to render. + + If true, select all inherited members, which can then be filtered with `members`. + If false or empty list, do not select any inherited member. + """, + ), + ] = False + + line_length: Annotated[ + int, + Field( + group="signatures", + description="Maximum line length when formatting code/signatures.", + ), + ] = 60 + + members: Annotated[ + list[str] | bool | None, + Field( + group="members", + description="""A boolean, or an explicit list of members to render. + + If true, select all members without further filtering. + If false or empty list, do not render members. + If none, select all members and apply further filtering with filters and docstrings. + """, + ), + ] = None + + members_order: Annotated[ + Literal["alphabetical", "source"], + Field( + group="members", + description="""The members ordering to use. + + - `alphabetical`: order by the members names, + - `source`: order members as they appear in the source file. + """, + ), + ] = "alphabetical" + + merge_init_into_class: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to merge the `__init__` method into the class' signature and docstring.", + ), + ] = False + + modernize_annotations: Annotated[ + bool, + Field( + group="signatures", + description="Whether to modernize annotations, for example `Optional[str]` into `str | None`.", + ), + ] = False + + parameter_headings: Annotated[ + bool, + Field( + group="headings", + description="Whether to render headings for parameters (therefore showing parameters in the ToC).", + ), + ] = False + + preload_modules: Annotated[ + list[str], + Field( + group="general", + description="""Pre-load modules that are not specified directly in autodoc instructions (`::: identifier`). + + It is useful when you want to render documentation for a particular member of an object, + and this member is imported from another package than its parent. + + For an imported member to be rendered, you need to add it to the `__all__` attribute + of the importing module. + + The modules must be listed as an array of strings. + """, + ), + ] = field(default_factory=list) + + relative_crossrefs: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to enable the relative crossref syntax.", + ), + ] = False + + scoped_crossrefs: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to enable the scoped crossref ability.", + ), + ] = False + + separate_signature: Annotated[ + bool, + Field( + group="signatures", + description="""Whether to put the whole signature in a code block below the heading. + + If Black or Ruff are installed, the signature is also formatted using them. + """, + ), + ] = False + + show_bases: Annotated[ + bool, + Field( + group="general", + description="Show the base classes of a class.", + ), + ] = True + + show_category_heading: Annotated[ + bool, + Field( + group="headings", + description="When grouped by categories, show a heading for each category.", + ), + ] = False + + show_docstring_attributes: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Attributes' section in the object's docstring.", + ), + ] = True + + show_docstring_classes: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Classes' section in the object's docstring.", + ), + ] = True + + show_docstring_description: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the textual block (including admonitions) in the object's docstring.", + ), + ] = True + + show_docstring_examples: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Examples' section in the object's docstring.", + ), + ] = True + + show_docstring_functions: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Functions' or 'Methods' sections in the object's docstring.", + ), + ] = True + + show_docstring_modules: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Modules' section in the object's docstring.", + ), + ] = True + + show_docstring_other_parameters: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Other Parameters' section in the object's docstring.", + ), + ] = True + + show_docstring_parameters: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Parameters' section in the object's docstring.", + ), + ] = True + + show_docstring_raises: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Raises' section in the object's docstring.", + ), + ] = True + + show_docstring_receives: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Receives' section in the object's docstring.", + ), + ] = True + + show_docstring_returns: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Returns' section in the object's docstring.", + ), + ] = True + + show_docstring_warns: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Warns' section in the object's docstring.", + ), + ] = True + + show_docstring_yields: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to display the 'Yields' section in the object's docstring.", + ), + ] = True + + show_if_no_docstring: Annotated[ + bool, + Field( + group="docstrings", + description="Show the object heading even if it has no docstring or children with docstrings.", + ), + ] = False + + show_inheritance_diagram: Annotated[ + bool, + Field( + group="docstrings", + description="Show the inheritance diagram of a class using Mermaid.", + ), + ] = False + + show_labels: Annotated[ + bool, + Field( + group="docstrings", + description="Whether to show labels of the members.", + ), + ] = True + + show_object_full_path: Annotated[ + bool, + Field( + group="docstrings", + description="Show the full Python path of every object.", + ), + ] = False + + show_root_full_path: Annotated[ + bool, + Field( + group="docstrings", + description="Show the full Python path for the root object heading.", + ), + ] = True + + show_root_heading: Annotated[ + bool, + Field( + group="headings", + description="""Show the heading of the object at the root of the documentation tree. + + The root object is the object referenced by the identifier after `:::`. + """, + ), + ] = False + + show_root_members_full_path: Annotated[ + bool, + Field( + group="headings", + description="Show the full Python path of the root members.", + ), + ] = False + + show_root_toc_entry: Annotated[ + bool, + Field( + group="headings", + description="If the root heading is not shown, at least add a ToC entry for it.", + ), + ] = True + + show_signature_annotations: Annotated[ + bool, + Field( + group="signatures", + description="Show the type annotations in methods and functions signatures.", + ), + ] = False + + show_signature: Annotated[ + bool, + Field( + group="signatures", + description="Show methods and functions signatures.", + ), + ] = True + + show_source: Annotated[ + bool, + Field( + group="general", + description="Show the source code of this object.", + ), + ] = True + + show_submodules: Annotated[ + bool, + Field( + group="members", + description="When rendering a module, show its submodules recursively.", + ), + ] = False + + show_symbol_type_heading: Annotated[ + bool, + Field( + group="headings", + description="Show the symbol type in headings (e.g. mod, class, meth, func and attr).", + ), + ] = False + + show_symbol_type_toc: Annotated[ + bool, + Field( + group="headings", + description="Show the symbol type in the Table of Contents (e.g. mod, class, methd, func and attr).", + ), + ] = False + + signature_crossrefs: Annotated[ + bool, + Field( + group="signatures", + description="Whether to render cross-references for type annotations in signatures.", + ), + ] = False + + summary: Annotated[ + bool | SummaryOption, + Field( + group="members", + description="Whether to render summaries of modules, classes, functions (methods) and attributes.", + ), + ] = field(default_factory=SummaryOption) + + toc_label: Annotated[ + str, + Field( + group="headings", + description="A custom string to override the autogenerated toc label of the root object.", + ), + ] = "" + + unwrap_annotated: Annotated[ + bool, + Field( + group="signatures", + description="Whether to unwrap `Annotated` types to show only the type without the annotations.", + ), + ] = False + + extra: Annotated[ + dict[str, Any], + Field( + group="general", + description="Extra options.", + ), + ] = field(default_factory=dict) + + @classmethod + def _extract_extra(cls, data: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]: + field_names = {field.name for field in fields(cls)} + copy = data.copy() + return {name: copy.pop(name) for name in data if name not in field_names}, copy + + # YORE: Bump 2: Remove block. + def __init__(self, **kwargs: Any) -> None: + """Initialize the instance.""" + extra_fields = self._extract_extra(kwargs) + for name, value in kwargs.items(): + object.__setattr__(self, name, value) + if extra_fields: + object.__setattr__(self, "_extra", extra_fields) + + @classmethod + def coerce(cls, **data: Any) -> MutableMapping[str, Any]: + """Coerce data.""" + if "docstring_options" in data: + docstring_style = data.get("docstring_style", "google") + docstring_options = data["docstring_options"] + if docstring_options is not None: + if docstring_style == "auto": + docstring_options = AutoStyleOptions.from_data(**docstring_options) + elif docstring_style == "google": + docstring_options = GoogleStyleOptions(**docstring_options) + elif docstring_style == "numpy": + docstring_options = NumpyStyleOptions(**docstring_options) + elif docstring_style == "sphinx": + docstring_options = SphinxStyleOptions(**docstring_options) + data["docstring_options"] = docstring_options + if "summary" in data: + summary = data["summary"] + if summary is True: + summary = SummaryOption(attributes=True, functions=True, classes=True, modules=True) + elif summary is False: + summary = SummaryOption(attributes=False, functions=False, classes=False, modules=False) + else: + summary = SummaryOption(**summary) + data["summary"] = summary + return data + + @classmethod + def from_data(cls, **data: Any) -> Self: + """Create an instance from a dictionary.""" + return cls(**cls.coerce(**data)) + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class PythonOptions(PythonInputOptions): # type: ignore[override,unused-ignore] + """Final options passed as template context.""" + + filters: list[tuple[re.Pattern, bool]] = field(default_factory=list) # type: ignore[assignment] + """A list of filters applied to filter objects based on their name.""" + + summary: SummaryOption = field(default_factory=SummaryOption) + """Whether to render summaries of modules, classes, functions (methods) and attributes.""" + + @classmethod + def coerce(cls, **data: Any) -> MutableMapping[str, Any]: + """Create an instance from a dictionary.""" + if "filters" in data: + data["filters"] = [ + (re.compile(filtr.lstrip("!")), filtr.startswith("!")) for filtr in data["filters"] or () + ] + return super().coerce(**data) + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class Inventory: + """An inventory.""" + + url: Annotated[ + str, + Field( + parent="inventories", + description="The URL of the inventory.", + ), + ] + + base: Annotated[ + str | None, + Field( + parent="inventories", + description="The base URL of the inventory.", + ), + ] = None + + domains: Annotated[ + list[str], + Field( + parent="inventories", + description="The domains to load from the inventory.", + ), + ] = field(default_factory=lambda: ["py"]) + + @property + def _config(self) -> dict[str, Any]: + return {"base": self.base, "domains": self.domains} + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +class PythonInputConfig: + """Python handler configuration.""" + + inventories: Annotated[ + list[str | Inventory], + 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(default_factory=lambda: ["."]) + + load_external_modules: Annotated[ + bool | None, + Field(description="Whether to always load external modules/packages."), + ] = None + + options: Annotated[ + PythonInputOptions, + 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."), + ] = None + + @classmethod + def coerce(cls, **data: Any) -> MutableMapping[str, Any]: + """Coerce data.""" + return data + + @classmethod + def from_data(cls, **data: Any) -> Self: + """Create an instance from a dictionary.""" + return cls(**cls.coerce(**data)) + + +# YORE: EOL 3.9: Replace `**_dataclass_options` with `frozen=True, kw_only=True` within line. +@dataclass(**_dataclass_options) # type: ignore[call-overload] +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] + + @classmethod + def coerce(cls, **data: Any) -> MutableMapping[str, Any]: + """Coerce data.""" + if "inventories" in data: + data["inventories"] = [ + Inventory(url=inv) if isinstance(inv, str) else Inventory(**inv) for inv in data["inventories"] + ] + return data diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 4171fd76..7ef6b358 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -5,12 +5,12 @@ import glob import os import posixpath -import re import sys -from collections import ChainMap from contextlib import suppress +from dataclasses import asdict from pathlib import Path from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar +from warnings import warn from griffe import ( AliasResolutionError, @@ -21,17 +21,18 @@ load_extensions, patch_loggers, ) -from mkdocstrings.extension import PluginError -from mkdocstrings.handlers.base import BaseHandler, CollectionError, CollectorItem +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_handlers.python import rendering +from mkdocstrings_handlers.python.config import PythonConfig, PythonOptions if TYPE_CHECKING: - from collections.abc import Iterator, Mapping, Sequence + from collections.abc import Iterator, Mapping, MutableMapping, Sequence - from markdown import Markdown + from mkdocs.config.defaults import MkDocsConfig if sys.version_info >= (3, 11): @@ -55,209 +56,78 @@ def chdir(path: str) -> Iterator[None]: # noqa: D103 patch_loggers(get_logger) +def _warn_extra_options(names: Sequence[str]) -> None: + warn( + "Passing extra options directly under `options` is deprecated. " + "Instead, pass them under `options.extra`, and update your templates. " + f"Current extra (unrecognized) options: {', '.join(sorted(names))}", + DeprecationWarning, + stacklevel=3, + ) + + class PythonHandler(BaseHandler): """The Python handler class.""" - name: str = "python" + name: ClassVar[str] = "python" """The handler's name.""" - domain: str = "py" # to match Sphinx's default domain + + domain: ClassVar[str] = "py" """The cross-documentation domain/language for this handler.""" - enable_inventory: bool = True + + enable_inventory: ClassVar[bool] = True """Whether this handler is interested in enabling the creation of the `objects.inv` Sphinx inventory file.""" - fallback_theme = "material" + + fallback_theme: ClassVar[str] = "material" """The fallback theme.""" - fallback_config: ClassVar[dict] = {"fallback": True} - """The configuration used to collect item during autorefs fallback.""" - default_config: ClassVar[dict] = { - "find_stubs_package": False, - "docstring_style": "google", - "docstring_options": {}, - "show_symbol_type_heading": False, - "show_symbol_type_toc": False, - "show_root_heading": False, - "show_root_toc_entry": True, - "show_root_full_path": True, - "show_root_members_full_path": False, - "show_object_full_path": False, - "show_category_heading": False, - "show_if_no_docstring": False, - "show_signature": True, - "show_signature_annotations": False, - "signature_crossrefs": False, - "separate_signature": False, - "line_length": 60, - "merge_init_into_class": False, - "relative_crossrefs": False, - "scoped_crossrefs": False, - "show_docstring_attributes": True, - "show_docstring_functions": True, - "show_docstring_classes": True, - "show_docstring_modules": True, - "show_docstring_description": True, - "show_docstring_examples": True, - "show_docstring_other_parameters": True, - "show_docstring_parameters": True, - "show_docstring_raises": True, - "show_docstring_receives": True, - "show_docstring_returns": True, - "show_docstring_warns": True, - "show_docstring_yields": True, - "show_source": True, - "show_bases": True, - "show_inheritance_diagram": False, - "show_submodules": False, - "group_by_category": True, - "heading_level": 2, - "members_order": rendering.Order.alphabetical.value, - "docstring_section_style": "table", - "members": None, - "inherited_members": False, - "filters": ["!^_[^_]"], - "annotations_path": "brief", - "preload_modules": None, - "allow_inspection": True, - "summary": False, - "show_labels": True, - "unwrap_annotated": False, - "parameter_headings": False, - "modernize_annotations": False, - } - """Default handler configuration. - - Attributes: General options: - find_stubs_package (bool): Whether to load stubs package (package-stubs) when extracting docstrings. Default `False`. - allow_inspection (bool): Whether to allow inspecting modules when visiting them is not possible. Default: `True`. - show_bases (bool): Show the base classes of a class. Default: `True`. - show_inheritance_diagram (bool): Show the inheritance diagram of a class using Mermaid. Default: `False`. - show_source (bool): Show the source code of this object. Default: `True`. - preload_modules (list[str] | None): Pre-load modules that are - not specified directly in autodoc instructions (`::: identifier`). - It is useful when you want to render documentation for a particular member of an object, - and this member is imported from another package than its parent. - - For an imported member to be rendered, you need to add it to the `__all__` attribute - of the importing module. - - The modules must be listed as an array of strings. Default: `None`. - - Attributes: Headings options: - heading_level (int): The initial heading level to use. Default: `2`. - parameter_headings (bool): Whether to render headings for parameters (therefore showing parameters in the ToC). Default: `False`. - show_root_heading (bool): Show the heading of the object at the root of the documentation tree - (i.e. the object referenced by the identifier after `:::`). Default: `False`. - show_root_toc_entry (bool): If the root heading is not shown, at least add a ToC entry for it. Default: `True`. - show_root_full_path (bool): Show the full Python path for the root object heading. Default: `True`. - show_root_members_full_path (bool): Show the full Python path of the root members. Default: `False`. - show_object_full_path (bool): Show the full Python path of every object. Default: `False`. - show_category_heading (bool): When grouped by categories, show a heading for each category. Default: `False`. - show_symbol_type_heading (bool): Show the symbol type in headings (e.g. mod, class, meth, func and attr). Default: `False`. - show_symbol_type_toc (bool): Show the symbol type in the Table of Contents (e.g. mod, class, methd, func and attr). Default: `False`. - - Attributes: Members options: - inherited_members (list[str] | bool | None): A boolean, or an explicit list of inherited members to render. - If true, select all inherited members, which can then be filtered with `members`. - If false or empty list, do not select any inherited member. Default: `False`. - members (list[str] | bool | None): A boolean, or an explicit list of members to render. - If true, select all members without further filtering. - If false or empty list, do not render members. - If none, select all members and apply further filtering with filters and docstrings. Default: `None`. - members_order (str): The members ordering to use. Options: `alphabetical` - order by the members names, - `source` - order members as they appear in the source file. Default: `"alphabetical"`. - filters (list[str] | None): A list of filters applied to filter objects based on their name. - A filter starting with `!` will exclude matching objects instead of including them. - The `members` option takes precedence over `filters` (filters will still be applied recursively - to lower members in the hierarchy). Default: `["!^_[^_]"]`. - group_by_category (bool): Group the object's children by categories: attributes, classes, functions, and modules. Default: `True`. - show_submodules (bool): When rendering a module, show its submodules recursively. Default: `False`. - summary (bool | dict[str, bool]): Whether to render summaries of modules, classes, functions (methods) and attributes. - show_labels (bool): Whether to show labels of the members. Default: `True`. - - Attributes: Docstrings options: - docstring_style (str): The docstring style to use: `google`, `numpy`, `sphinx`, or `None`. Default: `"google"`. - docstring_options (dict): The options for the docstring parser. See [docstring parsers](https://mkdocstrings.github.io/griffe/reference/docstrings/) and their options in Griffe docs. - docstring_section_style (str): The style used to render docstring sections. Options: `table`, `list`, `spacy`. Default: `"table"`. - merge_init_into_class (bool): Whether to merge the `__init__` method into the class' signature and docstring. Default: `False`. - relative_crossrefs (bool): Whether to enable the relative crossref syntax. Default: `False`. - scoped_crossrefs (bool): Whether to enable the scoped crossref ability. Default: `False`. - show_if_no_docstring (bool): Show the object heading even if it has no docstring or children with docstrings. Default: `False`. - show_docstring_attributes (bool): Whether to display the "Attributes" section in the object's docstring. Default: `True`. - show_docstring_functions (bool): Whether to display the "Functions" or "Methods" sections in the object's docstring. Default: `True`. - show_docstring_classes (bool): Whether to display the "Classes" section in the object's docstring. Default: `True`. - show_docstring_modules (bool): Whether to display the "Modules" section in the object's docstring. Default: `True`. - show_docstring_description (bool): Whether to display the textual block (including admonitions) in the object's docstring. Default: `True`. - show_docstring_examples (bool): Whether to display the "Examples" section in the object's docstring. Default: `True`. - show_docstring_other_parameters (bool): Whether to display the "Other Parameters" section in the object's docstring. Default: `True`. - show_docstring_parameters (bool): Whether to display the "Parameters" section in the object's docstring. Default: `True`. - show_docstring_raises (bool): Whether to display the "Raises" section in the object's docstring. Default: `True`. - show_docstring_receives (bool): Whether to display the "Receives" section in the object's docstring. Default: `True`. - show_docstring_returns (bool): Whether to display the "Returns" section in the object's docstring. Default: `True`. - show_docstring_warns (bool): Whether to display the "Warns" section in the object's docstring. Default: `True`. - show_docstring_yields (bool): Whether to display the "Yields" section in the object's docstring. Default: `True`. - - Attributes: Signatures/annotations options: - annotations_path (str): The verbosity for annotations path: `brief` (recommended), or `source` (as written in the source). Default: `"brief"`. - line_length (int): Maximum line length when formatting code/signatures. Default: `60`. - show_signature (bool): Show methods and functions signatures. Default: `True`. - show_signature_annotations (bool): Show the type annotations in methods and functions signatures. Default: `False`. - signature_crossrefs (bool): Whether to render cross-references for type annotations in signatures. Default: `False`. - separate_signature (bool): Whether to put the whole signature in a code block below the heading. - If a formatter (Black or Ruff) is installed, the signature is also formatted using it. Default: `False`. - unwrap_annotated (bool): Whether to unwrap `Annotated` types to show only the type without the annotations. Default: `False`. - modernize_annotations (bool): Whether to modernize annotations, for example `Optional[str]` into `str | None`. Default: `False`. - """ - def __init__( - self, - *args: Any, - config_file_path: str | None = None, - paths: list[str] | None = None, - locale: str = "en", - load_external_modules: bool | None = None, - **kwargs: Any, - ) -> None: + def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None: """Initialize the handler. Parameters: - *args: Handler name, theme and custom templates. - config_file_path: The MkDocs configuration file path. - paths: A list of paths to use as Griffe search paths. - locale: The locale to use when rendering content. - load_external_modules: Load external modules when resolving aliases. - **kwargs: Same thing, but with keyword arguments. + config: The handler configuration. + base_dir: The base directory of the project. + **kwargs: Arguments passed to the parent constructor. """ - super().__init__(*args, **kwargs) + super().__init__(**kwargs) + + self.config = config + self.base_dir = base_dir + + # YORE: Bump 2: Replace block with `self.global_options = config.options`. + global_extra, global_options = PythonOptions._extract_extra(config.options) + if global_extra: + _warn_extra_options(global_extra.keys()) # type: ignore[arg-type] + self._global_extra = global_extra + self.global_options = global_options # Warn if user overrides base templates. - if custom_templates := kwargs.get("custom_templates", ()): - config_dir = Path(config_file_path or "./mkdocs.yml").parent - for theme_dir in config_dir.joinpath(custom_templates, "python").iterdir(): + 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( f"Overriding base template '{theme_dir.name}/_base/