From 4f2cc75951d18766be50bb9c3d14a3bc525a071c Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Sat, 31 Jul 2021 23:34:09 +0300 Subject: [PATCH] Global filters for router (#644) * Bump version * Added more comments * Cover registering global filters * Reformat code * Add more tests * Rework event propagation to routers mechanism. Fixed compatibility with Python 3.10 syntax (match keyword) * Fixed tests * Fixed coverage Co-authored-by: evgfilim1 --- aiogram/dispatcher/dispatcher.py | 13 +--- aiogram/dispatcher/event/bases.py | 1 + aiogram/dispatcher/event/telegram.py | 26 +++++++- aiogram/dispatcher/filters/command.py | 4 +- aiogram/dispatcher/filters/exception.py | 6 +- aiogram/dispatcher/fsm/storage/redis.py | 6 +- aiogram/dispatcher/router.py | 18 ++++++ poetry.lock | 59 +++++++++++++++++-- pyproject.toml | 3 +- .../test_methods/test_delete_my_commands.py | 2 +- .../test_event/test_telegram.py | 47 ++++++++++++++- .../test_filters/test_exception.py | 6 +- tests/test_dispatcher/test_router.py | 16 ++++- 13 files changed, 176 insertions(+), 31 deletions(-) diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index 109bb920..ee61d1d4 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -232,20 +232,11 @@ class Dispatcher(Router): "installed not latest version of aiogram framework", RuntimeWarning, ) - raise SkipHandler + raise SkipHandler() kwargs.update(event_update=update) - for router in self.chain: - kwargs.update(event_router=router) - observer = router.observers[update_type] - response = await observer.trigger(event, update=update, **kwargs) - if response is not UNHANDLED: - break - else: - response = UNHANDLED - - return response + return await self.propagate_event(update_type=update_type, event=event, **kwargs) @classmethod async def _silent_call_request(cls, bot: Bot, result: TelegramMethod[Any]) -> None: diff --git a/aiogram/dispatcher/event/bases.py b/aiogram/dispatcher/event/bases.py index cb5fb2cf..8e5937ec 100644 --- a/aiogram/dispatcher/event/bases.py +++ b/aiogram/dispatcher/event/bases.py @@ -12,6 +12,7 @@ MiddlewareType = Union[ ] UNHANDLED = sentinel.UNHANDLED +REJECTED = sentinel.REJECTED class SkipHandler(Exception): diff --git a/aiogram/dispatcher/event/telegram.py b/aiogram/dispatcher/event/telegram.py index f91e8b77..ad03c06c 100644 --- a/aiogram/dispatcher/event/telegram.py +++ b/aiogram/dispatcher/event/telegram.py @@ -8,7 +8,7 @@ from pydantic import ValidationError from ...types import TelegramObject from ..filters.base import BaseFilter -from .bases import UNHANDLED, MiddlewareType, NextMiddlewareType, SkipHandler +from .bases import REJECTED, UNHANDLED, MiddlewareType, NextMiddlewareType, SkipHandler from .handler import CallbackType, FilterObject, FilterType, HandlerObject, HandlerType if TYPE_CHECKING: # pragma: no cover @@ -32,6 +32,24 @@ class TelegramEventObserver: self.outer_middlewares: List[MiddlewareType] = [] self.middlewares: List[MiddlewareType] = [] + # Re-used filters check method from already implemented handler object + # with dummy callback which never will be used + self._handler = HandlerObject(callback=lambda: True, filters=[]) + + def filter(self, *filters: FilterType, **bound_filters: Any) -> None: + """ + Register filter for all handlers of this event observer + + :param filters: positional filters + :param bound_filters: keyword filters + """ + resolved_filters = self.resolve_filters(bound_filters) + if self._handler.filters is None: + self._handler.filters = [] + self._handler.filters.extend( + [FilterObject(filter_) for filter_ in chain(resolved_filters, filters)] + ) + def bind_filter(self, bound_filter: Type[BaseFilter]) -> None: """ Register filter class in factory @@ -139,6 +157,12 @@ class TelegramEventObserver: return await wrapped_outer(event, kwargs) async def _trigger(self, event: TelegramObject, **kwargs: Any) -> Any: + # Check globally defined filters before any other handler will be checked + result, data = await self._handler.check(event, **kwargs) + if not result: + return REJECTED + kwargs.update(data) + for handler in self.handlers: result, data = await handler.check(event, **kwargs) if result: diff --git a/aiogram/dispatcher/filters/command.py b/aiogram/dispatcher/filters/command.py index ba760bef..0e46c1ec 100644 --- a/aiogram/dispatcher/filters/command.py +++ b/aiogram/dispatcher/filters/command.py @@ -89,7 +89,7 @@ class Command(BaseFilter): if isinstance(allowed_command, Pattern): # Regexp result = allowed_command.match(command.command) if result: - return replace(command, match=result) + return replace(command, regexp_match=result) elif command.command == allowed_command: # String return command raise CommandException("Command did not match pattern") @@ -134,7 +134,7 @@ class CommandObject: """Mention (if available)""" args: Optional[str] = field(repr=False, default=None) """Command argument""" - match: Optional[Match[str]] = field(repr=False, default=None) + regexp_match: Optional[Match[str]] = field(repr=False, default=None) """Will be presented match result if the command is presented as regexp in filter""" @property diff --git a/aiogram/dispatcher/filters/exception.py b/aiogram/dispatcher/filters/exception.py index f46cd739..f4a077f8 100644 --- a/aiogram/dispatcher/filters/exception.py +++ b/aiogram/dispatcher/filters/exception.py @@ -26,20 +26,20 @@ class ExceptionMessageFilter(BaseFilter): Allow to match exception by message """ - match: Union[str, Pattern[str]] + pattern: Union[str, Pattern[str]] """Regexp pattern""" class Config: arbitrary_types_allowed = True - @validator("match") + @validator("pattern") def _validate_match(cls, value: Union[str, Pattern[str]]) -> Union[str, Pattern[str]]: if isinstance(value, str): return re.compile(value) return value async def __call__(self, exception: Exception) -> Union[bool, Dict[str, Any]]: - pattern = cast(Pattern[str], self.match) + pattern = cast(Pattern[str], self.pattern) result = pattern.match(str(exception)) if not result: return False diff --git a/aiogram/dispatcher/fsm/storage/redis.py b/aiogram/dispatcher/fsm/storage/redis.py index 64c832f9..f176e209 100644 --- a/aiogram/dispatcher/fsm/storage/redis.py +++ b/aiogram/dispatcher/fsm/storage/redis.py @@ -45,7 +45,7 @@ class RedisStorage(BaseStorage): return cls(redis=redis, **kwargs) async def close(self) -> None: - await self.redis.close() + await self.redis.close() # type: ignore def generate_key(self, bot: Bot, *parts: Any) -> str: prefix_parts = [self.prefix] @@ -73,7 +73,7 @@ class RedisStorage(BaseStorage): await self.redis.delete(key) else: await self.redis.set( - key, state.state if isinstance(state, State) else state, ex=self.state_ttl + key, state.state if isinstance(state, State) else state, ex=self.state_ttl # type: ignore[arg-type] ) async def get_state(self, bot: Bot, chat_id: int, user_id: int) -> Optional[str]: @@ -89,7 +89,7 @@ class RedisStorage(BaseStorage): await self.redis.delete(key) return json_data = bot.session.json_dumps(data) - await self.redis.set(key, json_data, ex=self.data_ttl) + await self.redis.set(key, json_data, ex=self.data_ttl) # type: ignore[arg-type] async def get_data(self, bot: Bot, chat_id: int, user_id: int) -> Dict[str, Any]: key = self.generate_key(bot, chat_id, user_id, STATE_DATA_KEY) diff --git a/aiogram/dispatcher/router.py b/aiogram/dispatcher/router.py index b61afc68..bc66d0de 100644 --- a/aiogram/dispatcher/router.py +++ b/aiogram/dispatcher/router.py @@ -3,8 +3,10 @@ from __future__ import annotations import warnings from typing import Any, Dict, Generator, List, Optional, Union +from ..types import TelegramObject from ..utils.imports import import_module from ..utils.warnings import CodeHasNoEffect +from .event.bases import REJECTED, UNHANDLED from .event.event import EventObserver from .event.telegram import TelegramEventObserver from .filters import BUILTIN_FILTERS @@ -82,6 +84,22 @@ class Router: for builtin_filter in BUILTIN_FILTERS.get(name, ()): observer.bind_filter(builtin_filter) + async def propagate_event(self, update_type: str, event: TelegramObject, **kwargs: Any) -> Any: + kwargs.update(event_router=self) + observer = self.observers[update_type] + response = await observer.trigger(event, **kwargs) + if response is REJECTED: + return UNHANDLED + if response is not UNHANDLED: + return response + + for router in self.sub_routers: + response = await router.propagate_event(update_type=update_type, event=event, **kwargs) + if response is not UNHANDLED: + break + + return response + @property def chain_head(self) -> Generator[Router, None, None]: router: Optional[Router] = self diff --git a/poetry.lock b/poetry.lock index 4e6bcb7d..7f324bfc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -40,7 +40,7 @@ python-socks = {version = ">=1.0.1", extras = ["asyncio"]} [[package]] name = "aioredis" -version = "2.0.0a1" +version = "2.0.0" description = "asyncio (PEP 3156) Redis support" category = "main" optional = false @@ -296,6 +296,14 @@ importlib-metadata = "*" jinja2 = ">=2.9.0" pygments = ">=2.2.0" +[[package]] +name = "frozenlist" +version = "1.1.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "furo" version = "2021.6.18b36" @@ -1201,7 +1209,7 @@ redis = ["aioredis"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "ef3571030ff35c2a05e01dca86e9347239e98ad0f45bed6f5d9a73121013f376" +content-hash = "c51e22cdb0e17fb996fda81c5484d34f3dff0e57511380b1103a1d53c9416440" [metadata.files] aiofiles = [ @@ -1252,8 +1260,8 @@ aiohttp-socks = [ {file = "aiohttp_socks-0.5.5.tar.gz", hash = "sha256:2eb2059756bde34c55bb429541cbf2eba3fd53e36ac80875b461221e2858b04a"}, ] aioredis = [ - {file = "aioredis-2.0.0a1-py3-none-any.whl", hash = "sha256:32d7910724282a475c91b8b34403867069a4f07bf0c5ad5fe66cd797322f9a0d"}, - {file = "aioredis-2.0.0a1.tar.gz", hash = "sha256:5884f384b8ecb143bb73320a96e7c464fd38e117950a7d48340a35db8e35e7d2"}, + {file = "aioredis-2.0.0-py3-none-any.whl", hash = "sha256:9921d68a3df5c5cdb0d5b49ad4fc88a4cfdd60c108325df4f0066e8410c55ffb"}, + {file = "aioredis-2.0.0.tar.gz", hash = "sha256:3a2de4b614e6a5f8e104238924294dc4e811aefbe17ddf52c04a93cbf06e67db"}, ] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, @@ -1401,6 +1409,49 @@ flake8-html = [ {file = "flake8-html-0.4.1.tar.gz", hash = "sha256:2fb436cbfe1e109275bc8fb7fdd0cb00e67b3b48cfeb397309b6b2c61eeb4cb4"}, {file = "flake8_html-0.4.1-py2.py3-none-any.whl", hash = "sha256:17324eb947e7006807e4184ee26953e67baf421b3cf9e646a38bfec34eec5a94"}, ] +frozenlist = [ + {file = "frozenlist-1.1.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:968b520ef969541b2c8f47d9a13c78e080806dc97862434d29163d44c2c1d709"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:451b445120ea95d86af3817bbd4d67ab77269fe7f055dc67b8c70bf4633f4efe"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:313384e54a7285a6f20ca6530b207a0a9cf6ebcda6c7b074ee802e4a82a0a6dc"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3c4f7399e7338a5788d32802017f94aaab3267afa8b1a663272b81eee7193e66"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1e7b18fdf6682028f512d3e6142b79ca95b9b66f30c1bec2be237160d9eb6518"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:7622f5c4c3dfaa09b9c6a62fb1af94da124626bb30f3ad9095f9cec6328074c1"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:657341b9bc166d3f7418d37e1decf6d95485501e0d0e7da1a26a881e624216c6"}, + {file = "frozenlist-1.1.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:fc6d994de78b11e1f465f2224c56858eb52cb51c8f9faf0c33e5799184d414a7"}, + {file = "frozenlist-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:08428f9d0178b6fa0da95a42ab87a5b20ed2a707bacc97e3689e96ae6cab13fa"}, + {file = "frozenlist-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:5c4c42bdbf5754010e0cc5cc0f91019437839bc6b7e585262bcc126557a244bc"}, + {file = "frozenlist-1.1.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:32491ac26e72e5f35913887bc3ab7bcfe562b4fb65b0e58350fce6efa22fec75"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:d160a73e4a034a857a98384b5e05204c375489d2bbb6ecf1ee8fc124735028fa"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b3cf6737afc4347092a0c8392b4c0e77acc5594e73f4aef355705117a945743a"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:efb805e383836250bef3c99f1857c432a8941c802d0ed7767751315617a54794"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:2f1f56a36962e28c304872797e226cd646395381de97517870fb819ff7b4f496"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:7443d815fd9ff2de75b810e192cfa92854bada43aed47ec1598766c7bc9d4a40"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe463e7b3cd089d221f33bd9c22cfad2726622b2a96c3af56a8eb5a71c0943bd"}, + {file = "frozenlist-1.1.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:c228886891dc0170d21acbfb62fea801856c3fa207619c973e17d96455ab83e3"}, + {file = "frozenlist-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:de170ea97e7b5051a13989ea457300b8159c00455d2207d22afb6b129a433152"}, + {file = "frozenlist-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:158c6258ab4ee8a01470d86e75a7514091391b27bb400ba28a7f6a30466cc8e0"}, + {file = "frozenlist-1.1.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:572a5a0977b1bd2f15183a352df907726b20da5f91cd1242343b0d72ac677be6"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:748150da8bbd9cbe1b29f0965a675b5732337ae874eed47ccb48dfa75815d0d7"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:caba5ae97c40020771502866dee5024b0031187293185ca5c7714ea52a824a92"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:aea1b84bbebec7c46cd59da13aff90e23bece13bba91974a305bd555f66a72f3"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:d07f08268b8d37c357f4d34272f1f7588a0618d3fa509a87ad614b5e1cf7109f"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:66f7ba888fe51685502be51ad548b226eb4214fcab0ef48672a2a91a4de08417"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:4282d897ea190b5e38a18fc3b70295e20e00af7734892250876e1e1b452a7dc8"}, + {file = "frozenlist-1.1.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:84ef9f6f6f8e2dd9cf828367c61715202a781ef6d32caa9a016d9055a7daef8b"}, + {file = "frozenlist-1.1.1-cp38-cp38-win32.whl", hash = "sha256:d5cba2a537bcf8b4abffc9e01b037eb4ca5c9d1cb29d575fd433f82919a04c68"}, + {file = "frozenlist-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:6c1cefdc3666507f7241b120b828e223c1dfb18e599f960ebbd0558de5010efe"}, + {file = "frozenlist-1.1.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a6d2d80222eefe6e08b8167005e5a0c1a05ce784ce97de4d6d693be7e2a99862"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:8fbdc86968f71d1d1e216f1f3467da96571b092378ad55b7eb6fc9f3ff877902"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1032a7eb76ca47cb94dcfd05a289dfb2f31b5e155c9cd845f97a56526eca9800"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4fdfb300d205f3d007462d66c9e8ffa89d7b1b3699e538ae7344845223291ff0"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:e2d35804cf42b58e42e9b2cca6a2a5bb7155bb545808ff652503a8bacab2be5d"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:a2ebc6dd4f73f39212073add6b3a629a4274ed0a5e43c2fa87bd91957f511450"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:9ee0bca9801eea5431680bdf22817b1b07310474ac284a3aa7a3902d0dba2382"}, + {file = "frozenlist-1.1.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:08a4f1bd182659416c8ae518ef8a63c37953eb2d4bd77cf8b45941a90e87d27c"}, + {file = "frozenlist-1.1.1-cp39-cp39-win32.whl", hash = "sha256:803bc0fdb904a762b0a49572fe2f1cb2a03ade5514b265971da5c3e7a8b14798"}, + {file = "frozenlist-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:301220a5752fc2585ef97794b1dc88f87d77f5c1951782488896fcb6a732f883"}, + {file = "frozenlist-1.1.1.tar.gz", hash = "sha256:32fa8c86eee5c6f11b44863c0d3e945b2b1a03df3bf218617e832f1f04ba3146"}, +] furo = [ {file = "furo-2021.6.18b36-py3-none-any.whl", hash = "sha256:a4c00634afeb5896a34d141a5dffb62f20c5eca7831b78269823a8cd8b09a5e4"}, {file = "furo-2021.6.18b36.tar.gz", hash = "sha256:46a30bc597a9067088d39d730e7d9bf6c1a1d71967e4af062f796769f66b3bdb"}, diff --git a/pyproject.toml b/pyproject.toml index b9e92418..d9c29089 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,8 +37,9 @@ pydantic = "^1.8.1" Babel = "^2.9.1" aiofiles = "^0.6.0" async_lru = "^1.0.2" +frozenlist = "^1.1.1" aiohttp-socks = { version = "^0.5.5", optional = true } -aioredis = { version = "^2.0.0a1", allow-prereleases = true, optional = true } +aioredis = { version = "^2.0.0", allow-prereleases = true, optional = true } magic-filter = { version = "1.0.0a1", allow-prereleases = true } sphinx = { version = "^3.1.0", optional = true } sphinx-intl = { version = "^2.0.1", optional = true } diff --git a/tests/test_api/test_methods/test_delete_my_commands.py b/tests/test_api/test_methods/test_delete_my_commands.py index a39d080b..b5546b25 100644 --- a/tests/test_api/test_methods/test_delete_my_commands.py +++ b/tests/test_api/test_methods/test_delete_my_commands.py @@ -1,6 +1,6 @@ import pytest -from aiogram.methods import BanChatMember, DeleteMyCommands, Request +from aiogram.methods import DeleteMyCommands, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_dispatcher/test_event/test_telegram.py b/tests/test_dispatcher/test_event/test_telegram.py index d4306b1a..13f70503 100644 --- a/tests/test_dispatcher/test_event/test_telegram.py +++ b/tests/test_dispatcher/test_event/test_telegram.py @@ -4,7 +4,7 @@ from typing import Any, Awaitable, Callable, Dict, NoReturn, Union import pytest -from aiogram.dispatcher.event.bases import SkipHandler +from aiogram.dispatcher.event.bases import REJECTED, SkipHandler from aiogram.dispatcher.event.handler import HandlerObject from aiogram.dispatcher.event.telegram import TelegramEventObserver from aiogram.dispatcher.filters.base import BaseFilter @@ -233,3 +233,48 @@ class TestTelegramEventObserver: assert my_middleware3 in middlewares assert middlewares == [my_middleware1, my_middleware2, my_middleware3] + + def test_register_global_filters(self): + router = Router(use_builtin_filters=False) + assert isinstance(router.message._handler.filters, list) + assert not router.message._handler.filters + + my_filter = MyFilter1(test="pass") + router.message.filter(my_filter) + + assert len(router.message._handler.filters) == 1 + assert router.message._handler.filters[0].callback is my_filter + + router.message._handler.filters = None + router.message.filter(my_filter) + assert len(router.message._handler.filters) == 1 + assert router.message._handler.filters[0].callback is my_filter + + @pytest.mark.asyncio + async def test_global_filter(self): + r1 = Router() + r2 = Router() + + async def handler(evt): + return evt + + r1.message.filter(lambda evt: False) + r1.message.register(handler) + r2.message.register(handler) + + assert await r1.message.trigger(None) is REJECTED + assert await r2.message.trigger(None) is None + + @pytest.mark.asyncio + async def test_global_filter_in_nested_router(self): + r1 = Router() + r2 = Router() + + async def handler(evt): + return evt + + r1.include_router(r2) + r1.message.filter(lambda evt: False) + r2.message.register(handler) + + assert await r1.message.trigger(None) is REJECTED diff --git a/tests/test_dispatcher/test_filters/test_exception.py b/tests/test_dispatcher/test_filters/test_exception.py index 4dd6d5d9..23d850da 100644 --- a/tests/test_dispatcher/test_filters/test_exception.py +++ b/tests/test_dispatcher/test_filters/test_exception.py @@ -8,12 +8,12 @@ from aiogram.dispatcher.filters import ExceptionMessageFilter, ExceptionTypeFilt class TestExceptionMessageFilter: @pytest.mark.parametrize("value", ["value", re.compile("value")]) def test_converter(self, value): - obj = ExceptionMessageFilter(match=value) - assert isinstance(obj.match, re.Pattern) + obj = ExceptionMessageFilter(pattern=value) + assert isinstance(obj.pattern, re.Pattern) @pytest.mark.asyncio async def test_match(self): - obj = ExceptionMessageFilter(match="KABOOM") + obj = ExceptionMessageFilter(pattern="KABOOM") result = await obj(Exception()) assert not result diff --git a/tests/test_dispatcher/test_router.py b/tests/test_dispatcher/test_router.py index c84239b1..1516c33c 100644 --- a/tests/test_dispatcher/test_router.py +++ b/tests/test_dispatcher/test_router.py @@ -1,6 +1,6 @@ import pytest -from aiogram.dispatcher.event.bases import SkipHandler, skip +from aiogram.dispatcher.event.bases import SkipHandler, skip, UNHANDLED from aiogram.dispatcher.router import Router from aiogram.utils.warnings import CodeHasNoEffect @@ -122,3 +122,17 @@ class TestRouter: skip() with pytest.raises(SkipHandler, match="KABOOM"): skip("KABOOM") + + @pytest.mark.asyncio + async def test_global_filter_in_nested_router(self): + r1 = Router() + r2 = Router() + + async def handler(evt): + return evt + + r1.include_router(r2) + r1.message.filter(lambda evt: False) + r2.message.register(handler) + + assert await r1.propagate_event(update_type="message", event=None) is UNHANDLED