Skip to content

bug: attempting to load external non-module alias triggers .resolve_target() #283

@machow

Description

@machow

Description of the bug

It looks like when loading a non-module alias, the on_package_load event triggers on it, causing an extension (I think?) to run .resolve_target().

To Reproduce

Create two files, demo.py, and demo2.py. I'll use the package tabulate here as the external package in this demo (edit: actually tabulate wasn't needed, since it couldn't resolve demo, but same idea 😅 ).

demo.py

# importing the tabulate function from tabulate
from tabulate import tabulate

demo2.py

from demo import tabulate as alias

Then run the loader:

from griffe.loader import GriffeLoader

loader = GriffeLoader()
loader.load("demo2.alias")

Full traceback

Full traceback
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1373, in Alias._resolve_target(self)
   1372 try:
-> 1373     resolved = self.modules_collection.get_member(self.target_path)
   1374 except KeyError as error:

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/mixins.py:78, in GetMembersMixin.get_member(self, key)
     77     return self.members[parts[0]]  # type: ignore[attr-defined]
---> 78 return self.members[parts[0]].get_member(parts[1:])

KeyError: 'tabulate'

The above exception was the direct cause of the following exception:

AliasResolutionError                      Traceback (most recent call last)
Cell In[8], line 1
----> 1 loader.load("demo2.alias")

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/loader.py:233, in GriffeLoader.load(self, objspec, submodules, try_relative_path, find_stubs_package, module)
    231 # Package is loaded, we now retrieve the initially requested object and return it.
    232 obj = self.modules_collection.get_member(obj_path)
--> 233 self.extensions.call("on_package_loaded", pkg=obj)
    234 return obj

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/extensions/base.py:345, in Extensions.call(self, event, **kwargs)
    338 """Call the extension hook for the given event.
    339 
    340 Parameters:
    341     event: The trigerred event.
    342     **kwargs: Arguments passed to the hook.
    343 """
    344 for extension in self._extensions:
--> 345     getattr(extension, event)(**kwargs)

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/extensions/dataclasses.py:226, in DataclassesExtension.on_package_loaded(self, pkg)
    220 def on_package_loaded(self, *, pkg: Module) -> None:
    221     """Hook for loaded packages.
    222 
    223     Parameters:
    224         pkg: The loaded package.
    225     """
--> 226     _apply_recursively(pkg, set())

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/extensions/dataclasses.py:197, in _apply_recursively(mod_cls, processed)
    196 def _apply_recursively(mod_cls: Module | Class, processed: set[str]) -> None:
--> 197     if mod_cls.canonical_path in processed:
    198         return
    199     processed.add(mod_cls.canonical_path)

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1138, in Alias.canonical_path(self)
   1132 @property
   1133 def canonical_path(self) -> str:
   1134     """The full dotted path of this object.
   1135 
   1136     The canonical path is the path where the object was defined (not imported).
   1137     """
-> 1138     return self.final_target.canonical_path

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1339, in Alias.final_target(self)
   1337         raise CyclicAliasError([*paths_seen, target.path])
   1338     paths_seen[target.path] = None
-> 1339     target = target.target  # type: ignore[assignment]
   1340 return target

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1308, in Alias.target(self)
   1302 """The resolved target (actual object), if possible.
   1303 
   1304 Upon accessing this property, if the target is not already resolved,
   1305 a lookup is done using the modules collection to find the target.
   1306 """
   1307 if not self.resolved:
-> 1308     self.resolve_target()
   1309 return self._target

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1367, in Alias.resolve_target(self)
   1365 self._passed_through = True
   1366 try:
-> 1367     self._resolve_target()
   1368 finally:
   1369     self._passed_through = False

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1380, in Alias._resolve_target(self)
   1378 if resolved.is_alias and not resolved.resolved:
   1379     try:
-> 1380         resolved.resolve_target()
   1381     except CyclicAliasError as error:
   1382         raise CyclicAliasError([self.target_path, *error.chain]) from error

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1367, in Alias.resolve_target(self)
   1365 self._passed_through = True
   1366 try:
-> 1367     self._resolve_target()
   1368 finally:
   1369     self._passed_through = False

File ~/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py:1375, in Alias._resolve_target(self)
   1373     resolved = self.modules_collection.get_member(self.target_path)
   1374 except KeyError as error:
-> 1375     raise AliasResolutionError(self) from error
   1376 if resolved is self:
   1377     raise CyclicAliasError([self.target_path])

AliasResolutionError: Could not resolve alias demo.tabulate pointing at tabulate.tabulate (in demo.py:1)
$ griffe dump ... -LDEBUG
griffe dump demo2.alias -LDEBUG
INFO       Loading package demo2.alias
DEBUG      Searching path(s) for demo2.alias
DEBUG      Found demo2.alias: loading
DEBUG      Loading path /Users/machow/repos/quartodoc/demo2.py
Traceback (most recent call last):
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py", line 1373, in _resolve_target
    resolved = self.modules_collection.get_member(self.target_path)
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/mixins.py", line 78, in get_member
    return self.members[parts[0]].get_member(parts[1:])  # type: ignore[attr-defined]
KeyError: 'demo'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/machow/.virtualenvs/quartodoc/bin/griffe", line 8, in <module>
    sys.exit(main())
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/cli.py", line 555, in main
    return commands[subcommand](**opts_dict)
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/cli.py", line 385, in dump
    loader = _load_packages(
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/cli.py", line 97, in _load_packages
    loader.load(package, try_relative_path=True, find_stubs_package=find_stubs_package)
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/loader.py", line 233, in load
    self.extensions.call("on_package_loaded", pkg=obj)
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/extensions/base.py", line 345, in call
    getattr(extension, event)(**kwargs)
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/extensions/dataclasses.py", line 226, in on_package_loaded
    _apply_recursively(pkg, set())
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/extensions/dataclasses.py", line 197, in _apply_recursively
    if mod_cls.canonical_path in processed:
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py", line 1138, in canonical_path
    return self.final_target.canonical_path
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py", line 1339, in final_target
    target = target.target  # type: ignore[assignment]
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py", line 1308, in target
    self.resolve_target()
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py", line 1367, in resolve_target
    self._resolve_target()
  File "/Users/machow/.virtualenvs/quartodoc/lib/python3.10/site-packages/griffe/dataclasses.py", line 1375, in _resolve_target
    raise AliasResolutionError(self) from error
griffe.exceptions.AliasResolutionError: Could not resolve alias demo2.alias pointing at demo.tabulate (in demo2.py:1)
$ mkdocs build -v
PASTE LOGS HERE

Expected behavior

An Alias object is returned, even when tabulate is not loaded.

Environment information

griffe --debug-info  # | xclip -selection clipboard
- __System__: macOS-14.4.1-arm64-arm-64bit
- __Python__: cpython 3.10.2 (/Users/machow/.virtualenvs/quartodoc/bin/python)
- __Environment variables__:
- __Installed packages__:
  - `griffe` v0.45.2

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions