Skip to content

Commit 306ba31

Browse files
Fixes for mkdocstrings-python 1.14 changes
1 parent a898f0d commit 306ba31

File tree

11 files changed

+191
-73
lines changed

11 files changed

+191
-73
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# mkdocstring-python-xref changes
22

3+
## 1.14.0
4+
5+
* Work with mkdocstrings-python 1.14 or later
6+
* Drop python 3.8 support
7+
* Fix extra files in wheel's RECORD
8+
9+
## 1.6.2
10+
11+
* Use griffe 1.0 or later
12+
313
## 1.6.1
414

515
* Available on conda-forge

Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ help:
7979
@$(ECHO) "build - Build wheel"
8080
@$(ECHO) "build-wheel - Build wheel."
8181
@$(ECHO) "build-conda - Build conda package (requires whl2conda)"
82+
@$(ECHO) "unpacked-wheel - Build wheel and unpack it (for debugging)"
8283
@$(ECHO)
8384
@$(ECHO) "$(SECTION_COLOR)--- upload ---$(COLORLESS)"
8485
@$(ECHO) "upload - Upload wheel to pypi (requires authorization)"
@@ -164,6 +165,9 @@ build-conda: $(CONDA_FILE)
164165

165166
build: build-wheel build-sdist build-conda
166167

168+
unpacked-wheel: $(WHEEL_FILE)
169+
$(CONDA_RUN) wheel unpack $(WHEEL_FILE) -d dist
170+
167171
site/index.html: $(MKDOC_FILES) $(SRC_FILES)
168172
$(CONDA_RUN) mkdocs build -f $(MKDOC_CONFIG)
169173

docs/install.md

+9
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,12 @@ pip install mkdocstrings-python-xref
88
```
99
conda install -c conda-forge mkdocstrings-python-xref
1010
```
11+
12+
## A note on dependencies
13+
14+
This project is implemented by directly extending the [mkdocstrings-python]
15+
project, so you may want to specify a specific upper bound on the version
16+
of that project in your project's dependency file to guard against any
17+
surprises in case of a breaking change in the future.
18+
19+
[mkdocstrings-python]: https://mkdocstrings.github.io/python/

pyproject.toml

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,20 @@ classifiers = [
1313
"Development Status :: 3 - Alpha",
1414
"Intended Audience :: Developers",
1515
"Topic :: Software Development :: Documentation",
16-
"Programming Language :: Python :: 3.8",
1716
"Programming Language :: Python :: 3.9",
1817
"Programming Language :: Python :: 3.10",
1918
"Programming Language :: Python :: 3.11",
2019
"Programming Language :: Python :: 3.12",
20+
"Programming Language :: Python :: 3.13",
2121
]
2222
keywords = [
2323
"documentation-tool", "mkdocstrings", "mkdocstrings-handler", "python"
2424
]
2525
dynamic = ["version"]
26-
requires-python = ">=3.8"
26+
requires-python = ">=3.9"
2727
dependencies = [
28-
"mkdocstrings-python >=1.6.2,<2.0"
28+
"mkdocstrings-python >=1.14,<2.0",
29+
"griffe >=1.0"
2930
]
3031

