mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-06 07:50:32 +00:00
Deprecate filters factory (#976)
* Deprecate filters factory * Added changelog * Update filters usage in docs and examples
This commit is contained in:
parent
c1341ba2df
commit
0e0dbe7e59
11 changed files with 67 additions and 23 deletions
1
CHANGES/942.misc.rst
Normal file
1
CHANGES/942.misc.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
Deprecated filters factory. It will be removed in next Beta (3.0b5)
|
||||
|
|
@ -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__}(<argument>=<value>)` instead of `<argument>=<value>`)",
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
|
|
|
|||
|
|
@ -86,10 +86,10 @@ Handle user leave or join events
|
|||
|
||||
from aiogram.filters import IS_MEMBER, IS_NOT_MEMBER
|
||||
|
||||
@router.chat_member(member_status_changed=IS_MEMBER >> IS_NOT_MEMBER)
|
||||
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=IS_MEMBER >> IS_NOT_MEMBER))
|
||||
async def on_user_leave(event: ChatMemberUpdated): ...
|
||||
|
||||
@router.chat_member(member_status_changed=IS_NOT_MEMBER >> IS_MEMBER)
|
||||
@router.chat_member(ChatMemberUpdatedFilter(member_status_changed=IS_NOT_MEMBER >> IS_MEMBER))
|
||||
async def on_user_join(event: ChatMemberUpdated): ...
|
||||
|
||||
Or construct your own terms via using pre-defined set of statuses and transitions.
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ Usage
|
|||
1. Filter single variant of commands: :code:`Command(commands=["start"])` or :code:`Command(commands="start")`
|
||||
2. Handle command by regexp pattern: :code:`Command(commands=[re.compile(r"item_(\d+)")])`
|
||||
3. Match command by multiple variants: :code:`Command(commands=["item", re.compile(r"item_(\d+)")])`
|
||||
4. Handle commands in public chats intended for other bots: :code:`Command(commands=["command"], commands)`
|
||||
5. As keyword argument in registerer: :code:`@router.message(commands=["help"])`
|
||||
4. Handle commands in public chats intended for other bots: :code:`Command(commands=["command"], commands_ignore_mention=True)`
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
Filtering events
|
||||
================
|
||||
|
||||
|
||||
.. danger::
|
||||
|
||||
Note that the design of filters will be changed in 3.0b5
|
||||
|
||||
`Read more >> <https://github.com/aiogram/aiogram/issues/942>`_
|
||||
|
||||
Filters is needed for routing updates to the specific handler.
|
||||
Searching of handler is always stops on first match set of filters are pass.
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Or used from filters factory by passing corresponding arguments to handler regis
|
|||
Usage
|
||||
=====
|
||||
|
||||
#. :code:`magic_data=F.event.from_user.id == F.config.admin_id` (Note that :code:`config` should be passed from middleware)
|
||||
#. :code:`MagicData(magic_data=F.event.from_user.id == F.config.admin_id)` (Note that :code:`config` should be passed from middleware)
|
||||
|
||||
|
||||
Allowed handlers
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
|
||||
from aiogram import Bot, Dispatcher, types
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import Message
|
||||
|
||||
TOKEN = "42:TOKEN"
|
||||
|
|
@ -9,7 +10,7 @@ dp = Dispatcher()
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dp.message(commands=["start"])
|
||||
@dp.message(Command(commands=["start"]))
|
||||
async def command_start_handler(message: Message) -> None:
|
||||
"""
|
||||
This handler receive messages with `/start` command
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from os import getenv
|
|||
from typing import Any, Dict
|
||||
|
||||
from aiogram import Bot, Dispatcher, F, Router, html
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import KeyboardButton, Message, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
||||
|
|
@ -18,7 +19,7 @@ class Form(StatesGroup):
|
|||
language = State()
|
||||
|
||||
|
||||
@form_router.message(commands=["start"])
|
||||
@form_router.message(Command(commands=["start"]))
|
||||
async def command_start(message: Message, state: FSMContext) -> None:
|
||||
await state.set_state(Form.name)
|
||||
await message.answer(
|
||||
|
|
@ -27,7 +28,7 @@ async def command_start(message: Message, state: FSMContext) -> None:
|
|||
)
|
||||
|
||||
|
||||
@form_router.message(commands=["cancel"])
|
||||
@form_router.message(Command(commands=["cancel"]))
|
||||
@form_router.message(F.text.casefold() == "cancel")
|
||||
async def cancel_handler(message: Message, state: FSMContext) -> None:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
|
||||
from aiogram import Bot, Dispatcher, Router
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import (
|
||||
CallbackQuery,
|
||||
ChatMemberUpdated,
|
||||
|
|
@ -16,7 +17,7 @@ logger = logging.getLogger(__name__)
|
|||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
@dp.message(commands=["start"])
|
||||
@dp.message(Command(commands=["start"]))
|
||||
async def command_start_handler(message: Message) -> None:
|
||||
"""
|
||||
This handler receive messages with `/start` command
|
||||
|
|
@ -71,7 +72,7 @@ async def my_chat_member_change(chat_member: ChatMemberUpdated, bot: Bot) -> Non
|
|||
|
||||
|
||||
def main() -> None:
|
||||
# Initialize Bot instance with an default parse mode which will be passed to all API calls
|
||||
# Initialize Bot instance with a default parse mode which will be passed to all API calls
|
||||
bot = Bot(TOKEN, parse_mode="HTML")
|
||||
|
||||
sub_router.include_router(deep_dark_router)
|
||||
|
|
|
|||
|
|
@ -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"]})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue