initial commit
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
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
|
||||
Reference in New Issue
Block a user