Skip to content

Commit 4b38b6b

Browse files
committed
fix: Don't pass unsupported options to Griffe parsers
Issue-337: #337
1 parent 6ff2749 commit 4b38b6b

2 files changed

Lines changed: 99 additions & 2 deletions

File tree

src/mkdocstrings_handlers/python/_internal/handler.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
from __future__ import annotations
44

55
import glob
6+
import inspect
67
import os
78
import posixpath
89
import sys
910
from contextlib import suppress
1011
from dataclasses import asdict
1112
from pathlib import Path
12-
from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar
13+
from typing import TYPE_CHECKING, Any, BinaryIO, Callable, ClassVar
1314

1415
from griffe import (
1516
AliasResolutionError,
@@ -18,6 +19,10 @@
1819
ModulesCollection,
1920
Parser,
2021
load_extensions,
22+
parse_auto,
23+
parse_google,
24+
parse_numpy,
25+
parse_sphinx,
2126
patch_loggers,
2227
)
2328
from mkdocs.exceptions import PluginError
@@ -54,6 +59,34 @@ def chdir(path: str) -> Iterator[None]:
5459

5560
patch_loggers(get_logger)
5661

62+
_PARSER_FUNCTIONS: dict[Parser, Callable] = {
63+
Parser.auto: parse_auto,
64+
Parser.google: parse_google,
65+
Parser.numpy: parse_numpy,
66+
Parser.sphinx: parse_sphinx,
67+
}
68+
69+
70+
def _filter_parser_options(parser: Parser | None, options: dict[str, Any] | None) -> dict[str, Any] | None:
71+
"""Filter options unsupported by the selected Griffe parser."""
72+
if parser is None or options is None:
73+
return options
74+
75+
accepted_options = set(inspect.signature(_PARSER_FUNCTIONS[parser]).parameters) - {"docstring"}
76+
filtered_options = {}
77+
for name, value in options.items():
78+
if name in accepted_options:
79+
if parser is Parser.auto and name == "per_style_options":
80+
filtered_options[name] = {
81+
style: _filter_parser_options(Parser(style), style_options)
82+
for style, style_options in value.items()
83+
}
84+
else:
85+
filtered_options[name] = value
86+
else:
87+
_logger.warning(f"Ignoring unsupported {parser.value} docstring parser option: {name}")
88+
return filtered_options
89+
5790

5891
class PythonHandler(BaseHandler):
5992
"""The Python handler class."""
@@ -195,7 +228,10 @@ def collect(self, identifier: str, options: PythonOptions) -> CollectorItem:
195228

196229
parser_name = options.docstring_style
197230
parser = parser_name and Parser(parser_name)
198-
parser_options = options.docstring_options and asdict(options.docstring_options)
231+
parser_options = options.docstring_options
232+
if parser_options is not None:
233+
parser_options = asdict(parser_options)
234+
parser_options = _filter_parser_options(parser, parser_options)
199235

200236
if unknown_module:
201237
extensions = self.normalize_extension_paths(options.extensions)

tests/test_config.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Tests for configuration options."""
2+
3+
from __future__ import annotations
4+
5+
import inspect
6+
from dataclasses import asdict, fields
7+
from typing import TYPE_CHECKING, Any
8+
9+
import pytest
10+
from griffe import Parser, parse_google, parse_numpy, parse_sphinx
11+
12+
from mkdocstrings_handlers.python import AutoStyleOptions, GoogleStyleOptions, NumpyStyleOptions, SphinxStyleOptions
13+
from mkdocstrings_handlers.python._internal.handler import _filter_parser_options
14+
15+
if TYPE_CHECKING:
16+
from collections.abc import Callable
17+
18+
19+
@pytest.mark.parametrize(
20+
("options_class", "parser"),
21+
[
22+
(GoogleStyleOptions, parse_google),
23+
(NumpyStyleOptions, parse_numpy),
24+
(SphinxStyleOptions, parse_sphinx),
25+
],
26+
)
27+
def test_style_options_match_griffe_parser(options_class: type[Any], parser: Callable[..., object]) -> None:
28+
"""Ensure style options stay in sync with Griffe parser options."""
29+
option_names = {field.name for field in fields(options_class)}
30+
parser_parameters = inspect.signature(parser).parameters
31+
parser_option_names = set(parser_parameters) - {"docstring"}
32+
33+
assert parser_option_names <= option_names
34+
35+
36+
def test_filter_style_options(caplog: pytest.LogCaptureFixture) -> None:
37+
"""Ensure unsupported options are not passed to Griffe and are reported."""
38+
options = asdict(SphinxStyleOptions())
39+
40+
filtered_options = _filter_parser_options(Parser.sphinx, options)
41+
42+
assert filtered_options == {
43+
name: value for name, value in options.items() if name in inspect.signature(parse_sphinx).parameters
44+
}
45+
assert "warn_missing_types" in options
46+
for name in set(options) - set(filtered_options or {}):
47+
assert f"Ignoring unsupported sphinx docstring parser option: {name}" in caplog.text
48+
49+
50+
def test_filter_auto_style_options(caplog: pytest.LogCaptureFixture) -> None:
51+
"""Ensure unsupported options nested in auto style options are reported."""
52+
options = asdict(AutoStyleOptions())
53+
54+
filtered_options = _filter_parser_options(Parser.auto, options)
55+
56+
assert filtered_options is not None
57+
if "warn_missing_types" in inspect.signature(parse_sphinx).parameters:
58+
assert "warn_missing_types" in filtered_options["per_style_options"]["sphinx"]
59+
else:
60+
assert "warn_missing_types" not in filtered_options["per_style_options"]["sphinx"]
61+
assert "Ignoring unsupported sphinx docstring parser option: warn_missing_types" in caplog.text

0 commit comments

Comments
 (0)