Skip to content

Commit 16a49ec

Browse files
authored
Remove DispatcherHandlerContinue + more unitests for dispatcher (python-telegram-bot#792)
The idea was nice, but it really complicated things for us and for the user. If a user wants to run more than one handler on an update, he can put the handlers in different groups or he can have a single handler. If a user wants to have multiple handlers in the same group which only one of them should run on the update, he should use check_update(). Since we haven't released this code yet, there's no problem with backward compatability.
1 parent ee34d57 commit 16a49ec

File tree

3 files changed

+117
-79
lines changed

3 files changed

+117
-79
lines changed

telegram/ext/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# along with this program. If not, see [http://www.gnu.org/licenses/].
1919
"""Extensions over the Telegram Bot API to facilitate bot making"""
2020

21-
from .dispatcher import Dispatcher, DispatcherHandlerContinue, DispatcherHandlerStop, run_async
21+
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
2222
from .jobqueue import JobQueue, Job
2323
from .updater import Updater
2424
from .callbackqueryhandler import CallbackQueryHandler
@@ -43,4 +43,4 @@
4343
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
4444
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
4545
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
46-
'DispatcherHandlerContinue', 'DispatcherHandlerStop', 'run_async')
46+
'DispatcherHandlerStop', 'run_async')

telegram/ext/dispatcher.py

Lines changed: 38 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -55,25 +55,8 @@ def async_func(*args, **kwargs):
5555
return async_func
5656

5757

58-
class DispatcherHandlerFlow(Exception):
59-
"""
60-
Dispatcher update processing manipulation exceptions are base on this class.
61-
"""
62-
pass
63-
64-
65-
class DispatcherHandlerContinue(DispatcherHandlerFlow):
66-
"""
67-
If check Handler's check_updated returned true, but execution of handler raised this,
68-
then handlers checking will continue.
69-
"""
70-
pass
71-
72-
73-
class DispatcherHandlerStop(DispatcherHandlerFlow):
74-
"""
75-
Raise this in handler to prevent execution any other handlers (even in different group).
76-
"""
58+
class DispatcherHandlerStop(Exception):
59+
"""Raise this in handler to prevent execution any other handler (even in different group)."""
7760
pass
7861

7962

@@ -178,9 +161,10 @@ def _pooled(self):
178161
break
179162

180163
promise.run()
181-
if isinstance(promise.exception, DispatcherHandlerFlow):
182-
self.logger.warning('DispatcherHandlerFlow is not supported with async '
183-
'functions; func: %s', promise.pooled_function.__name__)
164+
if isinstance(promise.exception, DispatcherHandlerStop):
165+
self.logger.warning(
166+
'DispatcherHandlerStop is not supported with async functions; func: %s',
167+
promise.pooled_function.__name__)
184168

185169
def run_async(self, func, *args, **kwargs):
186170
"""
@@ -284,63 +268,54 @@ def process_update(self, update):
284268
return
285269
for group in self.groups:
286270
try:
287-
for handler in self.handlers[group]:
288-
try:
289-
if handler.check_update(update):
290-
try:
291-
handler.handle_update(update, self)
292-
except DispatcherHandlerContinue:
293-
continue
294-
break
295-
except DispatcherHandlerFlow:
296-
raise
297-
except TelegramError as te:
298-
self.logger.warning('A TelegramError was raised while processing the '
299-
'Update.')
300-
301-
try:
302-
self.dispatch_error(update, te)
303-
except Exception:
304-
self.logger.exception('An uncaught error was raised while '
305-
'handling the error')
306-
finally:
307-
break
308-
309-
# Errors should not stop the thread
310-
except Exception:
311-
self.logger.exception('An uncaught error was raised while '
312-
'processing the update')
313-
break
271+
for handler in (x for x in self.handlers[group] if x.check_update(update)):
272+
handler.handle_update(update, self)
273+
break
274+
275+
# Stop processing with any other handler.
314276
except DispatcherHandlerStop:
277+
self.logger.debug('Stopping further handlers due to DispatcherHandlerStop')
315278
break
316279

280+
# Dispatch any error.
281+
except TelegramError as te:
282+
self.logger.warning('A TelegramError was raised while processing the Update')
283+
284+
try:
285+
self.dispatch_error(update, te)
286+
except DispatcherHandlerStop:
287+
self.logger.debug('Error handler stopped further handlers')
288+
break
289+
except Exception:
290+
self.logger.exception('An uncaught error was raised while handling the error')
291+
292+
# Errors should not stop the thread.
293+
except Exception:
294+
self.logger.exception('An uncaught error was raised while processing the update')
295+
317296
def add_handler(self, handler, group=DEFAULT_GROUP):
318-
"""
319-
Register a handler.
297+
"""Register a handler.
320298
321-
TL;DR: Order and priority counts. 0 or 1 handlers per group will be
322-
used.
299+
TL;DR: Order and priority counts. 0 or 1 handlers per group will be used.
323300
324301
A handler must be an instance of a subclass of :class:`telegram.ext.Handler`. All handlers
325302
are organized in groups with a numeric value. The default group is 0. All groups will be
326-
evaluated for handling an update, but only 0 or 1 handler per group will be used,
327-
except situations when :class:`telegram.DispatcherHandlerContinue` or
328-
:class:`telegram.DispatcherHandlerStop` were raised.
303+
evaluated for handling an update, but only 0 or 1 handler per group will be used. If
304+
:class:`telegram.DispatcherHandlerStop` is raised from one of the handlers, no further
305+
handlers (regardless of the group) will be called.
329306
330307
The priority/order of handlers is determined as follows:
331308
332309
* Priority of the group (lower group number == higher priority)
333-
* The first handler in a group which should handle an update will be
334-
used. Other handlers from the group will not be used. The order in
335-
which handlers were added to the group defines the priority.
336-
* If :class:`telegram.DispatcherHandlerContinue` was raised, then next handler in the
337-
same group will be called.
338-
* If :class:`telegram.DispatcherHandlerStop` was raised, then zero handlers (even
339-
from other groups) will called.
310+
* The first handler in a group which should handle an update (see
311+
:method:`telegram.ext.Handler.check_update`) will be used. Other handlers from the
312+
group will not be used. The order in which handlers were added to the group defines the
313+
priority.
340314
341315
Args:
342316
handler (:class:`telegram.ext.Handler`): A Handler instance.
343317
group (:obj:`int`, optional): The group identifier. Default is 0.
318+
344319
"""
345320

