More usable state groups. All child groups know about name of parent group.

This commit is contained in:
Alex Root Junior 2018-06-28 18:51:00 +03:00
parent d8909ffe35
commit 051b92a716
2 changed files with 75 additions and 14 deletions

View file

@ -1,3 +1,4 @@
import inspect
import re
from _contextvars import ContextVar
@ -116,7 +117,7 @@ class StateFilter(BaseFilter):
ctx_state = ContextVar('user_state')
def __init__(self, dispatcher, state):
from aiogram.dispatcher.filters.state import State
from aiogram.dispatcher.filters.state import State, StatesGroup
super().__init__(dispatcher)
states = []
@ -125,8 +126,8 @@ class StateFilter(BaseFilter):
for item in state:
if isinstance(item, State):
states.append(item.state)
elif hasattr(item, 'state_names'): # issubclass() cannot be used in this place
states.extend(item.state_names)
elif inspect.isclass(item) and issubclass(item, StatesGroup):
states.extend(item.all_state_names)
else:
states.append(item)
self.states = states

View file

@ -1,16 +1,38 @@
import inspect
from typing import Optional
from ..dispatcher import Dispatcher
class State:
def __init__(self, state=None):
self.state = state
"""
State object
"""
def __init__(self, state: Optional[str] = None, group_name: Optional[str] = None):
self._state = state
self._group_name = group_name
self._group = None
@property
def state(self):
if self._group_name is None and self._group:
group = self._group.__full_group_name__
elif self._group_name:
group = self._group_name
else:
group = '*'
return f"{group}:{self._state}"
def set_parent(self, group):
if not issubclass(group, StatesGroup):
raise ValueError('Group must be subclass of StatesGroup')
self._group = group
def __set_name__(self, owner, name):
if self.state is None:
group_name = getattr(owner, '__group_name__')
if group_name is None:
group_name = owner.__name__
self.state = f"{group_name}:{name}"
if self._state is None:
self._state = name
self.set_parent(owner)
def __str__(self):
return f"<State '{self.state}>'"
@ -27,26 +49,64 @@ class MetaStatesGroup(type):
cls = super(MetaStatesGroup, mcs).__new__(mcs, name, bases, namespace)
states = []
for name, prop in ((name, prop) for name, prop in namespace.items() if isinstance(prop, State)):
states.append(prop)
childs = []
cls._group_name = name
for name, prop in namespace.items():
if isinstance(prop, State):
states.append(prop)
elif inspect.isclass(prop) and issubclass(prop, StatesGroup):
childs.append(prop)
prop._parent = cls
# continue
cls._parent = None
cls._childs = tuple(childs)
cls._states = tuple(states)
cls._state_names = tuple(state.state for state in states)
return cls
@property
def __group_name__(cls):
return cls._group_name
@property
def __full_group_name__(cls):
if cls._parent:
return cls._parent.__full_group_name__ + '.' + cls._group_name
return cls._group_name
@property
def states(cls) -> tuple:
return cls._states
@property
def childs(cls):
return cls._childs
@property
def all_states(cls):
result = cls.states
for group in cls.childs:
result += group.all_states
return result
@property
def all_state_names(cls):
return tuple(state.state for state in cls.all_states)
def __str__(self):
return f"<StatesGroup '{self.__full_group_name__}'>"
@property
def state_names(cls) -> tuple:
return cls._state_names
class StatesGroup(metaclass=MetaStatesGroup):
__group_name__ = None
@classmethod
async def next(cls) -> str:
state = Dispatcher.current().current_state()