Skip to content

Commit

Permalink
fix: Prevent cyclic aliases by not overwriting a module member with a…
Browse files Browse the repository at this point in the history
…n indirect alias to itself

Issue #122: #122
  • Loading branch information
pawamoy committed Apr 1, 2023
1 parent 48747b6 commit c188a95
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 1 deletion.
14 changes: 14 additions & 0 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,20 @@ def target(self, value: Object | Alias) -> None:
if self.parent is not None:
self._target.aliases[self.path] = self

@property
def final_target(self) -> Object:
"""Resolve and return the final target, if possible.
This will iterate through the targets until a non-alias object is found.
Returns:
The final target.
"""
target = self.target
while target.is_alias:
target = target.target # type: ignore[union-attr]
return target # type: ignore[return-value]

def resolve_target(self) -> None:
"""Resolve the target.
Expand Down
14 changes: 13 additions & 1 deletion src/griffe/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from __future__ import annotations

import sys
from contextlib import suppress
from datetime import datetime, timezone
from typing import TYPE_CHECKING, Any, Sequence, cast
from warnings import warn
Expand Down Expand Up @@ -296,13 +297,24 @@ def expand_wildcards(
old_lineno = getattr(old_member, "alias_lineno", old_member.lineno or 0)
overwrite = alias_lineno > old_lineno # type: ignore[operator]
if not self_alias and (not already_present or overwrite):
obj[new_member.name] = Alias(
alias = Alias(
new_member.name,
new_member,
lineno=alias_lineno,
endlineno=alias_endlineno,
parent=obj, # type: ignore[arg-type]
)
if already_present:
prev_member = obj[new_member.name]
with suppress(AliasResolutionError):
if prev_member.is_module:
if prev_member.is_alias:
prev_member = prev_member.final_target
if alias.final_target is prev_member:
# alias named after the module it targets:
# skip to avoid cyclic aliases
continue
obj[new_member.name] = alias

def resolve_module_aliases(
self,
Expand Down

0 comments on commit c188a95

Please sign in to comment.