Error handlers now handle all errors (#1483)

* python3.4 is no longer supported

* Prepare CHANGES.RST & README.rst for v12.0.0 release

* CHANGES.rst: small fix

* Add Bibo-Joshi to Credits

* improving error_handler

* fixing affected tests
This commit is contained in:
Poolitzer 2019-08-27 00:09:02 -07:00 committed by Eldinnie
parent f379a34ccd
commit 2c92c356b8
7 changed files with 111 additions and 46 deletions

View file

@ -2,7 +2,6 @@ language: python
matrix: matrix:
include: include:
- python: 2.7 - python: 2.7
- python: 3.4
- python: 3.5 - python: 3.5
- python: 3.6 - python: 3.6
- python: 3.7 - python: 3.7

View file

@ -4,7 +4,7 @@ Credits
``python-telegram-bot`` was originally created by ``python-telegram-bot`` was originally created by
`Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by `Leandro Toledo <https://github.com/leandrotoledo>`_ and is now maintained by
`Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram), `Jannes Höke <https://github.com/jh0ker>`_ (`@jh0ker <https://t.me/jh0ker>`_ on Telegram),
`Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_ and `Jasmin Bom <https://github.com/jsmnbom>`_. `Noam Meltzer <https://github.com/tsnoam>`_, `Pieter Schutz <https://github.com/eldinnie>`_, `Jasmin Bom <https://github.com/jsmnbom>`_ and `Hinrich Mahler <https://github.com/Bibo-Joshi>`_.
We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT We're vendoring urllib3 as part of ``python-telegram-bot`` which is distributed under the MIT
license. For more info, full credits & license terms, the sources can be found here: license. For more info, full credits & license terms, the sources can be found here:
@ -65,6 +65,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `Patrick Hofmann <https://github.com/PH89>`_ - `Patrick Hofmann <https://github.com/PH89>`_
- `Paul Larsen <https://github.com/PaulSonOfLars>`_ - `Paul Larsen <https://github.com/PaulSonOfLars>`_
- `Pieter Schutz <https://github.com/eldinnie>`_ - `Pieter Schutz <https://github.com/eldinnie>`_
- `Poolitzer <https://github.com/Poolitzer>`_
- `Rahiel Kasim <https://github.com/rahiel>`_ - `Rahiel Kasim <https://github.com/rahiel>`_
- `Sascha <https://github.com/saschalalala>`_ - `Sascha <https://github.com/saschalalala>`_
- `Shelomentsev D <https://github.com/shelomentsevd>`_ - `Shelomentsev D <https://github.com/shelomentsevd>`_

View file

@ -2,14 +2,13 @@
Changelog Changelog
========= =========
Version 12.0.0b1 Version 12.0.0
================ ================
*Released 2019-02-13* *Released 2019-08-24*
First beta release ever. Well... This felt like decades. But here we are with a new release.
It has been so long since last release that we would like to test the impact before a final release.
*We do NOT recommend using this beta release in production.* Expect minor releases soon (mainly complete Bot API 4.4 support)
**Major changes:** **Major changes:**
@ -18,6 +17,9 @@ It has been so long since last release that we would like to test the impact bef
- PrefixHandler added (Handler overhaul) - PrefixHandler added (Handler overhaul)
- Deprecation of RegexHandler and edited_messages, channel_post, etc. arguments (Filter overhaul) - Deprecation of RegexHandler and edited_messages, channel_post, etc. arguments (Filter overhaul)
- Various ConversationHandler changes and fixes - Various ConversationHandler changes and fixes
- Bot API 4.1, 4.2, 4.3 support
- Initial Bot API 4.4 support
- Python 3.4 is no longer supported
**See the wiki page at https://git.io/fxJuV for a detailed guide on how to migrate from version 11 to version 12.** **See the wiki page at https://git.io/fxJuV for a detailed guide on how to migrate from version 11 to version 12.**
@ -82,6 +84,13 @@ Bug fixes & improvements
- Allow SOCKSConnection to parse username and password from URL (`#1211`_) - Allow SOCKSConnection to parse username and password from URL (`#1211`_)
- Fix for arguments in passport/data.py (`#1213`_) - Fix for arguments in passport/data.py (`#1213`_)
- Improve message entity parsing by adding text_mention (`#1206`_) - Improve message entity parsing by adding text_mention (`#1206`_)
- Documentation fixes (`#1348`_, `#1397_`, `#1436`_)
- Merged filters short-circuit (`#1350`_)
- Fix webhook listen with tornado (`#1383`_)
- Call task_done() on update queue after update processing finished (`#1428`_)
- Fix send_location() - latitude may be 0 (`#1437`_)
- Make MessageEntity objects comparable (`#1465`_)
- Add prefix to thread names (`#1358`_)
.. _`#1100`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1100 .. _`#1100`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1100
.. _`#1283`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1283 .. _`#1283`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1283
@ -110,6 +119,15 @@ Bug fixes & improvements
.. _`#1319`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1319 .. _`#1319`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1319
.. _`#1343`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1343 .. _`#1343`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1343
.. _`#1270`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1270 .. _`#1270`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1270
.. _`#1348`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1348
.. _`#1350`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1350
.. _`#1383`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1383
.. _`#1397`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1397
.. _`#1428`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1428
.. _`#1436`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1436
.. _`#1437`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1437
.. _`#1465`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1465
.. _`#1358`: https://github.com/python-telegram-bot/python-telegram-bot/pull/1358
Internal improvements Internal improvements
--------------------- ---------------------

View file

@ -104,21 +104,7 @@ All types and methods of the Telegram Bot API **4.1** are supported.
Installing Installing
========== ==========
**Beta note** You can install or upgrade python-telegram-bot with:
The newest stable release is currently version 11.1.0.
The newest release is a beta release for version 12.
Install or upgrade with:
.. code:: shell
$ pip install python-telegram-bot==12.0.0b1 --upgrade
See CHANGES.rst for the changelog and make sure to report any bugs you find!
You can install or upgrade the stable python-telegram-bot with:
.. code:: shell .. code:: shell

View file

@ -328,15 +328,29 @@ class Dispatcher(object):
if self.persistence.store_chat_data and update.effective_chat: if self.persistence.store_chat_data and update.effective_chat:
chat_id = update.effective_chat.id chat_id = update.effective_chat.id
try: try:
self.persistence.update_chat_data(chat_id, self.chat_data[chat_id]) self.persistence.update_chat_data(chat_id,
except Exception: self.chat_data[chat_id])
self.logger.exception('Saving chat data raised an error') except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving chat data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
if self.persistence.store_user_data and update.effective_user: if self.persistence.store_user_data and update.effective_user:
user_id = update.effective_user.id user_id = update.effective_user.id
try: try:
self.persistence.update_user_data(user_id, self.user_data[user_id]) self.persistence.update_user_data(user_id,
except Exception: self.user_data[user_id])
self.logger.exception('Saving user data raised an error') except Exception as e:
try:
self.dispatch_error(update, e)
except Exception:
message = 'Saving user data raised an error and an ' \
'uncaught error was raised while handling ' \
'the error with an error_handler'
self.logger.exception(message)
# An error happened while polling # An error happened while polling
if isinstance(update, TelegramError): if isinstance(update, TelegramError):
@ -366,20 +380,17 @@ class Dispatcher(object):
break break
# Dispatch any error. # Dispatch any error.
except TelegramError as te: except Exception as e:
self.logger.warning('A TelegramError was raised while processing the Update')
try: try:
self.dispatch_error(update, te) self.dispatch_error(update, e)
except DispatcherHandlerStop: except DispatcherHandlerStop:
self.logger.debug('Error handler stopped further handlers') self.logger.debug('Error handler stopped further handlers')
break break
# Errors should not stop the thread.
except Exception: except Exception:
self.logger.exception('An uncaught error was raised while handling the error') self.logger.exception('An error was raised while processing the update and an '
'uncaught error was raised while handling the error '
# Errors should not stop the thread. 'with an error_handler')
except Exception:
self.logger.exception('An uncaught error was raised while processing the update')
def add_handler(self, handler, group=DEFAULT_GROUP): def add_handler(self, handler, group=DEFAULT_GROUP):
"""Register a handler. """Register a handler.
@ -453,11 +464,15 @@ class Dispatcher(object):
self.persistence.update_user_data(user_id, self.user_data[user_id]) self.persistence.update_user_data(user_id, self.user_data[user_id])
def add_error_handler(self, callback): def add_error_handler(self, callback):
"""Registers an error handler in the Dispatcher. """Registers an error handler in the Dispatcher. This handler will receive every error
which happens in your bot.
Warning: The errors handled within these handlers won't show up in the logger, so you
need to make sure that you reraise the error.
Args: Args:
callback (:obj:`callable`): The callback function for this error handler. Will be callback (:obj:`callable`): The callback function for this error handler. Will be
called when an error is raised Callback signature for context based API: called when an error is raised. Callback signature for context based API:
``def callback(update: Update, context: CallbackContext)`` ``def callback(update: Update, context: CallbackContext)``
@ -483,7 +498,7 @@ class Dispatcher(object):
Args: Args:
update (:obj:`str` | :class:`telegram.Update` | None): The update that caused the error update (:obj:`str` | :class:`telegram.Update` | None): The update that caused the error
error (:class:`telegram.TelegramError`): The Telegram error that was raised. error (:obj:`Exception`): The error that was raised.
""" """
if self.error_handlers: if self.error_handlers:

View file

@ -24,10 +24,12 @@ from time import sleep
import pytest import pytest
from telegram import TelegramError, Message, User, Chat, Update, Bot, MessageEntity from telegram import TelegramError, Message, User, Chat, Update, Bot, MessageEntity
from telegram.ext import MessageHandler, Filters, CommandHandler, CallbackContext, JobQueue from telegram.ext import (MessageHandler, Filters, CommandHandler, CallbackContext,
JobQueue, BasePersistence)
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.deprecate import TelegramDeprecationWarning
from tests.conftest import create_dp from tests.conftest import create_dp
from collections import defaultdict
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
@ -276,10 +278,11 @@ class TestDispatcher(object):
def test_exception_in_handler(self, dp, bot): def test_exception_in_handler(self, dp, bot):
passed = [] passed = []
err = Exception('General exception')
def start1(b, u): def start1(b, u):
passed.append('start1') passed.append('start1')
raise Exception('General exception') raise err
def start2(b, u): def start2(b, u):
passed.append('start2') passed.append('start2')
@ -298,14 +301,14 @@ class TestDispatcher(object):
bot=bot)) bot=bot))
# If an unhandled exception was caught, no further handlers from the same group should be # If an unhandled exception was caught, no further handlers from the same group should be
# called. # called. Also, the error handler should be called and receive the exception
passed = [] passed = []
dp.add_handler(CommandHandler('start', start1), 1) dp.add_handler(CommandHandler('start', start1), 1)
dp.add_handler(CommandHandler('start', start2), 1) dp.add_handler(CommandHandler('start', start2), 1)
dp.add_handler(CommandHandler('start', start3), 2) dp.add_handler(CommandHandler('start', start3), 2)
dp.add_error_handler(error) dp.add_error_handler(error)
dp.process_update(update) dp.process_update(update)
assert passed == ['start1', 'start3'] assert passed == ['start1', 'error', err, 'start3']
def test_telegram_error_in_handler(self, dp, bot): def test_telegram_error_in_handler(self, dp, bot):
passed = [] passed = []
@ -341,6 +344,49 @@ class TestDispatcher(object):
assert passed == ['start1', 'error', err, 'start3'] assert passed == ['start1', 'error', err, 'start3']
assert passed[2] is err assert passed[2] is err
def test_error_while_saving_chat_data(self, dp, bot):
increment = []
class OwnPersistence(BasePersistence):
def __init__(self):
super(BasePersistence, self).__init__()
self.store_user_data = True
self.store_chat_data = True
def get_chat_data(self):
return defaultdict(dict)
def update_chat_data(self, chat_id, data):
raise Exception
def get_user_data(self):
return defaultdict(dict)
def update_user_data(self, user_id, data):
raise Exception
def start1(b, u):
pass
def error(b, u, e):
increment.append("error")
# If updating a user_data or chat_data from a persistence object throws an error,
# the error handler should catch it
update = Update(1, message=Message(1, User(1, "Test", False), None, Chat(1, "lala"),
text='/start',
entities=[MessageEntity(type=MessageEntity.BOT_COMMAND,
offset=0,
length=len('/start'))],
bot=bot))
my_persistence = OwnPersistence()
dp = Dispatcher(bot, None, persistence=my_persistence)
dp.add_handler(CommandHandler('start', start1))
dp.add_error_handler(error)
dp.process_update(update)
assert increment == ["error", "error"]
def test_flow_stop_in_error_handler(self, dp, bot): def test_flow_stop_in_error_handler(self, dp, bot):
passed = [] passed = []
err = TelegramError('Telegram error') err = TelegramError('Telegram error')

View file

@ -176,10 +176,10 @@ class TestBasePersistence(object):
with caplog.at_level(logging.ERROR): with caplog.at_level(logging.ERROR):
dp.process_update(u) dp.process_update(u)
rec = caplog.records[-1] rec = caplog.records[-1]
assert rec.msg == 'Saving user data raised an error' assert rec.msg == 'No error handlers are registered, logging exception.'
assert rec.levelname == 'ERROR' assert rec.levelname == 'ERROR'
rec = caplog.records[-2] rec = caplog.records[-2]
assert rec.msg == 'Saving chat data raised an error' assert rec.msg == 'No error handlers are registered, logging exception.'
assert rec.levelname == 'ERROR' assert rec.levelname == 'ERROR'
m.from_user = user2 m.from_user = user2
m.chat = chat1 m.chat = chat1