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