From 2a11b408d2e3e50905f59203be025ce9c8192f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Mon, 8 Jan 2024 16:58:16 +0100 Subject: [PATCH 1/2] feat: Release Insiders features of the $500/month funding goal The features and projects related to *mkdocstrings* are: - Cross-references for type annotations in signatures - Symbol types in headings and table of contents - `griffe-inherited-docstrings`, a Griffe extension for inheriting docstrings - `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. --- docs/insiders/changelog.md | 48 +++++ docs/insiders/goals.yml | 19 +- docs/insiders/index.md | 15 +- docs/insiders/installation.md | 10 +- docs/schema.json | 12 ++ docs/usage/configuration/headings.md | 128 +++++++++++++ docs/usage/configuration/members.md | 96 +++++++++- docs/usage/configuration/signatures.md | 46 +++++ docs/usage/customization.md | 173 +++++++++++++++++ duties.py | 2 +- mkdocs.yml | 5 +- src/mkdocstrings_handlers/python/handler.py | 53 ++++-- src/mkdocstrings_handlers/python/rendering.py | 175 ++++++++++++++++-- .../templates/material/_base/attribute.html | 14 +- .../templates/material/_base/class.html | 14 +- .../material/_base/docstring/attributes.html | 6 +- .../material/_base/docstring/classes.html | 6 +- .../material/_base/docstring/functions.html | 6 +- .../material/_base/docstring/modules.html | 6 +- .../templates/material/_base/function.html | 18 +- .../templates/material/_base/module.html | 13 +- .../templates/material/_base/summary.html | 0 .../material/_base/summary/attributes.html | 0 .../material/_base/summary/classes.html | 0 .../material/_base/summary/functions.html | 0 .../material/_base/summary/modules.html | 0 .../python/templates/material/style.css | 91 ++++++--- .../python/templates/material/summary.html | 1 + .../material/summary/attributes.html | 1 + .../templates/material/summary/classes.html | 1 + .../templates/material/summary/functions.html | 1 + .../templates/material/summary/modules.html | 1 + 32 files changed, 862 insertions(+), 99 deletions(-) create mode 100644 src/mkdocstrings_handlers/python/templates/material/_base/summary.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/_base/summary/attributes.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/_base/summary/classes.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/_base/summary/functions.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/_base/summary/modules.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/summary.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/summary/attributes.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/summary/classes.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/summary/functions.html create mode 100644 src/mkdocstrings_handlers/python/templates/material/summary/modules.html diff --git a/docs/insiders/changelog.md b/docs/insiders/changelog.md index eead3a6a..3c8ac843 100644 --- a/docs/insiders/changelog.md +++ b/docs/insiders/changelog.md @@ -1,3 +1,51 @@ # Changelog ## mkdocstrings-python Insiders + +### 1.5.1 September 12, 2023 { id="1.5.1" } + +- Prevent empty auto-summarized Methods section. + +### 1.5.0 September 05, 2023 { id="1.5.0" } + +- Render function signature overloads. + +### 1.4.0 August 27, 2023 { id="1.4.0" } + +- Render cross-references in attribute signatures. + +### 1.3.0 August 24, 2023 { id="1.3.0" } + +- Add "method" symbol type. + +### 1.2.0 August 20, 2023 { id="1.2.0" } + +- Add [member auto-summaries](../usage/configuration/members.md#summary). + +### 1.1.4 July 17, 2023 { id="1.1.4" } + +- Fix heading level increment for class members. + +### 1.1.3 July 17, 2023 { id="1.1.3" } + +- Fix heading level (avoid with clause preventing to decrease it). + +### 1.1.2 July 15, 2023 { id="1.1.2" } + +- Use non-breaking spaces after symbol types. + +### 1.1.1 June 27, 2023 { id="1.1.1" } + +- Correctly escape expressions in signatures and other rendered types. + +### 1.1.0 June 4, 2023 { id="1.1.0" } + +- Add [Symbol types in headings and table of contents](../usage/configuration/headings.md#show_symbol_type_toc). + +### 1.0.0 May 10, 2023 { id="1.0.0" } + +- Add [cross-references for type annotations in signatures](../usage/configuration/signatures.md#signature_crossrefs). + Make sure to update your local templates as the signature of the + [`format_signature` filter][mkdocstrings_handlers.python.rendering.do_format_signature] + has changed. The templates that must be updated: + `class.html`, `expression.html`, `function.html` and `signature.html`. diff --git a/docs/insiders/goals.yml b/docs/insiders/goals.yml index a96ac51b..8b6cb2b0 100644 --- a/docs/insiders/goals.yml +++ b/docs/insiders/goals.yml @@ -1 +1,18 @@ -goals: {} \ No newline at end of file +goals: + 500: + name: PlasmaVac User Guide + features: + - name: Cross-references for type annotations in signatures + ref: /usage/configuration/signatures/#signature_crossrefs + since: 2023/05/10 + - name: Symbol types in headings and table of contents + ref: /usage/configuration/headings/#show_symbol_type_toc + since: 2023/06/04 + 1000: + name: GraviFridge User Manual + features: + - name: Auto-summary of object members + ref: /usage/configuration/members/#summary + since: 2023/08/20 + - name: Automatic rendering of function signature overloads + since: 2023/09/05 diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 7c69b590..ceb3c59c 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -33,7 +33,6 @@ Sponsorships start as low as [**$10 a month**][sponsors].[^2] technical reasons. Such contributions are still very much welcome as they help ensuring the project's sustainability. - ## What sponsorships achieve Sponsorships make this project sustainable, as they buy the maintainers of this @@ -48,15 +47,21 @@ The biggest bottleneck in Open Source is time.[^3] you can be sure that bugs are fixed quickly and new features are added regularly. - +were developed with the help of sponsorships. + ## What's in it for me? ```python exec="1" session="insiders" -data_source = "docs/insiders/goals.yml" +data_source = [ + "docs/insiders/goals.yml", + ("griffe-inherited-docstrings", "https://mkdocstrings.github.io/griffe-inherited-docstrings/", "insiders/goals.yml"), + ("griffe-pydantic", "https://mkdocstrings.github.io/griffe-pydantic/", "insiders/goals.yml"), + ("griffe-typing-deprecated", "https://mkdocstrings.github.io/griffe-typing-deprecated/", "insiders/goals.yml"), +] ``` diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md index bb387d99..3d9d75d8 100644 --- a/docs/insiders/installation.md +++ b/docs/insiders/installation.md @@ -28,7 +28,7 @@ and [how to use it](https://pawamoy.github.io/pypi-insiders/#usage). *mkdocstrings-python Insiders* can be installed with `pip` [using SSH][using ssh]: ```bash -pip install git+ssh://git@github.com/pawamoy-insiders/python.git +pip install git+ssh://git@github.com/pawamoy-insiders/mkdocstrings-python.git ``` [using ssh]: https://docs.github.com/en/authentication/connecting-to-github-with-ssh @@ -36,7 +36,7 @@ pip install git+ssh://git@github.com/pawamoy-insiders/python.git Or using HTTPS: ```bash -pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/python.git +pip install git+https://${GH_TOKEN}@github.com/pawamoy-insiders/mkdocstrings-python.git ``` >? NOTE: **How to get a GitHub personal access token** @@ -82,7 +82,7 @@ with [Twine]: [Artifactory]: https://jfrog.com/help/r/jfrog-artifactory-documentation/pypi-repositories [Google Cloud]: https://cloud.google.com/artifact-registry/docs/python [pypiserver]: https://pypi.org/project/pypiserver/ - [Github Releases]: https://github.com/pawamoy-insiders/python/releases + [Github Releases]: https://github.com/pawamoy-insiders/mkdocstrings-python/releases [Twine]: https://pypi.org/project/twine/ ```bash @@ -142,7 +142,7 @@ as it is against our [Terms of use](index.md#terms).** > > ```bash > # clone the repository -> git clone git@github.com:pawamoy-insiders/python +> git clone git@github.com:pawamoy-insiders/mkdocstrings-python > cd python > > # install build @@ -178,7 +178,7 @@ as it is against our [Terms of use](index.md#terms).** Of course, you can use *mkdocstrings-python Insiders* directly from `git`: ``` -git clone git@github.com:pawamoy-insiders/python +git clone git@github.com:pawamoy-insiders/mkdocstrings-python ``` When cloning from `git`, the package must be installed: diff --git a/docs/schema.json b/docs/schema.json index a34dc090..b4eca004 100644 --- a/docs/schema.json +++ b/docs/schema.json @@ -108,6 +108,18 @@ "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", diff --git a/docs/usage/configuration/headings.md b/docs/usage/configuration/headings.md index e1c2e63a..a9b75e6d 100644 --- a/docs/usage/configuration/headings.md +++ b/docs/usage/configuration/headings.md @@ -387,3 +387,131 @@ plugins:

Docstring of the method.

//// /// + +## `show_symbol_type_heading` + +[:octicons-tag-24: Insiders 1.1.0](../../insiders/changelog.md#1.1.0) + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + + +Show the symbol type in headings. + +This option will prefix headings with +, +, +, + or + types. +See also [`show_symbol_type_toc`][show_symbol_type_toc]. + +To customize symbols, see [Customizing symbol types](../customization.md/#symbol-types). + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + show_symbol_type_heading: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: package.module + options: + show_symbol_type_heading: false +``` + +/// admonition | Preview + type: preview + +//// tab | With symbol type in headings +

module

+

Docstring of the module.

+

attribute

+

Docstring of the module attribute.

+

function

+

Docstring of the function.

+

Class

+

Docstring of the class.

+

method

+

Docstring of the method.

+//// + +//// tab | Without symbol type in headings +

module

+

Docstring of the module.

+

attribute

+

Docstring of the module attribute.

+

function

+

Docstring of the function.

+

Class

+

Docstring of the class.

+

method

+

Docstring of the method.

+//// +/// + +## `show_symbol_type_toc` + +[:octicons-tag-24: Insiders 1.1.0](../../insiders/changelog.md#1.1.0) + +- **:octicons-package-24: Type [`bool`][] :material-equal: `False`{ title="default value" }** + + +Show the symbol type in the Table of Contents. + +This option will prefix items in the ToC with +, +, +, + or + types. +See also [`show_symbol_type_heading`][show_symbol_type_heading]. + +To customize symbols, see [Customizing symbol types](../customization.md/#symbol-types). + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + show_symbol_type_toc: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: package.module + options: + show_symbol_type_toc: false +``` + +/// admonition | Preview + type: preview + +//// tab | With symbol type in ToC + +//// + +//// tab | Without symbol type in ToC + +//// +/// diff --git a/docs/usage/configuration/members.md b/docs/usage/configuration/members.md index 16c707d0..0b6d27e5 100644 --- a/docs/usage/configuration/members.md +++ b/docs/usage/configuration/members.md @@ -544,4 +544,98 @@ package

