From 1a76d864287665363ee75b64e9fd162e11d7a262 Mon Sep 17 00:00:00 2001 From: Alex Root Junior Date: Sun, 14 Aug 2022 17:01:16 +0300 Subject: [PATCH] Deprecate filters factory --- aiogram/dispatcher/event/telegram.py | 44 ++++++++++++++----- aiogram/filters/__init__.py | 3 ++ .../test_event/test_telegram.py | 13 +++++- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/aiogram/dispatcher/event/telegram.py b/aiogram/dispatcher/event/telegram.py index 8dd438bc..d5170594 100644 --- a/aiogram/dispatcher/event/telegram.py +++ b/aiogram/dispatcher/event/telegram.py @@ -11,6 +11,7 @@ from aiogram.dispatcher.middlewares.manager import MiddlewareManager from aiogram.filters.base import BaseFilter from ...exceptions import FiltersResolveError +from ...filters import BUILTIN_FILTERS_SET from ...types import TelegramObject from .bases import REJECTED, UNHANDLED, MiddlewareType, SkipHandler from .handler import CallbackType, FilterObject, HandlerObject @@ -24,7 +25,7 @@ class TelegramEventObserver: Event observer for Telegram events Here you can register handler with filters or bounded filters which can be used as keyword arguments instead of writing full references when you register new handlers. - This observer will stops event propagation when first handler is pass. + This observer will stop event propagation when first handler is pass. """ def __init__(self, router: Router, event_name: str) -> None: @@ -41,14 +42,16 @@ class TelegramEventObserver: # with dummy callback which never will be used self._handler = HandlerObject(callback=lambda: True, filters=[]) - def filter(self, *filters: CallbackType, **bound_filters: Any) -> None: + def filter(self, *filters: CallbackType, _stacklevel: int = 2, **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(filters, bound_filters) + resolved_filters = self.resolve_filters( + filters, bound_filters, _stacklevel=_stacklevel + 1 + ) if self._handler.filters is None: self._handler.filters = [] self._handler.filters.extend( @@ -67,14 +70,18 @@ class TelegramEventObserver: :param bound_filter: """ - # TODO: This functionality should be deprecated in the future - # in due to bound filter has uncontrollable ordering and - # makes debugging process is harder that explicit using filters - if not isclass(bound_filter) or not issubclass(bound_filter, BaseFilter): raise TypeError( "bound_filter() argument 'bound_filter' must be subclass of BaseFilter" ) + if bound_filter not in BUILTIN_FILTERS_SET: + warnings.warn( + category=DeprecationWarning, + message="filters factory deprecated and will be removed in 3.0b5," + " use filters directly instead (Example: " + f"`{bound_filter.__name__}(=)` instead of `=`)", + stacklevel=2, + ) self.filters.append(bound_filter) def _resolve_filters_chain(self) -> Generator[Type[BaseFilter], None, None]: @@ -106,6 +113,7 @@ class TelegramEventObserver: filters: Tuple[CallbackType, ...], full_config: Dict[str, Any], ignore_default: bool = True, + _stacklevel: int = 2, ) -> List[BaseFilter]: """ Resolve keyword filters via filters factory @@ -164,11 +172,11 @@ class TelegramEventObserver: if bound_filters: warnings.warn( category=DeprecationWarning, - message="Filters factory deprecated and will be removed in Beta 5. " + message="Filters factory deprecated and will be removed in 3.0b5.\n" "Use filters directly, for example instead of " "`@router.message(commands=['help']')` " "use `@router.message(Command(commands=['help'])`", - stacklevel=3, + stacklevel=_stacklevel, ) return bound_filters @@ -177,6 +185,7 @@ class TelegramEventObserver: callback: CallbackType, *filters: CallbackType, flags: Optional[Dict[str, Any]] = None, + _stacklevel: int = 2, **bound_filters: Any, ) -> CallbackType: """ @@ -184,7 +193,12 @@ class TelegramEventObserver: """ if flags is None: flags = {} - resolved_filters = self.resolve_filters(filters, bound_filters, ignore_default=False) + resolved_filters = self.resolve_filters( + filters, + bound_filters, + ignore_default=False, + _stacklevel=_stacklevel + 1, + ) for resolved_filter in resolved_filters: resolved_filter.update_handler_flags(flags=flags) self.handlers.append( @@ -238,14 +252,20 @@ class TelegramEventObserver: return UNHANDLED def __call__( - self, *args: CallbackType, flags: Optional[Dict[str, Any]] = None, **bound_filters: Any + self, + *args: CallbackType, + flags: Optional[Dict[str, Any]] = None, + _stacklevel: int = 2, + **bound_filters: Any, ) -> Callable[[CallbackType], CallbackType]: """ Decorator for registering event handlers """ def wrapper(callback: CallbackType) -> CallbackType: - self.register(callback, *args, flags=flags, **bound_filters) + self.register( + callback, *args, flags=flags, **bound_filters, _stacklevel=_stacklevel + 1 + ) return callback return wrapper diff --git a/aiogram/filters/__init__.py b/aiogram/filters/__init__.py index 6c73bacf..2626d51e 100644 --- a/aiogram/filters/__init__.py +++ b/aiogram/filters/__init__.py @@ -1,3 +1,4 @@ +from itertools import chain from typing import Dict, Tuple, Type from .base import BaseFilter @@ -134,3 +135,5 @@ BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = { *_ALL_EVENTS_FILTERS, ), } + +BUILTIN_FILTERS_SET = set(chain.from_iterable(BUILTIN_FILTERS.values())) diff --git a/tests/test_dispatcher/test_event/test_telegram.py b/tests/test_dispatcher/test_event/test_telegram.py index a03d83d7..ebb4ae87 100644 --- a/tests/test_dispatcher/test_event/test_telegram.py +++ b/tests/test_dispatcher/test_event/test_telegram.py @@ -9,8 +9,9 @@ from aiogram.dispatcher.event.handler import HandlerObject from aiogram.dispatcher.event.telegram import TelegramEventObserver from aiogram.dispatcher.router import Router from aiogram.exceptions import FiltersResolveError -from aiogram.filters import BaseFilter +from aiogram.filters import BaseFilter, Command from aiogram.types import Chat, Message, User +from tests.deprecated import check_deprecated pytestmark = pytest.mark.asyncio @@ -368,3 +369,13 @@ class TestTelegramEventObserver: r2.message.register(handler) assert await r1.message.trigger(None) is REJECTED + + def test_deprecated_bind_filter(self): + router = Router() + with check_deprecated("3.0b5", exception=AttributeError): + router.message.bind_filter(MyFilter1) + + def test_deprecated_resolve_filters(self): + router = Router() + with check_deprecated("3.0b5", exception=AttributeError): + router.message.resolve_filters([Command], full_config={"commands": ["test"]})