Skip to content

Commit

Permalink
fix: Add missing proxies (methods/properties) to aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Oct 17, 2023
1 parent 6df330f commit 7320640
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 5 deletions.
51 changes: 46 additions & 5 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.name!r}, {self.lineno!r}, {self.endlineno!r})"

def __bool__(self) -> bool:
# Prevent using `__len__`.
return True

def __len__(self) -> int:
Expand Down Expand Up @@ -723,7 +724,7 @@ def as_dict(self, *, full: bool = False, **kwargs: Any) -> dict[str, Any]:
return base


class Alias(ObjectAliasMixin):
class Alias(ObjectAliasMixin, SerializationMixin):
"""This class represents an alias, or indirection, to an object declared in another module.
Aliases represent objects that are in the scope of a module or class,
Expand Down Expand Up @@ -804,6 +805,10 @@ def __delitem__(self, key: str | tuple[str, ...]):
# not handled by __getattr__
del self.target[key]

def __bool__(self) -> bool:
# Prevent using `__len__`.
return True

def __len__(self) -> int:
return 1

Expand Down Expand Up @@ -883,19 +888,27 @@ def inherited_members(self) -> dict[str, Alias]: # noqa: D102
for name, member in final_target.inherited_members.items()
}

def as_json(self, *, full: bool = False, **kwargs: Any) -> str: # noqa: D102
try:
return self.final_target.as_json(full=full, **kwargs)
except (AliasResolutionError, CyclicAliasError):
return super().as_json(full=full, **kwargs)

# GENERIC OBJECT PROXIES --------------------------------
# The following methods and properties exist on the target(s).
# We first try to reach the final target, trigerring alias resolution errors
# and cyclic aliases errors early. We avoid recursing in the alias chain.

@property
def lineno(self) -> int | None:
"""The target lineno or the alias lineno."""
def extra(self) -> dict: # noqa: D102
return self.final_target.extra

@property
def lineno(self) -> int | None: # noqa: D102
return self.final_target.lineno

@property
def endlineno(self) -> int | None:
"""The target endlineno or the alias endlineno."""
def endlineno(self) -> int | None: # noqa: D102
return self.final_target.endlineno

@property
Expand Down Expand Up @@ -966,6 +979,10 @@ def filepath(self) -> Path | list[Path]: # noqa: D102
def relative_filepath(self) -> Path: # noqa: D102
return self.final_target.relative_filepath

@property
def relative_package_filepath(self) -> Path: # noqa: D102
return self.final_target.relative_package_filepath

@property
def canonical_path(self) -> str: # noqa: D102
return self.final_target.canonical_path
Expand Down Expand Up @@ -1011,6 +1028,30 @@ def bases(self) -> list[Expr | str]: # noqa: D102
def decorators(self) -> list[Decorator]: # noqa: D102
return cast(Union[Class, Function], self.target).decorators

@property
def imports_future_annotations(self) -> bool: # noqa: D102
return cast(Module, self.final_target).imports_future_annotations

@property
def is_init_module(self) -> bool: # noqa: D102
return cast(Module, self.final_target).is_init_module

@property
def is_package(self) -> bool: # noqa: D102
return cast(Module, self.final_target).is_package

@property
def is_subpackage(self) -> bool: # noqa: D102
return cast(Module, self.final_target).is_subpackage

@property
def is_namespace_package(self) -> bool: # noqa: D102
return cast(Module, self.final_target).is_namespace_package

@property
def is_namespace_subpackage(self) -> bool: # noqa: D102
return cast(Module, self.final_target).is_namespace_subpackage

@property
def overloads(self) -> dict[str, list[Function]] | list[Function] | None: # noqa: D102
return cast(Union[Module, Class, Function], self.target).overloads
Expand Down
20 changes: 20 additions & 0 deletions tests/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from copy import deepcopy

import griffe
from griffe.dataclasses import Docstring, Module
from griffe.loader import GriffeLoader
from griffe.tests import module_vtree, temporary_pypackage
Expand Down Expand Up @@ -70,3 +71,22 @@ def test_deepcopy() -> None:

deepcopy(mod)
deepcopy(mod.as_dict())


def test_alias_proxies() -> None:
"""Assert that the Alias class has all the necessary methods and properties.
Parameters:
cls: The class to check.
"""
api = griffe.load("griffe")
alias_members = set(api["dataclasses.Alias"].all_members.keys())
for cls in (
api["dataclasses.Module"],
api["dataclasses.Class"],
api["dataclasses.Function"],
api["dataclasses.Attribute"],
):
for name in cls.all_members:
if not name.startswith("_") or name.startswith("__"):
assert name in alias_members

0 comments on commit 7320640

Please # to comment.