diff --git a/config/pytest.ini b/config/pytest.ini index 25f9c92e..4f43c18e 100644 --- a/config/pytest.ini +++ b/config/pytest.ini @@ -10,6 +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 mkdocstrings stops setting fallback function. ignore:.*fallback anchor function:DeprecationWarning:mkdocstrings 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 dd621735..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,145 @@ 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" }** @@ -64,6 +208,8 @@ Whether to force inspecting modules (importing them) even if their source code i 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: @@ -79,6 +225,65 @@ plugins: 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" }** @@ -116,6 +321,7 @@ plugins: //// /// +[](){#option-show_inheritance_diagram} ## `show_inheritance_diagram` [:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — @@ -201,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" }** @@ -245,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 33062baf..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,39 +74,7 @@ plugins: //// /// -## `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. - -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" -``` - -## `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. - -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" -``` - +[](){#option-parameter_headings} ## `parameter_headings` [:octicons-tag-24: Insiders 1.6.0](../../insiders/changelog.md#1.6.0) @@ -212,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" }** @@ -275,6 +261,7 @@ plugins: //// /// +[](){#option-show_root_toc_entry} ## `show_root_toc_entry` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -332,6 +319,7 @@ More text. //// /// +[](){#option-show_root_full_path} ## `show_root_full_path` - **:octicons-package-24: Type [`bool`][] :material-equal: `True`{ title="default value" }** @@ -377,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" }** @@ -425,6 +414,7 @@ plugins: //// /// +[](){#option-show_object_full_path} ## `show_object_full_path` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -478,6 +468,7 @@ plugins: //// /// +[](){#option-show_category_heading} ## `show_category_heading` - **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** @@ -543,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) @@ -607,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) @@ -670,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 345e1536..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,6 +200,7 @@ plugins: //// /// +[](){#option-modernize_annotations} ## `modernize_annotations` [:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — @@ -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/index.md b/docs/usage/index.md index 87f0a13f..84110936 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -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,9 +91,9 @@ 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. @@ -101,8 +102,8 @@ 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. 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/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..07f34397 --- /dev/null +++ b/src/mkdocstrings_handlers/python/config.py @@ -0,0 +1,994 @@ +"""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" + # } + 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 5725d566..bf22876d 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,215 +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": "", - "toc_label": "", - "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, - "force_inspection": False, - "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`. - force_inspection (bool): Whether to force using dynamic analysis when loading data. Default: `False`. - 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 (str): A custom string to override the autogenerated heading of the root object. - toc_label (str): A custom string to override the autogenerated toc label of the root object. - 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/