Skip to content

Commit

Permalink
refactor: Resolve alias chain recursively
Browse files Browse the repository at this point in the history
Previously we were resolving only the direct target
of an alias in the `resolve_target()` method. This
proved to be insufficient as we were later iterating
through the whole chain anyway. We also had a method
that checked if the alias was resolved, and hackishly
also checked if the target was resolved.

We now invert these two methods behaviors:

- `resolve_target()` tries to resolve the whole chain
    (done!) and fails at the first resolution error.
    All resolved parents are marked unresolved
    (we don't allow partial resolution).
    A resolution error raises a `CyclicAliasError`
    that can be caught and handled above in the stack.
    This effectively helps detecting cyclic aliases
    and avoid maximum recursion errors.
- the `resolved` property then only checks if the
    current target (and only the current target)
    is resolved.
  • Loading branch information
pawamoy committed Dec 4, 2022
1 parent 86454ec commit 6cdd3b2
Showing 1 changed file with 12 additions and 2 deletions.
14 changes: 12 additions & 2 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@ def target(self, value: Object | Alias) -> None:
if self.parent is not None:
self._target.aliases[self.path] = self

def resolve_target(self) -> None:
def resolve_target(self) -> None: # noqa: WPS231,WPS238
"""Resolve the target.
Raises:
Expand All @@ -1102,12 +1102,22 @@ def resolve_target(self) -> None:
and added to the collection.
CyclicAliasError: When the resolved target is the alias itself.
"""
if self._passed_through:
raise CyclicAliasError([self.target_path])
try:
resolved = self.modules_collection[self.target_path]
except KeyError as error:
raise AliasResolutionError(self.target_path) from error
if resolved is self:
raise CyclicAliasError([self.target_path])
if resolved.is_alias and not resolved.resolved:
self._passed_through = True
try:
resolved.resolve_target()
except CyclicAliasError as error: # noqa: WPS440
raise CyclicAliasError([self.target_path] + error.chain)
finally:
self._passed_through = False
self._target = resolved
if self.parent is not None:
self._target.aliases[self.path] = self # type: ignore[union-attr] # we just set the target
Expand All @@ -1119,7 +1129,7 @@ def resolved(self) -> bool:
Returns:
True or False.
"""
return self._target is not None and (not self._target.is_alias or self._target.resolved) # type: ignore[union-attr]
return self._target is not None # type: ignore[union-attr]

@cached_property
def wildcard(self) -> str | None:
Expand Down

0 comments on commit 6cdd3b2

Please sign in to comment.