Skip to content

Commit 0205d17

Browse files
authored
Merge pull request #300 from machow/fix-external-alias-members
fix: resolve external alias members via loading
2 parents a37b560 + 7cd7f05 commit 0205d17

File tree

4 files changed

+56
-11
lines changed

4 files changed

+56
-11
lines changed

quartodoc/builder/blueprint.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from griffe.loader import GriffeLoader
99
from griffe.collections import ModulesCollection, LinesCollection
1010
from griffe.docstrings.parsers import Parser
11+
from griffe.exceptions import AliasResolutionError
1112
from functools import partial
1213
from textwrap import indent
1314

@@ -120,6 +121,26 @@ def _non_default_entries(el: Auto):
120121
return {k: getattr(el, k) for k in el._fields_specified}
121122

122123

124+
def _resolve_alias(obj: dc.Alias | dc.Object, get_object):
125+
if not isinstance(obj, dc.Alias):
126+
return obj
127+
128+
# attempt to resolve alias, loading external modules when needed ----
129+
max_tries = 100
130+
131+
new_obj = obj
132+
for ii in range(max_tries):
133+
if not new_obj.is_alias:
134+
break
135+
136+
try:
137+
new_obj = new_obj.target
138+
except AliasResolutionError as e:
139+
new_obj = get_object(e.alias.target_path)
140+
141+
return new_obj
142+
143+
123144
class BlueprintTransformer(PydanticTransformer):
124145
def __init__(self, get_object=None, parser="numpy"):
125146
if get_object is None:
@@ -296,8 +317,9 @@ def enter(self, el: Auto):
296317

297318
# do no document submodules
298319
if (
299-
_is_external_alias(doc.obj, obj.package)
300-
or doc.obj.kind.value == "module"
320+
# _is_external_alias(doc.obj, obj.package)
321+
doc.obj.kind.value
322+
== "module"
301323
):
302324
continue
303325

@@ -332,13 +354,19 @@ def enter(self, el: Auto):
332354
signature_name=el.signature_name,
333355
)
334356

335-
@staticmethod
336-
def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):
357+
def _fetch_members(self, el: Auto, obj: dc.Object | dc.Alias):
358+
# Note that this could be a static method, if we passed in the griffe loader
359+
337360
if el.members is not None:
338361
return el.members
339362

340363
options = obj.all_members if el.include_inherited else obj.members
341364

365+
# use the __all__ attribute of modules to filter members
366+
# otherwise, all members are included in the initial options
367+
if obj.is_module and obj.exports is not None:
368+
options = {k: v for k, v in options.items() if v.is_exported}
369+
342370
if el.include:
343371
raise NotImplementedError("include argument currently unsupported.")
344372

@@ -348,9 +376,14 @@ def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):
348376
if not el.include_private:
349377
options = {k: v for k, v in options.items() if not k.startswith("_")}
350378

351-
if not el.include_imports and not el.include_inherited:
379+
if not (el.include_imports or el.include_inherited):
352380
options = {k: v for k, v in options.items() if not v.is_alias}
353381

382+
# resolve any remaining aliases ----
383+
# the reamining filters require attributes on the target object.
384+
for obj in options.values():
385+
_resolve_alias(obj, self.get_object)
386+
354387
if not el.include_empty:
355388
options = {k: v for k, v in options.items() if v.docstring is not None}
356389

@@ -363,12 +396,6 @@ def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):
363396
if not el.include_functions:
364397
options = {k: v for k, v in options.items() if not v.is_function}
365398

366-
# for modules, remove any Alias objects, since they were imported from
367-
# other places. Sphinx has a flag for this behavior, so may be good
368-
# to do something similar.
369-
# if obj.is_module:
370-
# options = {k: v for k, v in options.items() if not v.is_alias}
371-
372399
return sorted(options)
373400

374401

quartodoc/tests/example_alias_target.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from quartodoc.tests.example_alias_target__nested import ( # noqa: F401
22
nested_alias_target,
3+
tabulate as external_alias,
34
)
45

56

quartodoc/tests/example_alias_target__nested.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
This function gets imported in example_alias_target, and from there imported into example.
33
"""
44

5+
from tabulate import tabulate # noqa: F401
6+
57

68
def nested_alias_target():
79
"""A nested alias target"""

quartodoc/tests/test_builder_blueprint.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
from functools import partial
2+
from griffe.exceptions import AliasResolutionError
13
from quartodoc import get_object
24
from quartodoc import layout as lo
35
from quartodoc.builder.blueprint import (
46
_non_default_entries,
7+
_resolve_alias,
58
BlueprintTransformer,
69
blueprint,
710
WorkaroundKeyError,
811
)
912
import pytest
1013

14+
1115
TEST_MOD = "quartodoc.tests.example"
1216

1317

@@ -38,6 +42,17 @@ def bp():
3842
return BlueprintTransformer()
3943

4044

45+
def test_func_resolve_alias():
46+
obj = get_object("quartodoc.tests.example_alias_target.external_alias")
47+
assert obj.is_alias
48+
with pytest.raises(AliasResolutionError):
49+
obj.target
50+
51+
resolved = _resolve_alias(obj, get_object)
52+
53+
assert resolved.path == "tabulate.tabulate"
54+
55+
4156
def test_non_default_entries_auto():
4257
assert _non_default_entries(lo.Auto(name="a_func", include_attributes=False)) == {
4358
"name": "a_func",

0 commit comments

Comments
 (0)