subpackage_member

Member docstring.

//// -/// \ No newline at end of file +/// + +## `summary` + +[:octicons-heart-fill-24:{ .pulse } Sponsors only](../../insiders/index.md){ .insiders } — +[:octicons-tag-24: Insiders 1.2.0](../../insiders/changelog.md#1.2.0) + +- **:octicons-package-24: Type bool | dict[str, bool] :material-equal: `False`{ title="default value" }** + + +Whether to render summaries of modules, classes, functions (methods) and attributes. + +This option accepts a boolean (`yes`, `true`, `no`, `false` in YAML) +or a dictionary with one or more of the following keys: `attributes`, `functions`, `classes`, `modules`, +with booleans as values. Class methods summary is (de)activated with the `functions` key. +By default, `summary` is false, and by extension all values are false. + +Examples: + +```yaml +summary: true +``` + +```yaml +summary: false +``` + +```yaml +summary: + attributes: false + functions: true + modules: false +``` + +Summaries will be rendered as the corresponding docstring sections. +For example, the summary for attributes will be rendered as an Attributes docstring section. +The section will be rendered in accordance with the [`docstring_section_style`][] option. +If the objects appearing in the summary are also rendered on the page +(or somewhere else on the site), their name will automatically link to their rendered documentation. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + summary: true +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + summary: false +``` + +/// admonition | Preview + type: preview + +//// tab | With all summaries +``` +::: path.to.module.MyClass + options: + summary: true +``` +