3132
[project.urls]
@@ -46,13 +47,13 @@ include = [
4647
[tool.hatch.build.targets.sdist]
4748
packages = [
4849
"src/mkdocstrings_handlers",
49-
"src/mkdocstrings_handlers/python_xref",
50+
# "src/mkdocstrings_handlers/python_xref",
5051
]
5152

5253
[tool.hatch.build.targets.wheel]
5354
packages = [
5455
"src/mkdocstrings_handlers",
55-
"src/mkdocstrings_handlers/python_xref",
56+
# "src/mkdocstrings_handlers/python_xref",
5657
]
5758

5859
[tool.mypy]
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.6.1
1+
1.14.0

src/mkdocstrings_handlers/python_xref/__init__.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2022-2023. Analog Devices Inc.
1+
# Copyright (c) 2022-2025. Analog Devices Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -15,8 +15,7 @@
1515
Extended mkdocstrings python handler
1616
"""
1717

18-
from .handler import PythonRelXRefHandler
18+
from .handler import get_handler
1919

2020
__all__ = ["get_handler"]
2121

22-
get_handler = PythonRelXRefHandler

src/mkdocstrings_handlers/python_xref/crossref.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2022-2024. Analog Devices Inc.
1+
# Copyright (c) 2022-2025. Analog Devices Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
1818
import re
1919
from typing import Callable, List, Optional, cast
2020

21-
from griffe.dataclasses import Docstring, Object
21+
from griffe import Docstring, Object
2222
from mkdocstrings.loggers import get_logger
2323

2424
__all__ = [
+100-36
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2022=2023. Analog Devices Inc.
1+
# Copyright (c) 2022-2025. Analog Devices Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -17,12 +17,17 @@
1717

1818
from __future__ import annotations
1919

20-
from collections import ChainMap
20+
import sys
21+
from dataclasses import dataclass, fields
2122
from pathlib import Path
22-
from typing import Any, List, Mapping, Optional
23+
from textwrap import dedent
24+
from typing import Annotated, Any, ClassVar, Mapping, MutableMapping, Optional
25+
from warnings import warn
2326

24-
from griffe.dataclasses import Object
27+
from mkdocs.config.defaults import MkDocsConfig
28+
from mkdocstrings.handlers.base import CollectorItem
2529
from mkdocstrings.loggers import get_logger
30+
from mkdocstrings_handlers.python.config import PythonOptions, Field, PythonConfig
2631
from mkdocstrings_handlers.python.handler import PythonHandler
2732

2833
from .crossref import substitute_relative_crossrefs
@@ -33,63 +38,122 @@
3338

3439
logger = get_logger(__name__)
3540

41+
42+
# TODO mkdocstrings 0.28
43+
# - `name` and `domain` (py) must be specified as class attributes
44+
# - `handler` arg to superclass is deprecated
45+
# - add `mdx` arg to constructor to pass on to superclass
46+
# - `config_file_path` arg will no longer be passed
47+
#
48+
49+
# TODO python 3.9 - remove when 3.9 support is dropped
50+
_dataclass_options = {"frozen": True}
51+
if sys.version_info >= (3, 10):
52+
_dataclass_options["kw_only"] = True
53+
54+
@dataclass(**_dataclass_options)
55+
class PythonRelXRefOptions(PythonOptions):
56+
check_crossrefs: Annotated[
57+
bool,
58+
Field(
59+
group="docstrings",
60+
parent="docstring_options",
61+
description=dedent(
62+
"""
63+
Enables early checking of all cross-references.
64+
65+
Note that this option only takes affect if **relative_crossrefs** is
66+
also true. This option is true by default, so this option is used to
67+
disable checking. Checking can also be disabled on a per-case basis by
68+
prefixing the reference with '?', e.g. `[something][?dontcheckme]`.
69+
"""
70+
),
71+
),
72+
] = True
73+
3674
class PythonRelXRefHandler(PythonHandler):
3775
"""Extended version of mkdocstrings Python handler
3876
3977
* Converts relative cross-references into full references
4078
* Checks cross-references early in order to produce errors with source location
4179
"""
4280

43-
handler_name: str = __name__.rsplit('.', 2)[1]
44-
45-
default_config = dict(
46-
PythonHandler.default_config,
47-
relative_crossrefs = False,
48-
check_crossrefs = True,
49-
)
50-
51-
def __init__(self,
52-
theme: str,
53-
custom_templates: Optional[str] = None,
54-
config_file_path: Optional[str] = None,
55-
paths: Optional[List[str]] = None,
56-
locale: str = "en",
57-
**_config: Any,
58-
):
59-
super().__init__(
60-
handler = self.handler_name,
61-
theme = theme,
62-
custom_templates = custom_templates,
63-
config_file_path = config_file_path,
64-
paths = paths,
65-
locale=locale,
81+
name: ClassVar[str] = "python_xref"
82+
"""Override the handler name"""
83+
84+
def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None:
85+
"""Initialize the handler.
86+
87+
Parameters:
88+
config: The handler configuration.
89+
base_dir: The base directory of the project.
90+
**kwargs: Arguments passed to the parent constructor.
91+
"""
92+
check_crossrefs = config.options.pop('check_crossrefs', None) # Remove
93+
super().__init__(config, base_dir, **kwargs)
94+
if check_crossrefs is not None:
95+
self.global_options["check_crossrefs"] = check_crossrefs
96+
97+
def get_options(self, local_options: Mapping[str, Any]) -> PythonRelXRefOptions:
98+
local_options = dict(local_options)
99+
check_crossrefs = local_options.pop('check_crossrefs', None)
100+
_opts = super().get_options(local_options)
101+
opts = PythonRelXRefOptions(
102+
**{field.name: getattr(_opts, field.name) for field in fields(_opts)}
66103
)
67-
68-
def render(self, data: Object, config: Mapping[str,Any]) -> str:
69-
final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]
70-
71-
if final_config["relative_crossrefs"]:
72-
checkref = self._check_ref if final_config["check_crossrefs"] else None
104+
if check_crossrefs is not None:
105+
opts.check_crossrefs = bool(check_crossrefs)
106+
return opts
107+
108+
def render(self, data: CollectorItem, options: PythonOptions) -> str:
109+
if options.relative_crossrefs:
110+
if isinstance(options, PythonRelXRefOptions):
111+
checkref = self._check_ref if options.check_crossrefs else None
112+
else:
113+
checkref = None
73114
substitute_relative_crossrefs(data, checkref=checkref)
74115

75116
try:
76-
return super().render(data, config)
117+
return super().render(data, options)
77118
except Exception: # pragma: no cover
78119
print(f"{data.path=}")
79120
raise
80121

81122
def get_templates_dir(self, handler: Optional[str] = None) -> Path:
82123
"""See [render][.barf]"""
83-
if handler == self.handler_name:
124+
if handler == self.name:
84125
handler = 'python'
85126
return super().get_templates_dir(handler)
86127

87128
def _check_ref(self, ref:str) -> bool:
88129
"""Check for existence of reference"""
89130
try:
90-
self.collect(ref, {})
131+
self.collect(ref, PythonOptions())
91132
return True
92133
except Exception: # pylint: disable=broad-except
93134
# Only expect a CollectionError but we may as well catch everything.
94135
return False
95136

137+
def get_handler(
138+
handler_config: MutableMapping[str, Any],
139+
tool_config: MkDocsConfig,
140+
**kwargs: Any,
141+
) -> PythonHandler:
142+
"""Simply return an instance of `PythonRelXRefHandler`.
143+
144+
Arguments:
145+
handler_config: The handler configuration.
146+
tool_config: The tool (SSG) configuration.
147+
148+
Returns:
149+
An instance of `PythonRelXRefHandler`.
150+
"""
151+
base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent
152+
if "inventories" not in handler_config and "import" in handler_config:
153+
warn("The 'import' key is renamed 'inventories' for the Python handler", FutureWarning, stacklevel=1)
154+
handler_config["inventories"] = handler_config.pop("import", [])
155+
return PythonRelXRefHandler(
156+
config=PythonConfig.from_data(**handler_config),
157+
base_dir=base_dir,
158+
**kwargs,
159+
)

tests/test_crossref.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2022-2024. Analog Devices Inc.
1+
# Copyright (c) 2022-2025. Analog Devices Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
2222
from typing import Callable, Optional
2323

2424
import pytest
25-
from griffe.dataclasses import Class, Docstring, Function, Module, Object
25+
from griffe import Class, Docstring, Function, Module, Object
2626

2727
# noinspection PyProtectedMember
2828
from mkdocstrings_handlers.python_xref.crossref import (

0 commit comments

Comments
 (0)