Skip to content

Commit 17a1e97

Browse files
Merge pull request #49 from analog-garage/bug-47
Bug 47
2 parents 90bebc6 + f6e3432 commit 17a1e97

File tree

10 files changed

+90
-9
lines changed

10 files changed

+90
-9
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
*Note that versions roughly correspond to the version of mkdocstrings-python that they
44
are compatible with.*
55

6+
## 1.16.4
7+
8+
* Fix handling of aliases (see bug #47)
9+
610
## 1.16.3
711

812
* Added `check_crossrefs_exclude` config option

pixi.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ dynamic = ["version"]
2626
requires-python = ">=3.9"
2727
dependencies = [
2828
"mkdocstrings-python >=1.16.6,<2.0",
29-
"griffe >=1.0"
29+
"griffe >=1.0",
3030
]
3131

3232
[project.urls]
@@ -47,7 +47,7 @@ dev = [
4747
"mike >=1.1",
4848
"mkdocs >=1.5.3,<2.0",
4949
"mkdocs-material >=9.5.4",
50-
"linkchecker >=10.4"
50+
"linkchecker >=10.4",
5151
]
5252

5353
[tool.pixi.workspace]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.16.3
1+
1.16.4

src/mkdocstrings_handlers/python_xref/crossref.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import sys
2121
from typing import Any, Callable, List, Optional, cast
2222

23-
from griffe import Docstring, Object
23+
from griffe import Alias, Docstring, GriffeError, Object
2424
from mkdocstrings import get_logger
2525

2626
__all__ = [
@@ -318,21 +318,32 @@ def _error(self, msg: str, just_warn: bool = False) -> None:
318318
self._ok = just_warn
319319

320320

321-
def substitute_relative_crossrefs(obj: Object, checkref: Optional[Callable[[str], bool]] = None) -> None:
321+
def substitute_relative_crossrefs(
322+
obj: Alias|Object,
323+
checkref: Optional[Callable[[str], bool]] = None,
324+
) -> None:
322325
"""Recursively expand relative cross-references in all docstrings in tree.
323326
324327
Arguments:
325328
obj: a Griffe [Object][griffe.] whose docstrings should be modified
326329
checkref: optional function to check whether computed cross-reference is valid.
327330
Should return True if valid, False if not valid.
328331
"""
332+
if isinstance(obj, Alias):
333+
try:
334+
obj = obj.target
335+
except GriffeError:
336+
# If alias could not be resolved, it probably refers
337+
# to an external package, not be documented.
338+
return
339+
329340
doc = obj.docstring
330341

331342
if doc is not None:
332343
doc.value = _RE_CROSSREF.sub(_RelativeCrossrefProcessor(doc, checkref=checkref), doc.value)
333344

334345
for member in obj.members.values():
335-
if isinstance(member, Object): # pragma: no branch
346+
if isinstance(member, (Alias,Object)): # pragma: no branch
336347
substitute_relative_crossrefs(member, checkref=checkref)
337348

338349
def doc_value_offset_to_location(doc: Docstring, offset: int) -> tuple[int,int]:

tests/project/src/myproj/bar.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@
2121
class Bar(Foo):
2222
"""See [bar][.] method."""
2323

24+
attribute: str = "attribute"
25+
"""
26+
See [`foo`][(c).]
27+
"""
28+
2429
def bar(self) -> None:
2530
"""This is in the [Bar][(c)] class.
2631
Also see the [foo][^.] method and the [func][(m).] function.

tests/project/src/myproj/pkg/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
A module
1616
"""
1717

18+
from .dataclass import Dataclass
19+
20+
__all__ = [
21+
"Dataclass",
22+
"func",
23+
]
24+
1825
def func() -> None:
1926
"""
2027
A function
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
Dataclass example
3+
"""
4+
5+
from dataclasses import dataclass, field
6+
7+
@dataclass
8+
class Dataclass:
9+
"""
10+
Test dataclasses
11+
12+
See [content][(c).] for an example attribute.
13+
14+
See [method][(c).]
15+
"""
16+
content: str = "hi"
17+
"""some content"""
18+
19+
duration: float = field(default_factory=lambda: 0.0)
20+
"""
21+
example: [`content`][(c).]
22+
"""
23+
24+
def method(self) -> None:
25+
"""Example method."""

tests/test_crossref.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from textwrap import dedent
2424
from typing import Callable, Optional
2525

26+
import griffe
2627
import pytest
2728
from griffe import Class, Docstring, Function, Module, Object, LinesCollection
2829

@@ -264,3 +265,18 @@ def test_doc_value_offset_to_location() -> None:
264265

265266
assert doc_value_offset_to_location(doc3, 0) == (2, 5)
266267
assert doc_value_offset_to_location(doc3, 6) == (3, 3)
268+
269+
def test_griffe() -> None:
270+
"""
271+
Test substitution on griffe rep of local project
272+
Returns:
273+
274+
"""
275+
this_dir = Path(__file__).parent
276+
test_src_dir = this_dir / "project" / "src"
277+
myproj = griffe.load(
278+
"myproj",
279+
search_paths = [ test_src_dir ],
280+
)
281+
substitute_relative_crossrefs(myproj)
282+
# TODO - grovel output

tests/test_integration.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def check_autorefs(autorefs: List[Any], cases: Dict[Tuple[str,str],str] ) -> Non
3636
Arguments:
3737
autorefs: list of autoref tags parsed from HTML
3838
cases: mapping from (<location>,<title>) to generated reference tag
39-
where <location? is the qualified name of the object whose doc string
39+
where <location> is the qualified name of the object whose doc string
4040
contains the cross-reference, and <title> is the text in the cross-reference.
4141
"""
4242
cases = cases.copy()
@@ -123,4 +123,17 @@ def test_integration(tmpdir: PathLike) -> None:
123123
}
124124
)
125125

126+
pkg_html = site_dir.joinpath('pkg', 'index.html').read_text()
127+
pkg_bs = bs4.BeautifulSoup(pkg_html, 'html.parser')
126128

129+
autorefs = pkg_bs.find_all('a', attrs={'class':'autorefs'})
130+
assert len(autorefs) >= 3
131+
132+
check_autorefs(
133+
autorefs,
134+
{
135+
('myproj.pkg.Dataclass', 'content') : '#myproj.pkg.Dataclass.content',
136+
('myproj.pkg.Dataclass', 'method') : '#myproj.pkg.Dataclass.method',
137+
('myproj.pkg.Dataclass.duration', 'content') : '#myproj.pkg.Dataclass.content',
138+
}
139+
)

0 commit comments

Comments
 (0)