* Rework middlewares, separate management to `MiddlewareManager` class

* Rework middlewares

* Added changes description for redis

* Added changes description for redis

* Fixed tests with Redis // aioredis replacement

* Changed msg.<html/md>_text attributes behaviour

* Added changelog for spoilers

* Added possibility to get command magic result as handler arguments
This commit is contained in:
Alex Root Junior 2022-04-16 19:07:32 +03:00 committed by GitHub
parent 930bca0876
commit 286cf39c8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 1380 additions and 804 deletions

View file

@ -3,22 +3,9 @@ from __future__ import annotations
import abc
import datetime
import json
from functools import partial
from http import HTTPStatus
from types import TracebackType
from typing import (
TYPE_CHECKING,
Any,
AsyncGenerator,
Awaitable,
Callable,
Final,
List,
Optional,
Type,
Union,
cast,
)
from typing import TYPE_CHECKING, Any, AsyncGenerator, Callable, Final, Optional, Type, Union, cast
from aiogram.exceptions import (
RestartingTelegram,
@ -36,26 +23,15 @@ from aiogram.exceptions import (
from ...methods import Response, TelegramMethod
from ...methods.base import TelegramType
from ...types import UNSET, TelegramObject
from ...types import UNSET
from ..telegram import PRODUCTION, TelegramAPIServer
from .middlewares.base import BaseRequestMiddleware
from .middlewares.manager import RequestMiddlewareManager
if TYPE_CHECKING:
from ..bot import Bot
_JsonLoads = Callable[..., Any]
_JsonDumps = Callable[..., str]
NextRequestMiddlewareType = Callable[
["Bot", TelegramMethod[TelegramObject]], Awaitable[Response[TelegramObject]]
]
RequestMiddlewareType = Union[
BaseRequestMiddleware,
Callable[
[NextRequestMiddlewareType, "Bot", TelegramMethod[TelegramType]],
Awaitable[Response[TelegramType]],
],
]
DEFAULT_TIMEOUT: Final[float] = 60.0
@ -80,7 +56,7 @@ class BaseSession(abc.ABC):
self.json_dumps = json_dumps
self.timeout = timeout
self.middlewares: List[RequestMiddlewareType[TelegramObject]] = []
self.middleware = RequestMiddlewareManager()
def check_response(
self, method: TelegramMethod[TelegramType], status_code: int, content: str
@ -185,19 +161,11 @@ class BaseSession(abc.ABC):
return {k: self.clean_json(v) for k, v in value.items() if v is not None}
return value
def middleware(
self, middleware: RequestMiddlewareType[TelegramObject]
) -> RequestMiddlewareType[TelegramObject]:
self.middlewares.append(middleware)
return middleware
async def __call__(
self, bot: Bot, method: TelegramMethod[TelegramType], timeout: Optional[int] = UNSET
) -> TelegramType:
middleware = partial(self.make_request, timeout=timeout)
for m in reversed(self.middlewares):
middleware = partial(m, middleware) # type: ignore
return await middleware(bot, method)
middleware = self.middleware.wrap_middlewares(self.make_request, timeout=timeout)
return cast(TelegramType, await middleware(bot, method))
async def __aenter__(self) -> BaseSession:
return self

View file

@ -1,15 +1,23 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, Awaitable, Callable
from typing import TYPE_CHECKING, Awaitable, Callable, Union
from aiogram.methods import Response, TelegramMethod
from aiogram.types import TelegramObject
from aiogram.methods.base import TelegramType
if TYPE_CHECKING:
from ...bot import Bot
NextRequestMiddlewareType = Callable[
["Bot", TelegramMethod[TelegramObject]], Awaitable[Response[TelegramObject]]
["Bot", TelegramMethod[TelegramType]], Awaitable[Response[TelegramType]]
]
RequestMiddlewareType = Union[
"BaseRequestMiddleware",
Callable[
[NextRequestMiddlewareType[TelegramType], "Bot", TelegramMethod[TelegramType]],
Awaitable[Response[TelegramType]],
],
]
@ -21,10 +29,10 @@ class BaseRequestMiddleware(ABC):
@abstractmethod
async def __call__(
self,
make_request: NextRequestMiddlewareType,
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramObject],
) -> Response[TelegramObject]:
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
"""
Execute middleware

View file

@ -0,0 +1,79 @@
from __future__ import annotations
from functools import partial
from typing import (
TYPE_CHECKING,
Any,
Awaitable,
Callable,
List,
Optional,
Sequence,
Union,
overload,
)
from aiogram.client.session.middlewares.base import (
NextRequestMiddlewareType,
RequestMiddlewareType,
)
from aiogram.methods import Response
from aiogram.methods.base import TelegramMethod, TelegramType
from aiogram.types import TelegramObject
if TYPE_CHECKING:
from aiogram import Bot
class RequestMiddlewareManager(Sequence[RequestMiddlewareType[TelegramObject]]):
def __init__(self) -> None:
self._middlewares: List[RequestMiddlewareType[TelegramObject]] = []
def register(
self,
middleware: RequestMiddlewareType[TelegramObject],
) -> RequestMiddlewareType[TelegramObject]:
self._middlewares.append(middleware)
return middleware
def unregister(self, middleware: RequestMiddlewareType[TelegramObject]) -> None:
self._middlewares.remove(middleware)
def __call__(
self,
middleware: Optional[RequestMiddlewareType[TelegramObject]] = None,
) -> Union[
Callable[[RequestMiddlewareType[TelegramObject]], RequestMiddlewareType[TelegramObject]],
RequestMiddlewareType[TelegramObject],
]:
if middleware is None:
return self.register
return self.register(middleware)
@overload
def __getitem__(self, item: int) -> RequestMiddlewareType[TelegramObject]:
pass
@overload
def __getitem__(self, item: slice) -> Sequence[RequestMiddlewareType[TelegramObject]]:
pass
def __getitem__(
self, item: Union[int, slice]
) -> Union[
RequestMiddlewareType[TelegramObject], Sequence[RequestMiddlewareType[TelegramObject]]
]:
return self._middlewares[item]
def __len__(self) -> int:
return len(self._middlewares)
def wrap_middlewares(
self,
callback: Callable[[Bot, TelegramMethod[TelegramType]], Awaitable[Response[TelegramType]]],
**kwargs: Any,
) -> NextRequestMiddlewareType[TelegramType]:
middleware = partial(callback, **kwargs)
for m in reversed(self._middlewares):
middleware = partial(m, middleware) # type: ignore
return middleware

View file

@ -3,8 +3,7 @@ from typing import TYPE_CHECKING, Any, List, Optional, Type
from aiogram import loggers
from aiogram.methods import TelegramMethod
from aiogram.methods.base import Response
from aiogram.types import TelegramObject
from aiogram.methods.base import Response, TelegramType
from .base import BaseRequestMiddleware, NextRequestMiddlewareType
@ -25,10 +24,10 @@ class RequestLogging(BaseRequestMiddleware):
async def __call__(
self,
make_request: NextRequestMiddlewareType,
make_request: NextRequestMiddlewareType[TelegramType],
bot: "Bot",
method: TelegramMethod[TelegramObject],
) -> Response[TelegramObject]:
method: TelegramMethod[TelegramType],
) -> Response[TelegramType]:
if type(method) not in self.ignore_methods:
loggers.middlewares.info(
"Make request with method=%r by bot id=%d",