Add Dispatcher.add_handlers (#2823)

Co-authored-by: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com>
This commit is contained in:
Harshil 2022-01-03 12:04:11 +04:00 committed by Hinrich Mahler
parent 9a8c76fc2b
commit 5891db2f6b
3 changed files with 110 additions and 15 deletions

View file

@ -36,10 +36,12 @@ from typing import (
Generic,
TypeVar,
TYPE_CHECKING,
Tuple,
)
from uuid import uuid4
from telegram import Update
from telegram._utils.types import DVInput
from telegram.error import TelegramError
from telegram.ext import BasePersistence, ContextTypes, ExtBot
from telegram.ext._handler import Handler
@ -98,7 +100,9 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
:meth:`builder` (for convenience).
.. versionchanged:: 14.0
Initialization is now done through the :class:`telegram.ext.DispatcherBuilder`.
* Initialization is now done through the :class:`telegram.ext.DispatcherBuilder`.
* Removed the attribute ``groups``.
Attributes:
bot (:class:`telegram.Bot`): The bot object that should be passed to the handlers.
@ -120,11 +124,7 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
handler group to the list of handlers registered to that group.
.. seealso::
:meth:`add_handler`
groups (List[:obj:`int`]): A list of all handler groups that have handlers registered.
.. seealso::
:meth:`add_handler`
:meth:`add_handler`, :meth:`add_handlers`.
error_handlers (Dict[:obj:`callable`, :obj:`bool`]): A dict, where the keys are error
handlers and the values indicate whether they are to be run asynchronously via
:meth:`run_async`.
@ -149,7 +149,6 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
'bot_data',
'_update_persistence_lock',
'handlers',
'groups',
'error_handlers',
'running',
'__stop_event',
@ -243,7 +242,6 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
self.persistence = None
self.handlers: Dict[int, List[Handler]] = {}
self.groups: List[int] = []
self.error_handlers: Dict[Callable, Union[bool, DefaultValue]] = {}
self.running = False
@ -482,9 +480,9 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
handled = False
sync_modes = []
for group in self.groups:
for handlers in self.handlers.values():
try:
for handler in self.handlers[group]:
for handler in handlers:
check = handler.check_update(update)
if check is not None and check is not False:
if not context:
@ -573,11 +571,53 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
if group not in self.handlers:
self.handlers[group] = []
self.groups.append(group)
self.groups = sorted(self.groups)
self.handlers = dict(sorted(self.handlers.items())) # lower -> higher groups
self.handlers[group].append(handler)
def add_handlers(
self,
handlers: Union[
Union[List[Handler], Tuple[Handler]], Dict[int, Union[List[Handler], Tuple[Handler]]]
],
group: DVInput[int] = DefaultValue(0),
) -> None:
"""Registers multiple handlers at once. The order of the handlers in the passed
sequence(s) matters. See :meth:`add_handler` for details.
.. versionadded:: 14.0
.. seealso:: :meth:`add_handler`
Args:
handlers (List[:obj:`telegram.ext.Handler`] | \
Dict[int, List[:obj:`telegram.ext.Handler`]]): \
Specify a sequence of handlers *or* a dictionary where the keys are groups and
values are handlers.
group (:obj:`int`, optional): Specify which group the sequence of ``handlers``
should be added to. Defaults to ``0``.
"""
if isinstance(handlers, dict) and not isinstance(group, DefaultValue):
raise ValueError('The `group` argument can only be used with a sequence of handlers.')
if isinstance(handlers, dict):
for handler_group, grp_handlers in handlers.items():
if not isinstance(grp_handlers, (list, tuple)):
raise ValueError(f'Handlers for group {handler_group} must be a list or tuple')
for handler in grp_handlers:
self.add_handler(handler, handler_group)
elif isinstance(handlers, (list, tuple)):
for handler in handlers:
self.add_handler(handler, DefaultValue.get_value(group))
else:
raise ValueError(
"The `handlers` argument must be a sequence of handlers or a "
"dictionary where the keys are groups and values are sequences of handlers."
)
def remove_handler(self, handler: Handler, group: int = DEFAULT_GROUP) -> None:
"""Remove a handler from the specified group.
@ -590,7 +630,6 @@ class Dispatcher(Generic[BT, CCT, UD, CD, BD, JQ, PT]):
self.handlers[group].remove(handler)
if not self.handlers[group]:
del self.handlers[group]
self.groups.remove(group)
def update_persistence(self, update: object = None) -> None:
"""Update :attr:`user_data`, :attr:`chat_data` and :attr:`bot_data` in :attr:`persistence`.

View file

@ -199,7 +199,6 @@ def dp(_dp):
_dp.bot_data = {}
_dp.persistence = None
_dp.handlers = {}
_dp.groups = []
_dp.error_handlers = {}
_dp.exception_event = Event()
_dp.__stop_event = Event()

View file

@ -80,7 +80,7 @@ class TestDispatcher:
self.count += 1
def callback_set_count(self, count):
def callback(bot, update):
def callback(update, context):
self.count = count
return callback
@ -413,6 +413,63 @@ class TestDispatcher:
sleep(0.1)
assert self.count == 3
def test_add_handlers_complex(self, dp):
"""Tests both add_handler & add_handlers together & confirms the correct insertion order"""
msg_handler_set_count = MessageHandler(filters.TEXT, self.callback_set_count(1))
msg_handler_inc_count = MessageHandler(filters.PHOTO, self.callback_increase_count)
dp.add_handler(msg_handler_set_count, 1)
dp.add_handlers((msg_handler_inc_count, msg_handler_inc_count), 1)
photo_update = Update(2, message=Message(2, None, None, photo=True))
dp.update_queue.put(self.message_update) # Putting updates in the queue calls the callback
dp.update_queue.put(photo_update)
sleep(0.1) # sleep is required otherwise there is random behaviour
# Test if handler was added to correct group with correct order-
assert (
self.count == 2
and len(dp.handlers[1]) == 3
and dp.handlers[1][0] is msg_handler_set_count
)
# Now lets test add_handlers when `handlers` is a dict-
voice_filter_handler_to_check = MessageHandler(filters.VOICE, self.callback_increase_count)
dp.add_handlers(
handlers={
1: [
MessageHandler(filters.USER, self.callback_increase_count),
voice_filter_handler_to_check,
],
-1: [MessageHandler(filters.CAPTION, self.callback_set_count(2))],
}
)
user_update = Update(3, message=Message(3, None, None, from_user=User(1, 's', True)))
voice_update = Update(4, message=Message(4, None, None, voice=True))
dp.update_queue.put(user_update)
dp.update_queue.put(voice_update)
sleep(0.1)
assert (
self.count == 4
and len(dp.handlers[1]) == 5
and dp.handlers[1][-1] is voice_filter_handler_to_check
)
dp.update_queue.put(Update(5, message=Message(5, None, None, caption='cap')))
sleep(0.1)
assert self.count == 2 and len(dp.handlers[-1]) == 1
# Now lets test the errors which can be produced-
with pytest.raises(ValueError, match="The `group` argument"):
dp.add_handlers({2: [msg_handler_set_count]}, group=0)
with pytest.raises(ValueError, match="Handlers for group 3"):
dp.add_handlers({3: msg_handler_set_count})
with pytest.raises(ValueError, match="The `handlers` argument must be a sequence"):
dp.add_handlers({msg_handler_set_count})
def test_add_handler_errors(self, dp):
handler = 'not a handler'
with pytest.raises(TypeError, match='handler is not an instance of'):