Skip to content

Commit

Permalink
Merge pull request #30 from analog-garage/compatibility-fixesa
Browse files Browse the repository at this point in the history
Fixes for mkdocstrings-python 1.14 changes
  • Loading branch information
analog-cbarber authored Feb 10, 2025
2 parents b0f9e78 + f8f3d59 commit e0bce3b
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 77 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.8", "3.10", "3.12"]
python-version: ["3.9", "3.11", "3.13"]
steps:
- uses: actions/checkout@v3
- uses: goanpeca/setup-miniconda@v2.2.0
- uses: conda-incubator/setup-miniconda@v3.0.4
with:
miniforge-version: latest
conda-version: ">=23.7.4"
conda-build-version: ">=3.26"
conda-version: ">=24.11"
conda-build-version: ">=25.1"
environment-file: environment.yml
activate-environment: mkxref-dev
python-version: ${{ matrix.python-version }}
condarc-file: github-condarc.yml
auto-activate-base: true
use-mamba: true
use-mamba: false
- name: Dev install package
run: |
conda run -n mkxref-dev pip install -e . --no-deps --no-build-isolation
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# mkdocstring-python-xref changes

## 1.14.0

* Work with mkdocstrings-python 1.14 or later
* Drop python 3.8 support
* Fix extra files in wheel's RECORD

## 1.6.2

* Use griffe 1.0 or later
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ help:
@$(ECHO) "build - Build wheel"
@$(ECHO) "build-wheel - Build wheel."
@$(ECHO) "build-conda - Build conda package (requires whl2conda)"
@$(ECHO) "unpacked-wheel - Build wheel and unpack it (for debugging)"
@$(ECHO)
@$(ECHO) "$(SECTION_COLOR)--- upload ---$(COLORLESS)"
@$(ECHO) "upload - Upload wheel to pypi (requires authorization)"
Expand Down Expand Up @@ -164,6 +165,9 @@ build-conda: $(CONDA_FILE)

build: build-wheel build-sdist build-conda

unpacked-wheel: $(WHEEL_FILE)
$(CONDA_RUN) wheel unpack $(WHEEL_FILE) -d dist

site/index.html: $(MKDOC_FILES) $(SRC_FILES)
$(CONDA_RUN) mkdocs build -f $(MKDOC_CONFIG)

Expand Down
9 changes: 9 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ pip install mkdocstrings-python-xref
```
conda install -c conda-forge mkdocstrings-python-xref
```

## A note on dependencies

This project is implemented by directly extending the [mkdocstrings-python]
project, so you may want to specify a specific upper bound on the version
of that project in your project's dependency file to guard against any
surprises in case of a breaking change in the future.

[mkdocstrings-python]: https://mkdocstrings.github.io/python/
4 changes: 2 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ channels:

dependencies:
# runtime
- python >=3.8,<3.13
- mkdocstrings-python >=1.6.2,<2.0
- python >=3.9,<3.14
- mkdocstrings-python >=1.14,<2.0
- griffe >=1.0
# build
- python-build >=1.0.0
Expand Down
10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Topic :: Software Development :: Documentation",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
keywords = [
"documentation-tool", "mkdocstrings", "mkdocstrings-handler", "python"
]
dynamic = ["version"]
requires-python = ">=3.8"
requires-python = ">=3.9"
dependencies = [
"mkdocstrings-python >=1.6.2,<2.0",
"mkdocstrings-python >=1.14,<2.0",
"griffe >=1.0"
]

Expand All @@ -47,13 +47,13 @@ include = [
[tool.hatch.build.targets.sdist]
packages = [
"src/mkdocstrings_handlers",
"src/mkdocstrings_handlers/python_xref",
# "src/mkdocstrings_handlers/python_xref",
]

[tool.hatch.build.targets.wheel]
packages = [
"src/mkdocstrings_handlers",
"src/mkdocstrings_handlers/python_xref",
# "src/mkdocstrings_handlers/python_xref",
]

[tool.mypy]
Expand Down
2 changes: 1 addition & 1 deletion src/mkdocstrings_handlers/python_xref/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.6.2
1.14.0
5 changes: 2 additions & 3 deletions src/mkdocstrings_handlers/python_xref/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022-2023. Analog Devices Inc.
# Copyright (c) 2022-2025. Analog Devices Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -15,8 +15,7 @@
Extended mkdocstrings python handler
"""

from .handler import PythonRelXRefHandler
from .handler import get_handler

__all__ = ["get_handler"]

get_handler = PythonRelXRefHandler
2 changes: 1 addition & 1 deletion src/mkdocstrings_handlers/python_xref/crossref.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022-2024. Analog Devices Inc.
# Copyright (c) 2022-2025. Analog Devices Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
136 changes: 100 additions & 36 deletions src/mkdocstrings_handlers/python_xref/handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022=2023. Analog Devices Inc.
# Copyright (c) 2022-2025. Analog Devices Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -17,12 +17,17 @@

from __future__ import annotations

from collections import ChainMap
import sys
from dataclasses import dataclass, fields
from pathlib import Path
from typing import Any, List, Mapping, Optional
from textwrap import dedent
from typing import Annotated, Any, ClassVar, Mapping, MutableMapping, Optional
from warnings import warn

from griffe import Object
from mkdocs.config.defaults import MkDocsConfig
from mkdocstrings.handlers.base import CollectorItem
from mkdocstrings.loggers import get_logger
from mkdocstrings_handlers.python.config import PythonOptions, Field, PythonConfig
from mkdocstrings_handlers.python.handler import PythonHandler

from .crossref import substitute_relative_crossrefs
Expand All @@ -33,63 +38,122 @@

logger = get_logger(__name__)


# TODO mkdocstrings 0.28
# - `name` and `domain` (py) must be specified as class attributes
# - `handler` arg to superclass is deprecated
# - add `mdx` arg to constructor to pass on to superclass
# - `config_file_path` arg will no longer be passed
#

# TODO python 3.9 - remove when 3.9 support is dropped
_dataclass_options = {"frozen": True}
if sys.version_info >= (3, 10):
_dataclass_options["kw_only"] = True

@dataclass(**_dataclass_options)
class PythonRelXRefOptions(PythonOptions):
check_crossrefs: Annotated[
bool,
Field(
group="docstrings",
parent="docstring_options",
description=dedent(
"""
Enables early checking of all cross-references.
Note that this option only takes affect if **relative_crossrefs** is
also true. This option is true by default, so this option is used to
disable checking. Checking can also be disabled on a per-case basis by
prefixing the reference with '?', e.g. `[something][?dontcheckme]`.
"""
),
),
] = True

class PythonRelXRefHandler(PythonHandler):
"""Extended version of mkdocstrings Python handler
* Converts relative cross-references into full references
* Checks cross-references early in order to produce errors with source location
"""

handler_name: str = __name__.rsplit('.', 2)[1]

default_config = dict(
PythonHandler.default_config,
relative_crossrefs = False,
check_crossrefs = True,
)

def __init__(self,
theme: str,
custom_templates: Optional[str] = None,
config_file_path: Optional[str] = None,
paths: Optional[List[str]] = None,
locale: str = "en",
**_config: Any,
):
super().__init__(
handler = self.handler_name,
theme = theme,
custom_templates = custom_templates,
config_file_path = config_file_path,
paths = paths,
locale=locale,
name: ClassVar[str] = "python_xref"
"""Override the handler name"""

def __init__(self, config: PythonConfig, base_dir: Path, **kwargs: Any) -> None:
"""Initialize the handler.
Parameters:
config: The handler configuration.
base_dir: The base directory of the project.
**kwargs: Arguments passed to the parent constructor.
"""
check_crossrefs = config.options.pop('check_crossrefs', None) # Remove
super().__init__(config, base_dir, **kwargs)
if check_crossrefs is not None:
self.global_options["check_crossrefs"] = check_crossrefs

def get_options(self, local_options: Mapping[str, Any]) -> PythonRelXRefOptions:
local_options = dict(local_options)
check_crossrefs = local_options.pop('check_crossrefs', None)
_opts = super().get_options(local_options)
opts = PythonRelXRefOptions(
**{field.name: getattr(_opts, field.name) for field in fields(_opts)}
)

def render(self, data: Object, config: Mapping[str,Any]) -> str:
final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]

if final_config["relative_crossrefs"]:
checkref = self._check_ref if final_config["check_crossrefs"] else None
if check_crossrefs is not None:
opts.check_crossrefs = bool(check_crossrefs)
return opts

def render(self, data: CollectorItem, options: PythonOptions) -> str:
if options.relative_crossrefs:
if isinstance(options, PythonRelXRefOptions):
checkref = self._check_ref if options.check_crossrefs else None
else:
checkref = None
substitute_relative_crossrefs(data, checkref=checkref)

try:
return super().render(data, config)
return super().render(data, options)
except Exception: # pragma: no cover
print(f"{data.path=}")
raise

def get_templates_dir(self, handler: Optional[str] = None) -> Path:
"""See [render][.barf]"""
if handler == self.handler_name:
if handler == self.name:
handler = 'python'
return super().get_templates_dir(handler)

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

def get_handler(
handler_config: MutableMapping[str, Any],
tool_config: MkDocsConfig,
**kwargs: Any,
) -> PythonHandler:
"""Simply return an instance of `PythonRelXRefHandler`.
Arguments:
handler_config: The handler configuration.
tool_config: The tool (SSG) configuration.
Returns:
An instance of `PythonRelXRefHandler`.
"""
base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent
if "inventories" not in handler_config and "import" in handler_config:
warn("The 'import' key is renamed 'inventories' for the Python handler", FutureWarning, stacklevel=1)
handler_config["inventories"] = handler_config.pop("import", [])
return PythonRelXRefHandler(
config=PythonConfig.from_data(**handler_config),
base_dir=base_dir,
**kwargs,
)
2 changes: 1 addition & 1 deletion tests/test_crossref.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022-2024. Analog Devices Inc.
# Copyright (c) 2022-2025. Analog Devices Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
Loading

0 comments on commit e0bce3b

Please sign in to comment.