Skip to content

Add Dispatcher.add_handlers #2823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 52 additions & 13 deletions telegram/ext/_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
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
Expand Down Expand Up @@ -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.
Expand All @@ -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`.
Expand All @@ -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',
Expand Down Expand Up @@ -243,7 +242,6 @@ def __init__(
self.persistence = None

self.handlers: Dict[int, List[Handler]] = {}
self.groups: List[int] = []
self.error_handlers: Dict[Callable, Union[bool, DefaultValue]] = {}

self.running = False
Expand Down Expand Up @@ -482,9 +480,9 @@ def process_update(self, update: object) -> None:
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:
Expand Down Expand Up @@ -573,11 +571,53 @@ def add_handler(self, handler: Handler[UT, CCT], group: int = DEFAULT_GROUP) ->

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.

Expand All @@ -590,7 +630,6 @@ def remove_handler(self, handler: Handler, group: int = DEFAULT_GROUP) -> None:
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`.
Expand Down
1 change: 0 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
59 changes: 58 additions & 1 deletion tests/test_dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def callback_increase_count(self, update, context):
self.count += 1

def callback_set_count(self, count):
def callback(bot, update):
def callback(update, context):
self.count = count

return callback
Expand Down Expand Up @@ -413,6 +413,63 @@ def test_groups(self, dp):
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'):
Expand Down