346321
if not isinstance(handler, Handler):

tests/test_dispatcher.py

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424

2525
from telegram import TelegramError, Message, User, Chat, Update
2626
from telegram.ext import MessageHandler, Filters, CommandHandler
27-
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerContinue, \
28-
DispatcherHandlerStop
27+
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
2928
from tests.conftest import create_dp
3029

3130

@@ -173,12 +172,12 @@ def test_add_handler_errors(self, dp):
173172
with pytest.raises(TypeError, match='group is not int'):
174173
dp.add_handler(handler, 'one')
175174

176-
def test_handler_flow_continue(self, bot, dp):
175+
def test_flow_stop(self, dp, bot):
177176
passed = []
178177

179178
def start1(b, u):
180179
passed.append('start1')
181-
raise DispatcherHandlerContinue
180+
raise DispatcherHandlerStop
182181

183182
def start2(b, u):
184183
passed.append('start2')
@@ -192,19 +191,20 @@ def error(b, u, e):
192191

193192
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
194193

195-
# If Continue raised next handler should be proceed.
194+
# If Stop raised handlers in other groups should not be called.
196195
passed = []
197-
dp.add_handler(CommandHandler('start', start1))
198-
dp.add_handler(CommandHandler('start', start2))
196+
dp.add_handler(CommandHandler('start', start1), 1)
197+
dp.add_handler(CommandHandler('start', start3), 1)
198+
dp.add_handler(CommandHandler('start', start2), 2)
199199
dp.process_update(update)
200-
assert passed == ['start1', 'start2']
200+
assert passed == ['start1']
201201

202-
def test_dispatcher_handler_flow_stop(self, dp, bot):
202+
def test_exception_in_handler(self, dp, bot):
203203
passed = []
204204

205205
def start1(b, u):
206206
passed.append('start1')
207-
raise DispatcherHandlerStop
207+
raise Exception('General exception')
208208

209209
def start2(b, u):
210210
passed.append('start2')
@@ -218,10 +218,73 @@ def error(b, u, e):
218218

219219
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
220220

221-
# If Stop raised handlers in other groups should not be called.
221+
# If an unhandled exception was caught, no further handlers from the same group should be
222+
# called.
222223
passed = []
223224
dp.add_handler(CommandHandler('start', start1), 1)
224-
dp.add_handler(CommandHandler('start', start3), 1)
225-
dp.add_handler(CommandHandler('start', start2), 2)
225+
dp.add_handler(CommandHandler('start', start2), 1)
226+
dp.add_handler(CommandHandler('start', start3), 2)
227+
dp.add_error_handler(error)
226228
dp.process_update(update)
227-
assert passed == ['start1']
229+
assert passed == ['start1', 'start3']
230+
231+
def test_telegram_error_in_handler(self, dp, bot):
232+
passed = []
233+
err = TelegramError('Telegram error')
234+
235+
def start1(b, u):
236+
passed.append('start1')
237+
raise err
238+
239+
def start2(b, u):
240+
passed.append('start2')
241+
242+
def start3(b, u):
243+
passed.append('start3')
244+
245+
def error(b, u, e):
246+
passed.append('error')
247+
passed.append(e)
248+
249+
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
250+
251+
# If a TelegramException was caught, an error handler should be called and no further
252+
# handlers from the same group should be called.
253+
dp.add_handler(CommandHandler('start', start1), 1)
254+
dp.add_handler(CommandHandler('start', start2), 1)
255+
dp.add_handler(CommandHandler('start', start3), 2)
256+
dp.add_error_handler(error)
257+
dp.process_update(update)
258+
assert passed == ['start1', 'error', err, 'start3']
259+
assert passed[2] is err
260+
261+
def test_flow_stop_in_error_handler(self, dp, bot):
262+
passed = []
263+
err = TelegramError('Telegram error')
264+
265+
def start1(b, u):
266+
passed.append('start1')
267+
raise err
268+
269+
def start2(b, u):
270+
passed.append('start2')
271+
272+
def start3(b, u):
273+
passed.append('start3')
274+
275+
def error(b, u, e):
276+
passed.append('error')
277+
passed.append(e)
278+
raise DispatcherHandlerStop
279+
280+
update = Update(1, message=Message(1, None, None, None, text='/start', bot=bot))
281+
282+
# If a TelegramException was caught, an error handler should be called and no further
283+
# handlers from the same group should be called.
284+
dp.add_handler(CommandHandler('start', start1), 1)
285+
dp.add_handler(CommandHandler('start', start2), 1)
286+
dp.add_handler(CommandHandler('start', start3), 2)
287+
dp.add_error_handler(error)
288+
dp.process_update(update)
289+
assert passed == ['start1', 'error', err]
290+
assert passed[2] is err

0 commit comments

Comments
 (0)