MyClass

+

Class docstring.

+

Methods:

+ +

Attributes:

+ +//// + +//// tab | With methods summary only +``` +::: path.to.module.MyClass + options: + summary: + functions: true +``` + +

MyClass

+

Class docstring.

+

Methods:

+ +//// +/// diff --git a/docs/usage/configuration/signatures.md b/docs/usage/configuration/signatures.md index 6b2e8880..da96dc5b 100644 --- a/docs/usage/configuration/signatures.md +++ b/docs/usage/configuration/signatures.md @@ -332,6 +332,52 @@ function(param1, param2=None) //// /// +## `signature_crossrefs` + +[:octicons-tag-24: Insiders 1.0.0](../../insiders/changelog.md#1.0.0) + +Whether to render cross-references for type annotations in signatures. + +When signatures are separated from headings with the [`separate_signature`][] option +and type annotations are shown with the [`show_signature_annotations`][] option, +this option will render a cross-reference (link) for each type annotation in the signature. + +```yaml title="in mkdocs.yml (global configuration)" +plugins: +- mkdocstrings: + handlers: + python: + options: + separate_signature: true + show_signature_annotations: true + signature_crossrefs: false +``` + +```md title="or in docs/some_page.md (local configuration)" +::: path.to.module + options: + separate_signature: true + show_signature_annotations: true + signature_crossrefs: true +``` + +/// admonition | Preview + type: preview + +//// tab | With signature cross-references +

do_format_code

+
do_format_code(code: str, line_length: int) -> str
+
+

Function docstring.

+//// + +//// tab | Without signature cross-references +

do_format_code

+
do_format_code(code: str, line_length: int) -> str
+
+

Function docstring.

+//// +/// ## `unwrap_annotated` diff --git a/docs/usage/customization.md b/docs/usage/customization.md index 7bbed955..9dedbf20 100644 --- a/docs/usage/customization.md +++ b/docs/usage/customization.md @@ -23,6 +23,10 @@ The following CSS classes are used in the generated HTML: - `doc-label`: on `small` elements containing a label - `doc-label-LABEL`: same, where `LABEL` is replaced by the actual label - `doc-md-description`: on `div`s containing HTML descriptions converted from Markdown docstrings +- `doc-symbol`: on `code` tags of symbol types + - `doc-symbol-heading`: on symbol types in headings + - `doc-symbol-toc`: on symbol types in the ToC + - `doc-symbol-KIND`: specific to the kind of object (`module`, `class`, `function`, `method`, `attribute`) /// admonition | Example with colorful labels type: example @@ -53,6 +57,173 @@ The following CSS classes are used in the generated HTML: /// +## Symbol types + +### Colors + +You can customize the colors of the symbol types +(see [`show_symbol_type_heading`][show_symbol_type_heading] and [`show_symbol_type_toc`][show_symbol_type_toc]) +by overriding the values of our CSS variables, for example: + +```css title="docs/css/mkdocstrings.css" +[data-md-color-scheme="default"] { + --doc-symbol-attribute-fg-color: #0079ff; + --doc-symbol-function-fg-color: #00dfa2; + --doc-symbol-method-fg-color: #00dfa2; + --doc-symbol-class-fg-color: #d1b619; + --doc-symbol-module-fg-color: #ff0060; + + --doc-symbol-attribute-bg-color: #0079ff1a; + --doc-symbol-function-bg-color: #00dfa21a; + --doc-symbol-method-bg-color: #00dfa21a; + --doc-symbol-class-bg-color: #d1b6191a; + --doc-symbol-module-bg-color: #ff00601a; +} + +[data-md-color-scheme="slate"] { + --doc-symbol-attribute-fg-color: #963fb8; + --doc-symbol-function-fg-color: #6d67e4; + --doc-symbol-method-fg-color: #6d67e4; + --doc-symbol-class-fg-color: #46c2cb; + --doc-symbol-module-fg-color: #f2f7a1; + + --doc-symbol-attribute-bg-color: #963fb81a; + --doc-symbol-function-bg-color: #6d67e41a; + --doc-symbol-method-bg-color: #6d67e41a; + --doc-symbol-class-bg-color: #46c2cb1a; + --doc-symbol-module-bg-color: #f2f7a11a; +} +``` + +The `[data-md-color-scheme="*"]` selectors work with the [Material for MkDocs] theme. +If you are using another theme, adapt the selectors to this theme +if it supports light and dark themes, +otherwise just override the variables at root level: + +```css title="docs/css/mkdocstrings.css" +:root { + --doc-symbol-attribute-fg-color: #0079ff; + --doc-symbol-function-fg-color: #00dfa2; + --doc-symbol-method-fg-color: #00dfa2; + --doc-symbol-class-fg-color: #d1b619; + --doc-symbol-module-fg-color: #ff0060; + + --doc-symbol-attribute-bg-color: #0079ff1a; + --doc-symbol-function-bg-color: #00dfa21a; + --doc-symbol-method-bg-color: #00dfa21a; + --doc-symbol-class-bg-color: #d1b6191a; + --doc-symbol-module-bg-color: #ff00601a; +} +``` + +/// admonition | Preview + type: preview + +
+ +

