Skip to content

Commit 2c9aa24

Browse files
committed
Merge branch 'main' into backlinks
2 parents 8a4de0c + ab3b227 commit 2c9aa24

File tree

10 files changed

+165
-65
lines changed

10 files changed

+165
-65
lines changed

CHANGELOG.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,50 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

77
<!-- insertion marker -->
8+
## [1.15.0](https://github.com/mkdocstrings/python/releases/tag/1.15.0) - 2025-02-11
9+
10+
<small>[Compare with 1.14.6](https://github.com/mkdocstrings/python/compare/1.14.6...1.15.0)</small>
11+
12+
### Features
13+
14+
- Support cross-referencing constructor parameters in instance attribute values ([f07bf58](https://github.com/mkdocstrings/python/commit/f07bf58a7358dea106032c7da27098e7617eefa0) by Timothée Mazzucotelli).
15+
16+
## [1.14.6](https://github.com/mkdocstrings/python/releases/tag/1.14.6) - 2025-02-07
17+
18+
<small>[Compare with 1.14.5](https://github.com/mkdocstrings/python/compare/1.14.5...1.14.6)</small>
19+
20+
### Bug Fixes
21+
22+
- Catch alias resolution errors when getting aliases for an identifier ([0aaa260](https://github.com/mkdocstrings/python/commit/0aaa260139afe2e3ab85d62224c90a389df64978) by Timothée Mazzucotelli). [Issue-358](https://github.com/mkdocstrings/griffe/discussions/358)
23+
24+
### Code Refactoring
25+
26+
- Improve translations for Simplified Chinese and Japanese ([753a0df](https://github.com/mkdocstrings/python/commit/753a0df8f91f1cf42fb7e56b7fdd312b2bd652ab) by Zhikang Yan). [PR-244](https://github.com/mkdocstrings/python/pull/244)
27+
28+
## [1.14.5](https://github.com/mkdocstrings/python/releases/tag/1.14.5) - 2025-02-05
29+
30+
<small>[Compare with 1.14.4](https://github.com/mkdocstrings/python/compare/1.14.4...1.14.5)</small>
31+
32+
### Bug Fixes
33+
34+
- Remove type from property docstring summary in summary sections ([15f2cd4](https://github.com/mkdocstrings/python/commit/15f2cd48b79a1f062086a47ea0c6bc52d89786d8) by Uchechukwu Orji). [PR-242](https://github.com/mkdocstrings/python/pull/242)
35+
36+
## [1.14.4](https://github.com/mkdocstrings/python/releases/tag/1.14.4) - 2025-02-04
37+
38+
<small>[Compare with 1.14.3](https://github.com/mkdocstrings/python/compare/1.14.3...1.14.4)</small>
39+
40+
### Bug Fixes
41+
42+
- Deactivate Pydantic validation on Python 3.9 is `eval-type-backport` is not available (for modern typing syntax support) ([0de0e5e](https://github.com/mkdocstrings/python/commit/0de0e5e57f8f22e039b0d19aad6341ce7ab3da9f) by Timothée Mazzucotelli). [Issue-241](https://github.com/mkdocstrings/python/issues/241)
43+
44+
## [1.14.3](https://github.com/mkdocstrings/python/releases/tag/1.14.3) - 2025-02-04
45+
46+
<small>[Compare with 1.14.2](https://github.com/mkdocstrings/python/compare/1.14.2...1.14.3)</small>
47+
48+
### Bug Fixes
49+
50+
- Let dataclass implement `__init__` method, set extra fields in `get_options` ([477b9e4](https://github.com/mkdocstrings/python/commit/477b9e447ef9717c6edcb14bd4c53f9cacc555b8) by Timothée Mazzucotelli).
51+
852
## [1.14.2](https://github.com/mkdocstrings/python/releases/tag/1.14.2) - 2025-02-03
953

1054
<small>[Compare with 1.14.1](https://github.com/mkdocstrings/python/compare/1.14.1...1.14.2)</small>

scripts/mkdocs_hooks.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Generate a JSON schema of the Python handler configuration."""
22

33
import json
4-
from dataclasses import fields
4+
from dataclasses import dataclass, fields
55
from os.path import join
66
from typing import Any
77

@@ -25,7 +25,12 @@ def on_post_build(config: MkDocsConfig, **kwargs: Any) -> None: # noqa: ARG001
2525
if TypeAdapter is None:
2626
logger.info("Pydantic is not installed, skipping JSON schema generation")
2727
return
28-
adapter = TypeAdapter(PythonInputConfig)
28+
29+
@dataclass
30+
class PythonHandlerSchema:
31+
python: PythonInputConfig
32+
33+
adapter = TypeAdapter(PythonHandlerSchema)
2934
schema = adapter.json_schema()
3035
schema["$schema"] = "https://json-schema.org/draft-07/schema"
3136
with open(join(config.site_dir, "schema.json"), "w") as file:

src/mkdocstrings_handlers/python/config.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@
77
from dataclasses import field, fields
88
from typing import TYPE_CHECKING, Annotated, Any, Literal
99

10+
from mkdocstrings.loggers import get_logger
11+
1012
# YORE: EOL 3.10: Replace block with line 2.
1113
if sys.version_info >= (3, 11):
1214
from typing import Self
1315
else:
1416
from typing_extensions import Self
1517

18+
19+
logger = get_logger(__name__)
20+
21+
1622
try:
1723
# When Pydantic is available, use it to validate options (done automatically).
1824
# Users can therefore opt into validation by installing Pydantic in development/CI.
@@ -30,6 +36,17 @@
3036
if getattr(pydantic, "__version__", "1.").startswith("1."):
3137
raise ImportError # noqa: TRY301
3238

39+
if sys.version_info < (3, 10):
40+
try:
41+
import eval_type_backport # noqa: F401
42+
except ImportError:
43+
logger.debug(
44+
"Pydantic needs the `eval-type-backport` package to be installed "
45+
"for modern type syntax to work on Python 3.9. "
46+
"Deactivating Pydantic validation for Python handler options.",
47+
)
48+
raise
49+
3350
from inspect import cleandoc
3451

3552
from pydantic import Field as BaseField
@@ -843,15 +860,6 @@ def _extract_extra(cls, data: dict[str, Any]) -> tuple[dict[str, Any], dict[str,
843860
copy = data.copy()
844861
return {name: copy.pop(name) for name in data if name not in field_names}, copy
845862

846-
# YORE: Bump 2: Remove block.
847-
def __init__(self, **kwargs: Any) -> None:
848-
"""Initialize the instance."""
849-
extra_fields = self._extract_extra(kwargs)
850-
for name, value in kwargs.items():
851-
object.__setattr__(self, name, value)
852-
if extra_fields:
853-
object.__setattr__(self, "_extra", extra_fields)
854-
855863
@classmethod
856864
def coerce(cls, **data: Any) -> MutableMapping[str, Any]:
857865
"""Coerce data."""

src/mkdocstrings_handlers/python/handler.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,17 @@ def get_options(self, local_options: Mapping[str, Any]) -> HandlerOptions:
188188

189189
extra = {**self.global_options.get("extra", {}), **local_options.get("extra", {})}
190190
options = {**self.global_options, **local_options, "extra": extra}
191-
# YORE: Bump 2: Replace `, **unknown_extra` with `` within line.
192191
try:
193-
return PythonOptions.from_data(**options, **unknown_extra)
192+
# YORE: Bump 2: Replace `opts =` with `return` within line.
193+
opts = PythonOptions.from_data(**options)
194194
except Exception as error:
195195
raise PluginError(f"Invalid options: {error}") from error
196196

197+
# YORE: Bump 2: Remove block.
198+
for key, value in unknown_extra.items():
199+
object.__setattr__(opts, key, value)
200+
return opts
201+
197202
def collect(self, identifier: str, options: PythonOptions) -> CollectorItem: # noqa: D102
198203
module_name = identifier.split(".", 1)[0]
199204
unknown_module = module_name not in self._modules_collection
@@ -307,17 +312,24 @@ def update_env(self, config: Any) -> None: # noqa: ARG002
307312
self.env.tests["existing_template"] = lambda template_name: template_name in self.env.list_templates()
308313

309314
def get_aliases(self, identifier: str) -> tuple[str, ...]: # noqa: D102 (ignore missing docstring)
315+
if "(" in identifier:
316+
identifier, parameter = identifier.split("(", 1)
317+
parameter.removesuffix(")")
318+
else:
319+
parameter = ""
310320
try:
311321
data = self._modules_collection[identifier]
312-
except KeyError:
322+
except (KeyError, AliasResolutionError):
313323
return ()
314324
aliases = [data.path]
315325
try:
316326
for alias in [data.canonical_path, *data.aliases]:
317327
if alias not in aliases:
318328
aliases.append(alias)
319329
except AliasResolutionError:
320-
return tuple(aliases)
330+
pass
331+
if parameter:
332+
return tuple(f"{alias}({parameter})" for alias in aliases)
321333
return tuple(aliases)
322334

323335
def normalize_extension_paths(self, extensions: Sequence) -> Sequence:

src/mkdocstrings_handlers/python/rendering.py

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from mkdocstrings.loggers import get_logger
3333

3434
if TYPE_CHECKING:
35-
from collections.abc import Sequence
35+
from collections.abc import Iterator, Sequence
3636

3737
from griffe import Attribute, Class, Function, Module
3838
from jinja2 import Environment, Template
@@ -326,26 +326,47 @@ def repl(match: Match) -> str:
326326
return Markup(text).format(**variables)
327327

328328

329-
def do_split_path(path: str, full_path: str) -> list[tuple[str, str]]:
329+
_split_path_re = re.compile(r"([.(]?)([\w]+)(\))?")
330+
_splitable_re = re.compile(r"[().]")
331+
332+
333+
def do_split_path(path: str, full_path: str) -> Iterator[tuple[str, str, str, str]]:
330334
"""Split object paths for building cross-references.
331335
332336
Parameters:
333337
path: The path to split.
338+
full_path: The full path, used to compute correct paths for each part of the path.
334339
335-
Returns:
336-
A list of pairs (title, full path).
340+
Yields:
341+
4-tuples: prefix, word, full path, suffix.
337342
"""
338-
if "." not in path:
339-
return [(path, full_path)]
340-
pairs = []
341-
full_path = ""
342-
for part in path.split("."):
343-
if full_path:
344-
full_path += f".{part}"
345-
else:
346-
full_path = part
347-
pairs.append((part, full_path))
348-
return pairs
343+
# Path is a single word, yield full path directly.
344+
if not _splitable_re.search(path):
345+
yield ("", path, full_path, "")
346+
return
347+
348+
current_path = ""
349+
if path == full_path:
350+
# Split full path and yield directly without storing data in a dict.
351+
for match in _split_path_re.finditer(full_path):
352+
prefix, word, suffix = match.groups()
353+
current_path = f"{current_path}{prefix}{word}{suffix or ''}" if current_path else word
354+
yield prefix or "", word, current_path, suffix or ""
355+
return
356+
357+
# Split full path first to store tuples in a dict.
358+
elements = {}
359+
for match in _split_path_re.finditer(full_path):
360+
prefix, word, suffix = match.groups()
361+
current_path = f"{current_path}{prefix}{word}{suffix or ''}" if current_path else word
362+
elements[word] = (prefix or "", word, current_path, suffix or "")
363+
364+
# Then split path and pick tuples from the dict.
365+
first = True
366+
for match in _split_path_re.finditer(path):
367+
prefix, word, current_path, suffix = elements[match.group(2)]
368+
yield "" if first else prefix, word, current_path, suffix
369+
first = False
349370

350371

351372
def _keep_object(name: str, filters: Sequence[tuple[Pattern, bool]]) -> bool:
@@ -539,11 +560,20 @@ def do_as_attributes_section(
539560
Returns:
540561
An attributes docstring section.
541562
"""
563+
564+
def _parse_docstring_summary(attribute: Attribute) -> str:
565+
if attribute.docstring is None:
566+
return ""
567+
line = attribute.docstring.value.split("\n", 1)[0]
568+
if ":" in line and attribute.docstring.parser_options.get("returns_type_in_property_summary", False):
569+
_, line = line.split(":", 1)
570+
return line
571+
542572
return DocstringSectionAttributes(
543573
[
544574
DocstringAttribute(
545575
name=attribute.name,
546-
description=attribute.docstring.value.split("\n", 1)[0] if attribute.docstring else "",
576+
description=_parse_docstring_summary(attribute),
547577
annotation=attribute.annotation,
548578
value=attribute.value, # type: ignore[arg-type]
549579
)

src/mkdocstrings_handlers/python/templates/material/_base/expression.html.jinja

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ which is a tree-like structure representing a Python expression.
3131
{%- elif annotation_path == "full" -%}
3232
{%- set annotation = full -%}
3333
{%- endif -%}
34-
{%- for title, path in annotation|split_path(full) -%}
34+
{%- for prefix, title, path, suffix in annotation|split_path(full) -%}
35+
{{ prefix }}
3536
{%- if not signature -%}
3637
{#- Always render cross-references outside of signatures. We don't need to stash them. -#}
3738
<autoref identifier="{{ path }}"{% if backlink_type %} backlink-type="{{ backlink_type }}" backlink-anchor="{{ html_id }}"{% endif %} optional{% if title != path %} hover{% endif %}>{{ title }}</autoref>
@@ -44,7 +45,7 @@ which is a tree-like structure representing a Python expression.
4445
{#- We're in a signature but cross-references are disabled, we just render the title. -#}
4546
{{ title }}
4647
{%- endif -%}
47-
{%- if not loop.last -%}.{%- endif -%}
48+
{{ suffix }}
4849
{%- endfor -%}
4950
{%- endwith -%}
5051
{%- endmacro -%}

src/mkdocstrings_handlers/python/templates/material/_base/languages/ja.html.jinja

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@
1010
{% macro t(key) %}{{ {
1111
"ATTRIBUTE": "属性",
1212
"Attributes:": "属性:",
13-
"Classes:": "",
14-
"CLASS": "",
13+
"Classes:": "クラス:",
14+
"CLASS": "クラス",
1515
"DEFAULT:": "デフォルト:",
1616
"Default": "デフォルト",
1717
"default:": "デフォルト:",
1818
"DESCRIPTION": "デスクリプション",
1919
"Description": "デスクリプション",
2020
"Examples:": "例:",
21-
"Functions:": "",
22-
"FUNCTION": "",
23-
"Methods:": "",
24-
"METHOD": "",
25-
"Modules:": "",
26-
"MODULE": "",
21+
"Functions:": "関数:",
22+
"FUNCTION": "関数",
23+
"Methods:": "メソッド:",
24+
"METHOD": "メソッド",
25+
"Modules:": "モジュール:",
26+
"MODULE": "モジュール",
2727
"Name": "名前",
2828
"Other Parameters:": "他の引数:",
2929
"PARAMETER": "引数",

src/mkdocstrings_handlers/python/templates/material/_base/languages/zh.html.jinja

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@
1010
{% macro t(key) %}{{ {
1111
"ATTRIBUTE": "属性",
1212
"Attributes:": "属性:",
13-
"Classes:": "",
14-
"CLASS": "",
13+
"Classes:": "类:",
14+
"CLASS": "",
1515
"DEFAULT:": "默认:",
1616
"Default": "默认",
1717
"default:": "默认:",
1818
"DESCRIPTION": "描述",
1919
"Description": "描述",
2020
"Examples:": "示例:",
21-
"Functions:": "",
22-
"FUNCTION": "",
23-
"Methods:": "",
24-
"METHOD": "",
25-
"Modules:": "",
26-
"MODULE": "",
21+
"Functions:": "函数:",
22+
"FUNCTION": "函数",
23+
"Methods:": "方法:",
24+
"METHOD": "方法",
25+
"Modules:": "模块:",
26+
"MODULE": "模块",
2727
"Name": "名称",
2828
"Other Parameters:": "其他参数:",
2929
"PARAMETER": "参数",

src/mkdocstrings_handlers/python/templates/readthedocs/_base/languages/ja.html.jinja

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44
{% macro t(key) %}{{ {
55
"ATTRIBUTE": "属性",
66
"Attributes:": "属性:",
7-
"Classes:": "",
8-
"CLASS": "",
7+
"Classes:": "クラス:",
8+
"CLASS": "クラス",
99
"DEFAULT:": "デフォルト:",
1010
"Default": "デフォルト",
1111
"default:": "デフォルト:",
1212
"DESCRIPTION": "デスクリプション",
1313
"Description": "デスクリプション",
1414
"Examples:": "例:",
15-
"Functions:": "",
16-
"FUNCTION": "",
17-
"Methods:": "",
18-
"METHOD": "",
19-
"Modules:": "",
20-
"MODULE": "",
15+
"Functions:": "関数:",
16+
"FUNCTION": "関数",
17+
"Methods:": "メソッド:",
18+
"METHOD": "メソッド",
19+
"Modules:": "モジュール:",
20+
"MODULE": "モジュール",
2121
"Name": "名前",
2222
"Other Parameters:": "他の引数:",
2323
"PARAMETER": "引数",

0 commit comments

Comments
 (0)