Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: resolve external alias members via loading #300

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions quartodoc/builder/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from griffe.loader import GriffeLoader
from griffe.collections import ModulesCollection, LinesCollection
from griffe.docstrings.parsers import Parser
from griffe.exceptions import AliasResolutionError
from functools import partial
from textwrap import indent

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


def _resolve_alias(obj: dc.Alias | dc.Object, get_object):
if not isinstance(obj, dc.Alias):
return obj

# attempt to resolve alias, loading external modules when needed ----
max_tries = 100

new_obj = obj
for ii in range(max_tries):
if not new_obj.is_alias:
break

try:
new_obj = new_obj.target
except AliasResolutionError as e:
new_obj = get_object(e.alias.target_path)

return new_obj


class BlueprintTransformer(PydanticTransformer):
def __init__(self, get_object=None, parser="numpy"):
if get_object is None:
Expand Down Expand Up @@ -296,8 +317,9 @@ def enter(self, el: Auto):

# do no document submodules
if (
_is_external_alias(doc.obj, obj.package)
or doc.obj.kind.value == "module"
# _is_external_alias(doc.obj, obj.package)
doc.obj.kind.value
== "module"
):
continue

Expand Down Expand Up @@ -332,13 +354,19 @@ def enter(self, el: Auto):
signature_name=el.signature_name,
)

@staticmethod
def _fetch_members(el: Auto, obj: dc.Object | dc.Alias):
def _fetch_members(self, el: Auto, obj: dc.Object | dc.Alias):
# Note that this could be a static method, if we passed in the griffe loader

if el.members is not None:
return el.members

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

# use the __all__ attribute of modules to filter members
# otherwise, all members are included in the initial options
if obj.is_module and obj.exports is not None:
options = {k: v for k, v in options.items() if v.is_exported}

if el.include:
raise NotImplementedError("include argument currently unsupported.")

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

if not el.include_imports and not el.include_inherited:
if not (el.include_imports or el.include_inherited):
options = {k: v for k, v in options.items() if not v.is_alias}

# resolve any remaining aliases ----
# the reamining filters require attributes on the target object.
for obj in options.values():
_resolve_alias(obj, self.get_object)

if not el.include_empty:
options = {k: v for k, v in options.items() if v.docstring is not None}

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

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

return sorted(options)


Expand Down
1 change: 1 addition & 0 deletions quartodoc/tests/example_alias_target.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from quartodoc.tests.example_alias_target__nested import ( # noqa: F401
nested_alias_target,
tabulate as external_alias,
)


Expand Down
2 changes: 2 additions & 0 deletions quartodoc/tests/example_alias_target__nested.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
This function gets imported in example_alias_target, and from there imported into example.
"""

from tabulate import tabulate # noqa: F401


def nested_alias_target():
"""A nested alias target"""
15 changes: 15 additions & 0 deletions quartodoc/tests/test_builder_blueprint.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
from functools import partial
from griffe.exceptions import AliasResolutionError
from quartodoc import get_object
from quartodoc import layout as lo
from quartodoc.builder.blueprint import (
_non_default_entries,
_resolve_alias,
BlueprintTransformer,
blueprint,
WorkaroundKeyError,
)
import pytest


TEST_MOD = "quartodoc.tests.example"


Expand Down Expand Up @@ -38,6 +42,17 @@ def bp():
return BlueprintTransformer()


def test_func_resolve_alias():
obj = get_object("quartodoc.tests.example_alias_target.external_alias")
assert obj.is_alias
with pytest.raises(AliasResolutionError):
obj.target

resolved = _resolve_alias(obj, get_object)

assert resolved.path == "tabulate.tabulate"


def test_non_default_entries_auto():
assert _non_default_entries(lo.Auto(name="a_func", include_attributes=False)) == {
"name": "a_func",
Expand Down
Loading