Skip to content

Commit 5035e92

Browse files
waylanpawamoy
andauthored
fix: Make extension paths relative to config file
PR mkdocstrings#112: mkdocstrings#112 Co-authored-by: Timothée Mazzucotelli <pawamoy@pm.me>
1 parent f747ecb commit 5035e92

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

src/mkdocstrings_handlers/python/handler.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import sys
1010
from collections import ChainMap
1111
from contextlib import suppress
12-
from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, Iterator, Mapping
12+
from typing import TYPE_CHECKING, Any, BinaryIO, ClassVar, Iterator, Mapping, Sequence
1313

1414
from griffe.collections import LinesCollection, ModulesCollection
1515
from griffe.docstrings.parsers import Parser
@@ -265,8 +265,9 @@ def collect(self, identifier: str, config: Mapping[str, Any]) -> CollectorItem:
265265
parser = parser_name and Parser(parser_name)
266266

267267
if unknown_module:
268+
extensions = self.normalize_extension_paths(final_config.get("extensions", []))
268269
loader = GriffeLoader(
269-
extensions=load_extensions(final_config.get("extensions", [])),
270+
extensions=load_extensions(extensions),
270271
search_paths=self._paths,
271272
docstring_parser=parser,
272273
docstring_options=parser_options,
@@ -369,6 +370,35 @@ def get_anchors(self, data: CollectorItem) -> tuple[str, ...]: # noqa: D102 (ig
369370
return tuple(anchors)
370371
return tuple(anchors)
371372

373+
def normalize_extension_paths(self, extensions: Sequence) -> Sequence:
374+
"""Resolve extension paths relative to config file."""
375+
if self._config_file_path is None:
376+
return extensions
377+
378+
base_path = os.path.dirname(self._config_file_path)
379+
normalized = []
380+
381+
for ext in extensions:
382+
if isinstance(ext, dict):
383+
pth, options = next(iter(ext.items()))
384+
pth = str(pth)
385+
else:
386+
pth = str(ext)
387+
options = None
388+
389+
if pth.endswith(".py") or ".py:" in pth or "/" in pth or "\\" in pth: # noqa: SIM102
390+
# This is a sytem path. Normalize it.
391+
if not os.path.isabs(pth):
392+
# Make path absolute relative to config file path.
393+
pth = os.path.normpath(os.path.join(base_path, pth))
394+
395+
if options is not None:
396+
normalized.append({pth: options})
397+
else:
398+
normalized.append(pth)
399+
400+
return normalized
401+
372402

373403
def get_handler(
374404
*,

tests/test_handler.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,43 @@ def test_expand_globs_without_changing_directory() -> None:
105105
)
106106
for path in list(glob(os.path.abspath(".") + "/*.md")):
107107
assert path in handler._paths
108+
109+
110+
@pytest.mark.parametrize(
111+
("expect_change", "extension"),
112+
[
113+
(True, "extension.py"),
114+
(True, "extension.py:SomeExtension"),
115+
(True, "path/to/extension.py"),
116+
(True, "path/to/extension.py:SomeExtension"),
117+
(True, {"extension.py": {"option": "value"}}),
118+
(True, {"extension.py:SomeExtension": {"option": "value"}}),
119+
(True, {"path/to/extension.py": {"option": "value"}}),
120+
(True, {"path/to/extension.py:SomeExtension": {"option": "value"}}),
121+
(False, "/absolute/path/to/extension.py"),
122+
(False, "/absolute/path/to/extension.py:SomeExtension"),
123+
(False, {"/absolute/path/to/extension.py": {"option": "value"}}),
124+
(False, {"/absolute/path/to/extension.py:SomeExtension": {"option": "value"}}),
125+
(False, "dot.notation.path.to.extension"),
126+
(False, "dot.notation.path.to.pyextension"),
127+
(False, {"dot.notation.path.to.extension": {"option": "value"}}),
128+
(False, {"dot.notation.path.to.pyextension": {"option": "value"}}),
129+
],
130+
)
131+
def test_extension_paths(tmp_path: Path, expect_change: bool, extension: str | dict) -> None:
132+
"""Assert extension paths are resolved relative to config file."""
133+
handler = get_handler(
134+
theme="material",
135+
config_file_path=str(tmp_path.joinpath("mkdocs.yml")),
136+
)
137+
normalized = handler.normalize_extension_paths([extension])[0]
138+
if expect_change:
139+
if isinstance(normalized, str) and isinstance(extension, str):
140+
assert normalized == str(tmp_path.joinpath(extension))
141+
elif isinstance(normalized, dict) and isinstance(extension, dict):
142+
pth, options = next(iter(extension.items()))
143+
assert normalized == {str(tmp_path.joinpath(pth)): options}
144+
else:
145+
raise ValueError("Normalization must not change extension items type")
146+
else:
147+
assert normalized == extension

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy