diff --git a/.copier-answers.yml b/.copier-answers.yml index 52bd8acf..9869a311 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: 0.16.2 +_commit: 0.16.5 _src_path: gh:pawamoy/copier-pdm author_email: pawamoy@pm.me author_fullname: Timothée Mazzucotelli diff --git a/CHANGELOG.md b/CHANGELOG.md index faed7fef..a6deb264 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.4.0](https://github.com/mkdocstrings/python/releases/tag/1.4.0) - 2023-08-18 + +[Compare with 1.3.0](https://github.com/mkdocstrings/python/compare/1.3.0...1.4.0) + +### Features + +- Support new Griffe expressions (in v0.33) ([9b8e1b1](https://github.com/mkdocstrings/python/commit/9b8e1b1604b978cf2d89b7abf826cf4407f92394) by Timothée Mazzucotelli). + +### Code Refactoring + +- Deprecate `crossref` and `multi_crossref` filters ([4fe3d20](https://github.com/mkdocstrings/python/commit/4fe3d2051047061780e20683da6513a7c8d91829) by Timothée Mazzucotelli). + ## [1.3.0](https://github.com/mkdocstrings/python/releases/tag/1.3.0) - 2023-08-06 [Compare with 1.2.1](https://github.com/mkdocstrings/python/compare/1.2.1...1.3.0) diff --git a/Makefile b/Makefile index 686de675..7e8de7cc 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .DEFAULT_GOAL := help SHELL := bash DUTY := $(if $(VIRTUAL_ENV),,pdm run) duty -export PDM_MULTIRUN_VERSIONS ?= 3.8 3.9 3.10 3.11 +export PDM_MULTIRUN_VERSIONS ?= 3.8 3.9 3.10 3.11 3.12 args = $(foreach a,$($(subst -,_,$1)_args),$(if $(value $a),$a="$($a)")) check_quality_args = files diff --git a/docs/insiders/index.md b/docs/insiders/index.md index 71c06b92..66146fff 100644 --- a/docs/insiders/index.md +++ b/docs/insiders/index.md @@ -220,7 +220,7 @@ by the [ISC License][license]. However, we kindly ask you to respect our [goals completed]: #goals-completed [github sponsor profile]: https://github.com/sponsors/pawamoy [billing cycle]: https://docs.github.com/en/github/setting-up-and-managing-billing-and-payments-on-github/changing-the-duration-of-your-billing-cycle -[license]: ../license/ +[license]: ../license.md [private forks]: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/removing-a-collaborator-from-a-personal-repository diff --git a/docs/insiders/installation.md b/docs/insiders/installation.md index 88ebd021..bb387d99 100644 --- a/docs/insiders/installation.md +++ b/docs/insiders/installation.md @@ -13,6 +13,16 @@ you need to [become an eligible sponsor] of @pawamoy on GitHub. ## Installation +### with PyPI Insiders + +[PyPI Insiders](https://pawamoy.github.io/pypi-insiders/) +is a tool that helps you keep up-to-date versions +of Insiders projects in the PyPI index of your choice +(self-hosted, Google registry, Artifactory, etc.). + +See [how to install it](https://pawamoy.github.io/pypi-insiders/#installation) +and [how to use it](https://pawamoy.github.io/pypi-insiders/#usage). + ### with pip (ssh/https) *mkdocstrings-python Insiders* can be installed with `pip` [using SSH][using ssh]: @@ -97,7 +107,7 @@ or installing a package (with pip), and depending on the registry you are using Please check the documentation of your registry to learn how to configure your environment. **We kindly ask that you do not upload the distributions to public registries, -as it is against our [Terms of use](../#terms).** +as it is against our [Terms of use](index.md#terms).** >? TIP: **Full example with `pypiserver`** > In this example we use [pypiserver] to serve a local PyPI index. diff --git a/docs/usage/configuration/signatures.md b/docs/usage/configuration/signatures.md index 9d978fb5..dbf3b654 100644 --- a/docs/usage/configuration/signatures.md +++ b/docs/usage/configuration/signatures.md @@ -16,6 +16,10 @@ Possible values: - `source`: render annotations as written in the source. For example if you imported `typing` as `t`, it will render `typing.Sequence` as `t.Sequence`. Each part will cross-reference the relevant object: `t` will link to the `typing` module and `Sequence` will link to the `Sequence` type. +- `full`: render annotations with their full path (the opposite of brief). + For example if you import `Sequence` and `Pattern` from `typing` and annoate something using + `Sequence[Pattern]`, it will render as `typing.Sequence[typing.Pattern]`, with each part + cross-referencing the corresponding object. ```yaml title="in mkdocs.yml (global configuration)" plugins: @@ -32,6 +36,11 @@ plugins: annotations_path: source ``` + +/// admonition | Preview + type: preview + +//// tab | Brief annotations ```python import markdown import markupsafe @@ -47,13 +56,9 @@ def convert(text: str, md: markdown.Markdown) -> markupsafe.Markup: Returns: Converted markup. """ - return Markup(md.convert(text)) + return markupsafe.Markup(md.convert(text)) ``` -/// admonition | Preview - type: preview - -//// tab | Brief annotations

convert(text, md)

Convert text to Markdown.

Parameters:

@@ -71,6 +76,59 @@ def convert(text: str, md: markdown.Markdown) -> markupsafe.Markup: //// //// tab | Source annotations +```python +import markdown +from markupsafe import Markup + + +def convert(text: str, md: markdown.Markdown) -> Markup: + """Convert text to Markdown. + + Parameters: + text: The text to convert. + md: A Markdown instance. + + Returns: + Converted markup. + """ + return Markup(md.convert(text)) +``` + +

convert(text, md)

+

Convert text to Markdown.

+

Parameters:

+ +**Type** | **Description** | **Default** +---------- | ------------------------ | ----------- +[`str`][] | The text to convert. | *required* +markdown.Markdown | A Markdown instance. | *required* + +

Returns:

+ +**Type** | **Name** | **Description** +---------- | ----------- | --------------- +[`Markup`](#ref-to-markup){ .external title="markupsafe.Markup" } | `text` | Converted markup. +//// + +//// tab | Full annotations +```python +from markdown import Markdown +from markupsafe import Markup + + +def convert(text: str, md: Markdown) -> Markup: + """Convert text to Markdown. + + Parameters: + text: The text to convert. + md: A Markdown instance. + + Returns: + Converted markup. + """ + return Markup(md.convert(text)) +``` +

convert(text, md)

Convert text to Markdown.

Parameters:

diff --git a/duties.py b/duties.py index c8e616a4..1e37569f 100644 --- a/duties.py +++ b/duties.py @@ -53,10 +53,10 @@ def merge(d1: Any, d2: Any) -> Any: # noqa: D103 def mkdocs_config() -> str: # noqa: D103 - from mkdocs import utils + import mergedeep - # patch YAML loader to merge arrays - utils.merge = merge + # force YAML loader to merge arrays + mergedeep.merge = merge if "+insiders" in pkgversion("mkdocs-material"): return "mkdocs.insiders.yml" diff --git a/mkdocs.yml b/mkdocs.yml index c7bc867b..4bf8abe1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,6 +6,12 @@ repo_name: "mkdocstrings/python" site_dir: "site" watch: [mkdocs.yml, README.md, CONTRIBUTING.md, CHANGELOG.md, src/mkdocstrings_handlers] copyright: Copyright © 2021 Timothée Mazzucotelli +edit_uri: edit/main/docs/ + +validation: + omitted_files: warn + absolute_links: warn + unrecognized_links: warn nav: - Home: diff --git a/pyproject.toml b/pyproject.toml index 3f53308b..c25588d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Documentation", "Topic :: Software Development", "Topic :: Software Development :: Documentation", @@ -29,7 +30,7 @@ classifiers = [ ] dependencies = [ "mkdocstrings>=0.20", - "griffe>=0.30,<0.33", + "griffe>=0.33", ] [project.urls] @@ -61,7 +62,7 @@ docs = [ "black>=23.1", "markdown-callouts>=0.2", "markdown-exec>=0.5", - "mkdocs>=1.3", + "mkdocs>=1.5", "mkdocs-coverage>=0.2", "mkdocs-gen-files>=0.3", "mkdocs-git-committers-plugin-2>=1.1", diff --git a/src/mkdocstrings_handlers/python/handler.py b/src/mkdocstrings_handlers/python/handler.py index 7a7d0da3..bd25424d 100644 --- a/src/mkdocstrings_handlers/python/handler.py +++ b/src/mkdocstrings_handlers/python/handler.py @@ -340,6 +340,7 @@ def update_env(self, md: Markdown, config: dict) -> None: # noqa: D102 (ignore self.env.trim_blocks = True self.env.lstrip_blocks = True self.env.keep_trailing_newline = False + self.env.filters["split_path"] = rendering.do_split_path self.env.filters["crossref"] = rendering.do_crossref self.env.filters["multi_crossref"] = rendering.do_multi_crossref self.env.filters["order_members"] = rendering.do_order_members diff --git a/src/mkdocstrings_handlers/python/rendering.py b/src/mkdocstrings_handlers/python/rendering.py index ce6cf9cb..de20f799 100644 --- a/src/mkdocstrings_handlers/python/rendering.py +++ b/src/mkdocstrings_handlers/python/rendering.py @@ -5,6 +5,7 @@ import enum import re import sys +import warnings from functools import lru_cache from typing import TYPE_CHECKING, Any, Callable, Match, Pattern, Sequence @@ -131,8 +132,17 @@ def do_order_members( return sorted(members, key=order_map[order]) +@lru_cache +def _warn_crossref() -> None: + warnings.warn( + "The `crossref` filter is deprecated and will be removed in a future version", + DeprecationWarning, + stacklevel=1, + ) + + def do_crossref(path: str, *, brief: bool = True) -> Markup: - """Filter to create cross-references. + """Deprecated. Filter to create cross-references. Parameters: path: The path to link to. @@ -141,14 +151,24 @@ def do_crossref(path: str, *, brief: bool = True) -> Markup: Returns: Markup text. """ + _warn_crossref() full_path = path if brief: path = full_path.split(".")[-1] return Markup("{path}").format(full_path=full_path, path=path) +@lru_cache +def _warn_multi_crossref() -> None: + warnings.warn( + "The `multi_crossref` filter is deprecated and will be removed in a future version", + DeprecationWarning, + stacklevel=1, + ) + + def do_multi_crossref(text: str, *, code: bool = True) -> Markup: - """Filter to create cross-references. + """Deprecated. Filter to create cross-references. Parameters: text: The text to scan. @@ -157,6 +177,7 @@ def do_multi_crossref(text: str, *, code: bool = True) -> Markup: Returns: Markup text. """ + _warn_multi_crossref() group_number = 0 variables = {} @@ -174,6 +195,28 @@ def repl(match: Match) -> str: return Markup(text).format(**variables) +def do_split_path(path: str, full_path: str) -> list[tuple[str, str]]: + """Split object paths for building cross-references. + + Parameters: + path: The path to split. + + Returns: + A list of pairs (title, full path). + """ + if "." not in path: + return [(path, full_path)] + pairs = [] + full_path = "" + for part in path.split("."): + if full_path: + full_path += f".{part}" + else: + full_path = part + pairs.append((part, full_path)) + return pairs + + def _keep_object(name: str, filters: Sequence[tuple[Pattern, bool]]) -> bool: keep = None rules = set() diff --git a/src/mkdocstrings_handlers/python/templates/material/_base/expression.html b/src/mkdocstrings_handlers/python/templates/material/_base/expression.html index 9bcfc867..52d3a624 100644 --- a/src/mkdocstrings_handlers/python/templates/material/_base/expression.html +++ b/src/mkdocstrings_handlers/python/templates/material/_base/expression.html @@ -1,14 +1,46 @@ -{%- set original_expression = expression -%} -{%- if original_expression is iterable and original_expression is not string -%} - {%- for expression in original_expression -%} - {%- include "expression.html" with context -%} - {%- endfor -%} -{%- elif original_expression is string -%} - {{ original_expression }} -{%- else -%} - {%- with annotation = original_expression|attr(config.annotations_path) -%} - {%- filter stash_crossref(length=annotation|length) -%} - {{ annotation }} - {%- endfilter -%} +{%- macro crossref(name, annotation_path) -%} + {%- with full = name.canonical_path -%} + {%- if annotation_path == "brief" -%} + {%- set annotation = name.canonical_name -%} + {%- elif annotation_path == "source" -%} + {%- set annotation = name.name -%} + {%- elif annotation_path == "full" -%} + {%- set annotation = full -%} + {%- endif -%} + {%- for title, path in annotation|split_path(full) -%} + {%- filter stash_crossref(length=title|length) -%} + {{ title }} + {%- endfilter -%} + {%- if not loop.last -%}.{%- endif -%} + {%- endfor -%} {%- endwith -%} -{%- endif -%} +{%- endmacro -%} + +{%- macro render(expression, annotations_path) -%} + {%- if expression is string -%} + {%- if signature -%}{{ expression|safe }}{%- else -%}{{ expression }}{%- endif -%} + {%- elif expression.classname == "ExprName" -%} + {{ crossref(expression, annotations_path) }} + {%- elif expression.classname == "ExprAttribute" -%} + {%- if annotations_path == "brief" -%} + {{ render(expression.last, "brief") }} + {%- elif annotations_path == "full" -%} + {{ render(expression.first, "full") }} + {%- for element in expression -%} + {%- if not loop.first -%} + {{ render(element, "brief") }} + {%- endif -%} + {%- endfor -%} + {%- else -%} + {%- for element in expression -%} + {{ render(element, annotations_path) }} + {%- endfor -%} + {%- endif -%} + {%- else -%} + {%- for element in expression -%} + {{ render(element, annotations_path) }} + {%- endfor -%} + {%- endif -%} +{%- endmacro -%} + +{{ render(expression, config.annotations_path) }}