87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
import inspect
|
|
from dataclasses import dataclass
|
|
from operator import itemgetter
|
|
from typing import Any, Generator, NamedTuple, Protocol
|
|
|
|
from aiogram.utils.dataclass import dataclass_kwargs
|
|
|
|
|
|
class ClassAttrsResolver(Protocol):
|
|
def __call__(self, cls: type) -> Generator[tuple[str, Any], None, None]: ...
|
|
|
|
|
|
def inspect_members_resolver(cls: type) -> Generator[tuple[str, Any], None, None]:
|
|
"""
|
|
Inspects and resolves attributes of a given class.
|
|
|
|
This function uses the `inspect.getmembers` utility to yield all attributes of
|
|
a provided class. The output is a generator that produces tuples containing
|
|
attribute names and their corresponding values. This function is suitable for
|
|
analyzing class attributes dynamically. However, it guarantees alphabetical
|
|
order of attributes.
|
|
|
|
:param cls: The class for which the attributes will be resolved.
|
|
:return: A generator yielding tuples containing attribute names and their values.
|
|
"""
|
|
yield from inspect.getmembers(cls)
|
|
|
|
|
|
def get_reversed_mro_unique_attrs_resolver(cls: type) -> Generator[tuple[str, Any], None, None]:
|
|
"""
|
|
Resolve and yield attributes from the reversed method resolution order (MRO) of a given class.
|
|
|
|
This function iterates through the reversed MRO of a class and yields attributes
|
|
that have not yet been encountered. It avoids duplicates by keeping track of
|
|
attribute names that have already been processed.
|
|
|
|
:param cls: The class for which the attributes will be resolved.
|
|
:return: A generator yielding tuples containing attribute names and their values.
|
|
"""
|
|
known_attrs = set()
|
|
for base in reversed(inspect.getmro(cls)):
|
|
for name, value in base.__dict__.items():
|
|
if name in known_attrs:
|
|
continue
|
|
|
|
yield name, value
|
|
known_attrs.add(name)
|
|
|
|
|
|
class _Position(NamedTuple):
|
|
in_mro: int
|
|
in_class: int
|
|
|
|
|
|
@dataclass(**dataclass_kwargs(slots=True))
|
|
class _AttributeContainer:
|
|
position: _Position
|
|
value: Any
|
|
|
|
def __lt__(self, other: "_AttributeContainer") -> bool:
|
|
return self.position < other.position
|
|
|
|
|
|
def get_sorted_mro_attrs_resolver(cls: type) -> Generator[tuple[str, Any], None, None]:
|
|
"""
|
|
Resolve and yield attributes from the method resolution order (MRO) of a given class.
|
|
|
|
Iterates through a class's method resolution order (MRO) and collects its attributes
|
|
along with their respective positions in the MRO and the class hierarchy. This generator
|
|
yields a tuple containing the name of each attribute and its associated value.
|
|
|
|
:param cls: The class for which the attributes will be resolved.
|
|
:return: A generator yielding tuples containing attribute names and their values.
|
|
"""
|
|
attributes: dict[str, _AttributeContainer] = {}
|
|
for position_in_mro, base in enumerate(inspect.getmro(cls)):
|
|
for position_in_class, (name, value) in enumerate(vars(base).items()):
|
|
position = _Position(position_in_mro, position_in_class)
|
|
if attribute := attributes.get(name):
|
|
attribute.position = position
|
|
continue
|
|
|
|
attributes[name] = _AttributeContainer(value=value, position=position)
|
|
|
|
for name, attribute in sorted(attributes.items(), key=itemgetter(1)):
|
|
yield name, attribute.value
|