+ Try cycling through the themes to see the colors for each theme: + + + + + +

+
+ +/// + +### Names + +You can also change the actual symbol names. +For example, to use single letters instead of truncated types: + +```css title="docs/css/mkdocstrings.css" +.doc-symbol-attribute::after { + content: "A"; +} + +.doc-symbol-function::after { + content: "F"; +} + +.doc-symbol-method::after { + content: "M"; +} + +.doc-symbol-class::after { + content: "C"; +} + +.doc-symbol-module::after { + content: "M"; +} +``` + +/// admonition | Preview + type: preview + +
+ + +
+ +/// + ## Templates Templates are organized into the following tree: @@ -103,6 +274,7 @@ and the Jinja context available in their scope. - `labels`: The module labels. - `contents`: The module contents: docstring and children blocks. - `docstring`: The module docstring. +- `summary`: The automatic summaries of members. - `children`: The module children. Available context: @@ -118,6 +290,7 @@ Available context: - `contents`: The class contents: bases, docstring, source and children blocks. - `bases`: The class bases. - `docstring`: The class docstring. +- `summary`: The automatic summaries of members. - `source`: The class source code. - `children`: The class children. diff --git a/duties.py b/duties.py index cfdc9376..30fb2221 100644 --- a/duties.py +++ b/duties.py @@ -237,7 +237,7 @@ def release(ctx: Context, version: str) -> None: version: The new version number to use. """ origin = ctx.run("git config --get remote.origin.url", silent=True) - if "pawamoy-insiders/python" in origin: + if "pawamoy-insiders/mkdocstrings-python" in origin: ctx.run( lambda: False, title="Not releasing from insiders repository (do that from public repo instead!)", diff --git a/mkdocs.yml b/mkdocs.yml index 6c069795..3d21de72 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -40,7 +40,7 @@ nav: - Development: - Contributing: contributing.md - Code of Conduct: code_of_conduct.md - - Coverage report: coverage.md + # - Coverage report: coverage.md - Insiders: - insiders/index.md - Getting started: @@ -136,7 +136,7 @@ plugins: - scripts/gen_ref_nav.py - literate-nav: nav_file: SUMMARY.md -- coverage +# - coverage - mkdocstrings: handlers: python: @@ -158,6 +158,7 @@ plugins: show_root_heading: true show_root_full_path: false show_signature_annotations: true + show_source: false show_symbol_type_heading: true show_symbol_type_toc: true signature_crossrefs: true diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 6fc2804f..9f6cae4b 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -50,25 +50,21 @@ def chdir(path: str) -> Iterator[None]: # noqa: D103 class PythonHandler(BaseHandler): - """The Python handler class. - - Attributes: - domain: The cross-documentation domain/language for this handler. - enable_inventory: Whether this handler is interested in enabling the creation - of the `objects.inv` Sphinx inventory file. - fallback_theme: The fallback theme. - fallback_config: The configuration used to collect item during autorefs fallback. - default_config: The default rendering options, - see [`default_config`][mkdocstrings_handlers.python.handler.PythonHandler.default_config]. - """ + """The Python handler class.""" domain: str = "py" # to match Sphinx's default domain + """The cross-documentation domain/language for this handler.""" enable_inventory: bool = True + """Whether this handler is interested in enabling the creation of the `objects.inv` Sphinx inventory file.""" fallback_theme = "material" + """The fallback theme.""" fallback_config: ClassVar[dict] = {"fallback": True} + """The configuration used to collect item during autorefs fallback.""" default_config: ClassVar[dict] = { "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, @@ -108,9 +104,11 @@ class PythonHandler(BaseHandler): "annotations_path": "brief", "preload_modules": None, "allow_inspection": True, + "summary": False, "unwrap_annotated": False, } - """ + """Default handler configuration. + Attributes: General options: 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`. @@ -134,6 +132,8 @@ class PythonHandler(BaseHandler): 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. @@ -151,6 +151,7 @@ class PythonHandler(BaseHandler): 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. Attributes: Docstrings options: docstring_style (str): The docstring style to use: `google`, `numpy`, `sphinx`, or `None`. Default: `"google"`. @@ -328,8 +329,28 @@ def render(self, data: CollectorItem, config: Mapping[str, Any]) -> str: # noqa (re.compile(filtr.lstrip("!")), filtr.startswith("!")) for filtr in final_config["filters"] ] - # TODO: goal reached: remove once `signature_crossrefs` feature becomes public - final_config["signature_crossrefs"] = False + summary = final_config["summary"] + if summary is True: + final_config["summary"] = { + "attributes": True, + "functions": True, + "classes": True, + "modules": True, + } + elif summary is False: + final_config["summary"] = { + "attributes": False, + "functions": False, + "classes": False, + "modules": False, + } + else: + final_config["summary"] = { + "attributes": summary.get("attributes", False), + "functions": summary.get("functions", False), + "classes": summary.get("classes", False), + "modules": summary.get("modules", False), + } return template.render( **{ @@ -356,6 +377,10 @@ def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore self.env.filters["filter_objects"] = rendering.do_filter_objects self.env.filters["stash_crossref"] = lambda ref, length: ref self.env.filters["get_template"] = rendering.do_get_template + self.env.filters["as_attributes_section"] = rendering.do_as_attributes_section + self.env.filters["as_functions_section"] = rendering.do_as_functions_section + self.env.filters["as_classes_section"] = rendering.do_as_classes_section + self.env.filters["as_modules_section"] = rendering.do_as_modules_section self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates() def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: D102 (ignore missing docstring) diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index c54eb2cd..b1cb7ffc 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -3,18 +3,26 @@ from __future__ import annotations import enum +import random import re +import string import sys import warnings -from functools import lru_cache +from functools import lru_cache, partial from typing import TYPE_CHECKING, Any, Callable, Match, Pattern, Sequence +from griffe.docstrings.dataclasses import ( + DocstringSectionAttributes, + DocstringSectionClasses, + DocstringSectionFunctions, + DocstringSectionModules, +) from jinja2 import pass_context from markupsafe import Markup from mkdocstrings.loggers import get_logger if TYPE_CHECKING: - from griffe.dataclasses import Alias, Attribute, Function, Object + from griffe.dataclasses import Alias, Attribute, Class, Function, Module, Object from jinja2.runtime import Context from mkdocstrings.handlers.base import CollectorItem @@ -25,7 +33,9 @@ class Order(enum.Enum): """Enumeration for the possible members ordering.""" alphabetical = "alphabetical" + """Alphabetical order.""" source = "source" + """Source code order.""" def _sort_key_alphabetical(item: CollectorItem) -> Any: @@ -65,6 +75,26 @@ def do_format_code(code: str, line_length: int) -> str: return formatter(code, line_length) +_stash_key_alphabet = string.ascii_letters + string.digits + + +def _gen_key(length: int) -> str: + return "_" + "".join(random.choice(_stash_key_alphabet) for _ in range(max(1, length - 1))) # noqa: S311 + + +def _gen_stash_key(stash: dict[str, str], length: int) -> str: + key = _gen_key(length) + while key in stash: + key = _gen_key(length) + return key + + +def _stash_crossref(stash: dict[str, str], crossref: str, *, length: int) -> str: + key = _gen_stash_key(stash, length) + stash[key] = crossref + return key + + def _format_signature(name: Markup, signature: str, line_length: int) -> str: name = str(name).strip() # type: ignore[assignment] signature = signature.strip() @@ -90,8 +120,8 @@ def do_format_signature( function: Function, line_length: int, *, - annotations: bool | None = None, # noqa: ARG001 - crossrefs: bool = False, # noqa: ARG001 + annotations: bool | None = None, + crossrefs: bool = False, ) -> str: """Format a signature using Black. @@ -108,17 +138,40 @@ def do_format_signature( """ env = context.environment template = env.get_template("signature.html") - signature = template.render(context.parent, function=function) + config_annotations = context.parent["config"]["show_signature_annotations"] + old_stash_ref_filter = env.filters["stash_crossref"] + + stash: dict[str, str] = {} + if (annotations or config_annotations) and crossrefs: + env.filters["stash_crossref"] = partial(_stash_crossref, stash) + + if annotations is None: + new_context = context.parent + else: + new_context = dict(context.parent) + new_context["config"] = dict(new_context["config"]) + new_context["config"]["show_signature_annotations"] = annotations + try: + signature = template.render(new_context, function=function, signature=True) + finally: + env.filters["stash_crossref"] = old_stash_ref_filter + signature = _format_signature(callable_path, signature, line_length) - return str( + signature = str( env.filters["highlight"]( - signature, + Markup.escape(signature), language="python", inline=False, classes=["doc-signature"], ), ) + if stash: + for key, value in stash.items(): + signature = re.sub(rf"\b{key}\b", value, signature) + + return signature + @pass_context def do_format_attribute( @@ -127,7 +180,7 @@ def do_format_attribute( attribute: Attribute, line_length: int, *, - crossrefs: bool = False, # noqa: ARG001 + crossrefs: bool = False, ) -> str: """Format an attribute using Black. @@ -142,16 +195,28 @@ def do_format_attribute( The same code, formatted. """ env = context.environment + template = env.get_template("expression.html") annotations = context.parent["config"]["show_signature_annotations"] + separate_signature = context.parent["config"]["separate_signature"] + old_stash_ref_filter = env.filters["stash_crossref"] - signature = str(attribute_path).strip() - if annotations and attribute.annotation: - signature += f": {attribute.annotation}" - if attribute.value: - signature += f" = {attribute.value}" + stash: dict[str, str] = {} + if separate_signature and crossrefs: + env.filters["stash_crossref"] = partial(_stash_crossref, stash) + + try: + signature = str(attribute_path).strip() + if annotations and attribute.annotation: + annotation = template.render(context.parent, expression=attribute.annotation, signature=True) + signature += f": {annotation}" + if attribute.value: + value = template.render(context.parent, expression=attribute.value, signature=True) + signature += f" = {value}" + finally: + env.filters["stash_crossref"] = old_stash_ref_filter signature = do_format_code(signature, line_length) - return str( + signature = str( env.filters["highlight"]( Markup.escape(signature), language="python", @@ -160,6 +225,12 @@ def do_format_attribute( ), ) + if stash: + for key, value in stash.items(): + signature = re.sub(rf"\b{key}\b", value, signature) + + return signature + def do_order_members( members: Sequence[Object | Alias], @@ -379,3 +450,79 @@ def do_get_template(obj: Object) -> str: """ extra_data = getattr(obj, "extra", {}).get("mkdocstrings", {}) return extra_data.get("template", "") or f"{obj.kind.value}.html" + + +@pass_context +def do_as_attributes_section( + context: Context, # noqa: ARG001 + attributes: Sequence[Attribute], # noqa: ARG001 + *, + check_public: bool = True, # noqa: ARG001 +) -> DocstringSectionAttributes: + """Build an attributes section from a list of attributes. + + Parameters: + attributes: The attributes to build the section from. + check_public: Whether to check if the attribute is public. + + Returns: + An attributes docstring section. + """ + return DocstringSectionAttributes([]) + + +@pass_context +def do_as_functions_section( + context: Context, # noqa: ARG001 + functions: Sequence[Function], # noqa: ARG001 + *, + check_public: bool = True, # noqa: ARG001 +) -> DocstringSectionFunctions: + """Build a functions section from a list of functions. + + Parameters: + functions: The functions to build the section from. + check_public: Whether to check if the function is public. + + Returns: + A functions docstring section. + """ + return DocstringSectionFunctions([]) + + +@pass_context +def do_as_classes_section( + context: Context, # noqa: ARG001 + classes: Sequence[Class], # noqa: ARG001 + *, + check_public: bool = True, # noqa: ARG001 +) -> DocstringSectionClasses: + """Build a classes section from a list of classes. + + Parameters: + classes: The classes to build the section from. + check_public: Whether to check if the class is public. + + Returns: + A classes docstring section. + """ + return DocstringSectionClasses([]) + + +@pass_context +def do_as_modules_section( + context: Context, # noqa: ARG001 + modules: Sequence[Module], # noqa: ARG001 + *, + check_public: bool = True, # noqa: ARG001 +) -> DocstringSectionModules: + """Build a modules section from a list of modules. + + Parameters: + modules: The modules to build the section from. + check_public: Whether to check if the module is public. + + Returns: + A modules docstring section. + """ + return DocstringSectionModules([]) diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html b/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html index 764e9a7d..3f1d887e 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/attribute.html @@ -16,14 +16,16 @@ {% set attribute_name = attribute.path if show_full_path else attribute.name %} {% if not root or config.show_root_heading %} - - {% filter heading(heading_level, + {% filter heading( + heading_level, role="data" if attribute.parent.kind.value == "module" else "attr", id=html_id, class="doc doc-heading", - toc_label=attribute.name) %} + toc_label=(' '|safe if config.show_symbol_type_toc else '') + attribute.name, + ) %} {% block heading scoped %} + {% if config.show_symbol_type_heading %}{% endif %} {% if config.separate_signature %} {{ attribute_name }} {% else %} @@ -51,12 +53,14 @@ {% endblock signature %} {% else %} + {% if config.show_root_toc_entry %} {% filter heading(heading_level, role="data" if attribute.parent.kind.value == "module" else "attr", id=html_id, - toc_label=attribute.name, - hidden=True) %} + toc_label=(' '|safe if config.show_symbol_type_toc else '') + attribute.name, + hidden=True, + ) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/class.html b/src/mkdocstrings_handlers/python/templates/material/_base/class.html index ae8a2774..fb7ca764 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/class.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/class.html @@ -16,14 +16,16 @@ {% set class_name = class.path if show_full_path else class.name %} {% if not root or config.show_root_heading %} - - {% filter heading(heading_level, + {% filter heading( + heading_level, role="class", id=html_id, class="doc doc-heading", - toc_label=class.name) %} + toc_label=(' '|safe if config.show_symbol_type_toc else '') + class.name, + ) %} {% block heading scoped %} + {% if config.show_symbol_type_heading %}{% endif %} {% if config.separate_signature %} {{ class_name }} {% elif config.merge_init_into_class and "__init__" in class.all_members %} @@ -62,8 +64,9 @@ {% filter heading(heading_level, role="class", id=html_id, - toc_label=class.name, - hidden=True) %} + toc_label=(' '|safe if config.show_symbol_type_toc else '') + class.name, + hidden=True, + ) %} {% endfilter %} {% endif %} {% set heading_level = heading_level - 1 %} @@ -135,4 +138,5 @@ {% endwith %} + diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/attributes.html b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/attributes.html index 6dc82d66..0b291574 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/docstring/attributes.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/docstring/attributes.html @@ -16,7 +16,7 @@ {% for attribute in section.value %} - {{ attribute.name }} + {{ attribute.name }} {% if attribute.annotation %} {% with expression = attribute.annotation %} @@ -40,7 +40,7 @@