From d400cb13c8b7c250ff1e6b6c8ec9be1c7b6ff989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 23 Aug 2023 16:18:43 +0200 Subject: [PATCH] refactor: Preserve alias members path by re-aliasing members instead of returning target's members --- src/griffe/dataclasses.py | 97 +++++---------------------------------- src/griffe/mixins.py | 60 +++++++++++++++++++++++- 2 files changed, 71 insertions(+), 86 deletions(-) diff --git a/src/griffe/dataclasses.py b/src/griffe/dataclasses.py index 1cb867d2..0ba5430e 100644 --- a/src/griffe/dataclasses.py +++ b/src/griffe/dataclasses.py @@ -420,15 +420,6 @@ def inherited_members(self) -> dict[str, Alias]: inherited_members[name] = Alias(name, member, parent=self, inherited=True) return inherited_members - @property - def all_members(self) -> dict[str, Object | Alias]: - """All members (declared and inherited). - - This method is part of the consumer API: - do not use when producing Griffe trees! - """ - return {**self.inherited_members, **self.members} - @property def is_module(self) -> bool: """Tell if this object is a module.""" @@ -477,54 +468,6 @@ def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[ members[name] = member return members - @property - def modules(self) -> dict[str, Module]: - """Return the module members. - - This method is part of the consumer API: - do not use when producing Griffe trees! - - Returns: - A dictionary of modules. - """ - return {name: member for name, member in self.all_members.items() if member.kind is Kind.MODULE} # type: ignore[misc] - - @property - def classes(self) -> dict[str, Class]: - """Return the class members. - - This method is part of the consumer API: - do not use when producing Griffe trees! - - Returns: - A dictionary of classes. - """ - return {name: member for name, member in self.all_members.items() if member.kind is Kind.CLASS} # type: ignore[misc] - - @property - def functions(self) -> dict[str, Function]: - """Return the function members. - - This method is part of the consumer API: - do not use when producing Griffe trees! - - Returns: - A dictionary of functions. - """ - return {name: member for name, member in self.all_members.items() if member.kind is Kind.FUNCTION} # type: ignore[misc] - - @property - def attributes(self) -> dict[str, Attribute]: - """Return the attribute members. - - This method is part of the consumer API: - do not use when producing Griffe trees! - - Returns: - A dictionary of attributes. - """ - return {name: member for name, member in self.all_members.items() if member.kind is Kind.ATTRIBUTE} # type: ignore[misc] - @property def module(self) -> Module: """Return the parent module of this object. @@ -798,7 +741,7 @@ def __init__( lineno: int | None = None, endlineno: int | None = None, runtime: bool = True, - parent: Module | Class | None = None, + parent: Module | Class | Alias | None = None, inherited: bool = False, ) -> None: """Initialize the alias. @@ -818,7 +761,7 @@ def __init__( self.alias_endlineno: int | None = endlineno self.runtime: bool = runtime self.inherited: bool = inherited - self._parent: Module | Class | None = parent + self._parent: Module | Class | Alias | None = parent self._passed_through: bool = False if isinstance(target, str): self._target: Object | Alias | None = None @@ -896,7 +839,7 @@ def has_docstrings(self) -> bool: return False @property - def parent(self) -> Module | Class | None: + def parent(self) -> Module | Class | Alias | None: """Return the parent of this alias. Returns: @@ -905,7 +848,7 @@ def parent(self) -> Module | Class | None: return self._parent @parent.setter - def parent(self, value: Module | Class) -> None: + def parent(self, value: Module | Class | Alias) -> None: self._parent = value self._update_target_aliases() @@ -938,9 +881,10 @@ def docstring(self) -> Docstring | None: # noqa: D102 def docstring(self, docstring: Docstring | None) -> None: self.final_target.docstring = docstring - @property + @cached_property def members(self) -> dict[str, Object | Alias]: # noqa: D102 - return self.final_target.members + final_target = self.final_target + return {name: Alias(name, target=member, parent=self) for name, member in final_target.members.items()} @property def labels(self) -> set[str]: # noqa: D102 @@ -961,13 +905,12 @@ def aliases(self) -> dict[str, Alias]: # noqa: D102 def member_is_exported(self, member: Object | Alias, *, explicitely: bool = True) -> bool: # noqa: D102 return self.final_target.member_is_exported(member, explicitely=explicitely) - @property + @cached_property def inherited_members(self) -> dict[str, Alias]: # noqa: D102 - return self.final_target.inherited_members - - @property - def all_members(self) -> dict[str, Object | Alias]: # noqa: D102 - return self.final_target.all_members + final_target = self.final_target + return { + name: Alias(name, target=member, parent=self) for name, member in final_target.inherited_members.items() + } def is_kind(self, kind: str | Kind | set[str | Kind]) -> bool: # noqa: D102 return self.final_target.is_kind(kind) @@ -994,22 +937,6 @@ def has_labels(self, labels: set[str]) -> bool: # noqa: D102 def filter_members(self, *predicates: Callable[[Object | Alias], bool]) -> dict[str, Object | Alias]: # noqa: D102 return self.final_target.filter_members(*predicates) - @property - def modules(self) -> dict[str, Module]: # noqa: D102 - return self.final_target.modules - - @property - def classes(self) -> dict[str, Class]: # noqa: D102 - return self.final_target.classes - - @property - def functions(self) -> dict[str, Function]: # noqa: D102 - return self.final_target.functions - - @property - def attributes(self) -> dict[str, Attribute]: # noqa: D102 - return self.final_target.attributes - @property def module(self) -> Module: # noqa: D102 return self.final_target.module diff --git a/src/griffe/mixins.py b/src/griffe/mixins.py index 3a5279eb..64d23062 100644 --- a/src/griffe/mixins.py +++ b/src/griffe/mixins.py @@ -6,12 +6,13 @@ from contextlib import suppress from typing import TYPE_CHECKING, Any, Sequence, TypeVar +from griffe.enumerations import Kind from griffe.exceptions import AliasResolutionError, CyclicAliasError from griffe.logger import get_logger from griffe.merger import merge_stubs if TYPE_CHECKING: - from griffe.dataclasses import Alias, Object + from griffe.dataclasses import Alias, Attribute, Class, Function, Module, Object logger = get_logger(__name__) _ObjType = TypeVar("_ObjType") @@ -206,6 +207,63 @@ def set_member(self, key: str | Sequence[str], value: Object | Alias) -> None: class ObjectAliasMixin: """A mixin for methods that appear both in objects and aliases, unchanged.""" + @property + def all_members(self) -> dict[str, Object | Alias]: + """All members (declared and inherited). + + This method is part of the consumer API: + do not use when producing Griffe trees! + """ + return {**self.inherited_members, **self.members} # type: ignore[attr-defined] + + @property + def modules(self) -> dict[str, Module]: + """Return the module members. + + This method is part of the consumer API: + do not use when producing Griffe trees! + + Returns: + A dictionary of modules. + """ + return {name: member for name, member in self.all_members.items() if member.kind is Kind.MODULE} # type: ignore[misc] + + @property + def classes(self) -> dict[str, Class]: + """Return the class members. + + This method is part of the consumer API: + do not use when producing Griffe trees! + + Returns: + A dictionary of classes. + """ + return {name: member for name, member in self.all_members.items() if member.kind is Kind.CLASS} # type: ignore[misc] + + @property + def functions(self) -> dict[str, Function]: + """Return the function members. + + This method is part of the consumer API: + do not use when producing Griffe trees! + + Returns: + A dictionary of functions. + """ + return {name: member for name, member in self.all_members.items() if member.kind is Kind.FUNCTION} # type: ignore[misc] + + @property + def attributes(self) -> dict[str, Attribute]: + """Return the attribute members. + + This method is part of the consumer API: + do not use when producing Griffe trees! + + Returns: + A dictionary of attributes. + """ + return {name: member for name, member in self.all_members.items() if member.kind is Kind.ATTRIBUTE} # type: ignore[misc] + def is_exported(self, *, explicitely: bool = True) -> bool: """Tell if this object/alias is implicitely exported by its parent.