Skip to content

Commit

Permalink
Упрощение таймаута и тайпхинты
Browse files Browse the repository at this point in the history
Убрал повторную регистрацию мидлварей в диспетчере для таймаут события
Добавил удобные тайпхинты для property полей
Добавил session в __init__ в TYPE_CHECKING блоке для наследников от AliceEvent
  • Loading branch information
K1rL3s committed Jan 23, 2024
1 parent 6008b6f commit 3babe19
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 128 deletions.
29 changes: 13 additions & 16 deletions aliceio/dispatcher/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
from ..fsm.storage.base import BaseStorage
from ..fsm.storage.memory import MemoryStorage
from ..fsm.strategy import FSMStrategy
from ..types import AliceResponse, Response, TimeoutEvent, Update, UpdateTypeLookupError
from ..types import (
AliceResponse,
Response,
TimeoutUpdate,
Update,
UpdateTypeLookupError,
)
from ..types.base import AliceObject
from .event.alice import AliceEventObserver
from .event.bases import UNHANDLED, SkipHandler
Expand Down Expand Up @@ -43,7 +49,7 @@ def __init__(
:param disable_fsm: Отключить ли FSM.
:param name: Имя как роутера, полезно при дебаге.
:param response_timeout: Время для обработки события,
после которого будет вызван TimeoutEvent.
после которого будет вызван :class:`TimeoutUpdate`.
:param kwargs: Остальные аргументы,
будут переданы в обработчики как именованные аргументы
"""
Expand All @@ -55,20 +61,14 @@ def __init__(
)
self.update.register(self._listen_update)

# На timeout-observer тоже регистрируются все те же мидлвари, что и на update,
# потому что при возникновении TimeoutEvent'а контекстные данные из мидлварей
# оригинального Update не получить. Засчитаю за костыль

# Обработчики ошибок должны работать вне всех других функций
# и должны быть зарегистрированы раньше всех остальных мидлварей.
self.update.outer_middleware(ErrorsMiddleware(self))
self.timeout.outer_middleware(ErrorsMiddleware(self))

# UserContextMiddleware выполняет небольшую оптимизацию
# для всех других встроенных мидлварей путем кэширования
# экземпляров пользователя и сессиив контексте событий.
self.update.outer_middleware(UserContextMiddleware())
self.timeout.outer_middleware(UserContextMiddleware())

# FSMContextMiddleware всегда следует регистрировать после UserContextMiddleware
# поскольку здесь используется контекст из предыдущего шага.
Expand All @@ -78,7 +78,6 @@ def __init__(
)
if not disable_fsm:
self.update.outer_middleware(self.fsm)
self.timeout.outer_middleware(self.fsm)
self.shutdown.register(self.fsm.close)

self.response_timeout = response_timeout
Expand Down Expand Up @@ -158,7 +157,7 @@ async def feed_update(self, skill: Skill, update: Update, **kwargs: Any) -> Any:
finish_time = loop.time()
duration = (finish_time - start_time) * 1000
loggers.event.info(
"Update from session_id=%s is %s. Duration %d ms by skill id=%d",
"Update from session_id=%r is %s. Duration %d ms by skill id=%r",
update.session.session_id,
"handled" if handled else "not handled",
duration,
Expand Down Expand Up @@ -292,14 +291,12 @@ async def _process_timeouted_update(
"by `@<router>.timeout` to respond to timeouted updates.",
RuntimeWarning,
)
return await self.propagate_event(
event_type=EventType.TIMEOUT,
event=TimeoutEvent(
update=update,
session=update.session,
return await self._feed_webhook_update(
skill=skill,
update=TimeoutUpdate.model_validate(
update.model_dump(),
context={"skill": skill},
),
skill=skill,
**self.workflow_data,
**kwargs,
)
Expand Down
10 changes: 5 additions & 5 deletions aliceio/dispatcher/middlewares/user_context.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, Union
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple

from aliceio.dispatcher.middlewares.base import BaseMiddleware
from aliceio.types import Session, TimeoutEvent, Update, User
from aliceio.types import Session, Update, User
from aliceio.types.base import AliceObject

EVENT_FROM_USER_KEY = "event_from_user"
Expand All @@ -16,21 +16,21 @@ async def __call__(
event: AliceObject,
data: Dict[str, Any],
) -> Any:
if not isinstance(event, (Update, TimeoutEvent)):
if not isinstance(event, Update):
raise RuntimeError("UserContextMiddleware got an unexpected event type!")

session, user = self.resolve_event_context(event=event)

if user is not None:
data[EVENT_FROM_USER_KEY] = user
data[EVENT_SESSION_KEY] = session
data[EVENT_UPDATE_KEY] = event if isinstance(event, Update) else event.update
data[EVENT_UPDATE_KEY] = event

return await handler(event, data)

@classmethod
def resolve_event_context(
cls,
event: Union[Update, TimeoutEvent],
event: Update,
) -> Tuple[Session, Optional[User]]:
return event.session, event.session.user
4 changes: 2 additions & 2 deletions aliceio/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from .state import ApplicationState, AuthorizedUserState, SessionState, StateDict
from .stream import Stream
from .text_button import TextButton
from .timeout_event import TimeoutEvent
from .timeout_event import TimeoutUpdate
from .tokens_entity import TokensEntity
from .update import Update, UpdateTypeLookupError
from .uploaded_image import PreUploadedImage, UploadedImage, UploadedImagesList
Expand Down Expand Up @@ -109,7 +109,7 @@
"StateDict",
"Stream",
"TextButton",
"TimeoutEvent",
"TimeoutUpdate",
"TokensEntity",
"Update",
"UpdateTypeLookupError",
Expand Down
18 changes: 11 additions & 7 deletions aliceio/types/alice_event.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any, ClassVar, Optional

from aliceio.types.base import MutableAliceObject
from aliceio.types.session import Session
Expand All @@ -10,6 +10,8 @@ class AliceEvent(MutableAliceObject, ABC):
session: Session

if TYPE_CHECKING:
from_user: ClassVar[Optional[User]]
user: ClassVar[Optional[User]]

def __init__(
__pydantic_self__,
Expand All @@ -22,10 +24,12 @@ def __init__(
**__pydantic_kwargs,
)

@property
def from_user(self) -> Optional[User]:
return self.session.user
else:

@property
def user(self) -> Optional[User]:
return self.session.user
@property
def from_user(self) -> Optional[User]:
return self.session.user

@property
def user(self) -> Optional[User]:
return self.session.user
11 changes: 7 additions & 4 deletions aliceio/types/error_event.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, ClassVar

from .alice_event import AliceEvent
from .update import Update
Expand All @@ -11,6 +11,7 @@ class ErrorEvent(AliceEvent):
exception: Exception

if TYPE_CHECKING:
event: ClassVar[AliceEvent]

def __init__(
__pydantic_self__,
Expand All @@ -25,6 +26,8 @@ def __init__(
**__pydantic_kwargs,
)

@property
def event(self) -> AliceEvent:
return self.update.event
else:

@property
def event(self) -> AliceEvent:
return self.update.event
22 changes: 21 additions & 1 deletion aliceio/types/message.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any, ClassVar, Optional

from .alice_event import AliceEvent
from .markup import Markup
from .nlu import NLU
from .payload import Payload
from .session import Session


class Message(AliceEvent):
Expand All @@ -21,13 +22,17 @@ class Message(AliceEvent):
nlu: Optional[NLU] = None

if TYPE_CHECKING:
text: ClassVar[str]
original_text: ClassVar[str]
original_command: ClassVar[str]

def __init__(
__pydantic_self__,
*,
type: str,
command: str,
original_utterance: str,
session: Session, # из AliceEvent
payload: Optional[Payload] = None,
markup: Optional[Markup] = None,
nlu: Optional[NLU] = None,
Expand All @@ -37,8 +42,23 @@ def __init__(
type=type,
command=command,
original_utterance=original_utterance,
session=session,
payload=payload,
markup=markup,
nlu=nlu,
**__pydantic_kwargs,
)

else:

@property
def text(self) -> str:
return self.command

@property
def original_text(self) -> str:
return self.original_utterance

@property
def original_command(self) -> str:
return self.original_utterance
12 changes: 7 additions & 5 deletions aliceio/types/quota.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, ClassVar

from pydantic import computed_field

Expand All @@ -16,6 +16,7 @@ class Quota(AliceObject):
used: int

if TYPE_CHECKING:
available: ClassVar[int]

def __init__(
__pydantic_self__,
Expand All @@ -30,10 +31,11 @@ def __init__(
**__pydantic_kwargs,
)

@computed_field # type: ignore[misc]
@property
def available(self) -> int:
return self.total - self.used
else:

@property
def available(self) -> int:
return self.total - self.used


class PreQuota(AliceObject):
Expand Down
1 change: 1 addition & 0 deletions aliceio/types/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __init__(
message_id: int,
session_id: str,
skill_id: str,
# user_id: str,
application: Application,
new: bool,
user: Optional[User] = None, # None если пользователь неавторизован
Expand Down
42 changes: 25 additions & 17 deletions aliceio/types/timeout_event.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast

from ..enums import EventType
from .alice_event import AliceEvent
from .update import Update
from .base import MutableAliceObject
from .update import Update, UpdateTypeLookupError


class TimeoutEvent(AliceEvent):
# Если кто-то сможет лучше реализовать подмену update'а - буду благодарен
class TimeoutUpdate(Update):
"""Внутренннее событие, используется для реакции на выход за время ответа."""

update: Update

if TYPE_CHECKING:
__init__ = Update.__init__

else:

@property
def event(self) -> AliceEvent:
return cast(AliceEvent, getattr(self, self._real_event_type))

@property
def event_type(self) -> str:
return str(EventType.TIMEOUT)

def __init__(
__pydantic_self__,
*,
update: Update,
**__pydantic_kwargs: Any,
) -> None:
super().__init__(
update=update,
**__pydantic_kwargs,
)
def model_post_init(self, __context: Any) -> None:
MutableAliceObject.model_post_init(self, __context)
try:
self._event_model_validate(self._real_event_type, __context)
except UpdateTypeLookupError:
pass

@property
def event(self) -> AliceEvent:
return self.update.event
def _real_event_type(self) -> str:
return super().event_type
Loading

0 comments on commit 3babe19

Please sign in to comment.