Precalculate StateGroup values (#1507)

* Precalculate StateGroup values

* Changelog added

* Remove redundant overrides

* Refactor children preparing

* Refactor mcs fn visibility

* Refactor mcs fn visibility (fix)
This commit is contained in:
Oleg A. 2024-08-14 02:10:31 +03:00 committed by GitHub
parent 2215df7a0f
commit 9b0b6a68ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 32 additions and 9 deletions

1
CHANGES/1507.misc.rst Normal file
View file

@ -0,0 +1 @@
Improved performance of StatesGroup

View file

@ -70,10 +70,13 @@ class StatesGroupMeta(type):
__childs__: "Tuple[Type[StatesGroup], ...]" __childs__: "Tuple[Type[StatesGroup], ...]"
__states__: Tuple[State, ...] __states__: Tuple[State, ...]
__state_names__: Tuple[str, ...] __state_names__: Tuple[str, ...]
__all_childs__: Tuple[Type["StatesGroup"], ...]
__all_states__: Tuple[State, ...]
__all_states_names__: Tuple[str, ...]
@no_type_check @no_type_check
def __new__(mcs, name, bases, namespace, **kwargs): def __new__(mcs, name, bases, namespace, **kwargs):
cls = super(StatesGroupMeta, mcs).__new__(mcs, name, bases, namespace) cls = super().__new__(mcs, name, bases, namespace)
states = [] states = []
childs = [] childs = []
@ -82,14 +85,22 @@ class StatesGroupMeta(type):
if isinstance(arg, State): if isinstance(arg, State):
states.append(arg) states.append(arg)
elif inspect.isclass(arg) and issubclass(arg, StatesGroup): elif inspect.isclass(arg) and issubclass(arg, StatesGroup):
childs.append(arg) child = cls._prepare_child(arg)
arg.__parent__ = cls childs.append(child)
cls.__parent__ = None cls.__parent__ = None
cls.__childs__ = tuple(childs) cls.__childs__ = tuple(childs)
cls.__states__ = tuple(states) cls.__states__ = tuple(states)
cls.__state_names__ = tuple(state.state for state in states) cls.__state_names__ = tuple(state.state for state in states)
cls.__all_childs__ = cls._get_all_childs()
cls.__all_states__ = cls._get_all_states()
# In order to ensure performance, we calculate this parameter
# in advance already during the production of the class.
# Depending on the relationship, it should be recalculated
cls.__all_states_names__ = cls._get_all_states_names()
return cls return cls
@property @property
@ -98,22 +109,33 @@ class StatesGroupMeta(type):
return ".".join((cls.__parent__.__full_group_name__, cls.__name__)) return ".".join((cls.__parent__.__full_group_name__, cls.__name__))
return cls.__name__ return cls.__name__
@property def _prepare_child(cls, child: Type["StatesGroup"]) -> Type["StatesGroup"]:
def __all_childs__(cls) -> Tuple[Type["StatesGroup"], ...]: """Prepare child.
While adding `cls` for its children, we also need to recalculate
the parameter `__all_states_names__` for each child
`StatesGroup`. Since the child class appears before the
parent, at the time of adding the parent, the child's
`__all_states_names__` is already recorded without taking into
account the name of current parent.
"""
child.__parent__ = cls # type: ignore[assignment]
child.__all_states_names__ = child._get_all_states_names()
return child
def _get_all_childs(cls) -> Tuple[Type["StatesGroup"], ...]:
result = cls.__childs__ result = cls.__childs__
for child in cls.__childs__: for child in cls.__childs__:
result += child.__childs__ result += child.__childs__
return result return result
@property def _get_all_states(cls) -> Tuple[State, ...]:
def __all_states__(cls) -> Tuple[State, ...]:
result = cls.__states__ result = cls.__states__
for group in cls.__childs__: for group in cls.__childs__:
result += group.__all_states__ result += group.__all_states__
return result return result
@property def _get_all_states_names(cls) -> Tuple[str, ...]:
def __all_states_names__(cls) -> Tuple[str, ...]:
return tuple(state.state for state in cls.__all_states__ if state.state) return tuple(state.state for state in cls.__all_states__ if state.state)
def __contains__(cls, item: Any) -> bool: def __contains__(cls, item: Any) -> bool: