From f6497093ce5a37c7820ac20153d18e6ab51eedad Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Mon, 20 Sep 2021 10:45:42 +0400 Subject: [PATCH] Warnings Overhaul (#2662) --- docs/source/telegram.rst | 1 + docs/source/telegram.utils.warnings.rst | 8 +++ telegram/base.py | 12 +++-- telegram/bot.py | 9 ++-- telegram/ext/basepersistence.py | 34 ++++++------- telegram/ext/conversationhandler.py | 41 ++++++++------- telegram/ext/dispatcher.py | 13 ++--- telegram/ext/updater.py | 23 +++++---- telegram/utils/deprecate.py | 28 ---------- telegram/utils/warnings.py | 68 +++++++++++++++++++++++++ tests/conftest.py | 4 +- tests/test_conversationhandler.py | 50 ++++++++++++------ tests/test_dispatcher.py | 18 +++---- tests/test_persistence.py | 4 +- tests/test_telegramobject.py | 4 +- tests/test_updater.py | 14 ++++- 16 files changed, 207 insertions(+), 124 deletions(-) create mode 100644 docs/source/telegram.utils.warnings.rst delete mode 100644 telegram/utils/deprecate.py create mode 100644 telegram/utils/warnings.py diff --git a/docs/source/telegram.rst b/docs/source/telegram.rst index 1dc0a8574..6705fda97 100644 --- a/docs/source/telegram.rst +++ b/docs/source/telegram.rst @@ -182,3 +182,4 @@ utils telegram.utils.promise telegram.utils.request telegram.utils.types + telegram.utils.warnings diff --git a/docs/source/telegram.utils.warnings.rst b/docs/source/telegram.utils.warnings.rst new file mode 100644 index 000000000..1be541810 --- /dev/null +++ b/docs/source/telegram.utils.warnings.rst @@ -0,0 +1,8 @@ +:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/utils/warnings.py + +telegram.utils.warnings Module +=============================== + +.. automodule:: telegram.utils.warnings + :members: + :show-inheritance: diff --git a/telegram/base.py b/telegram/base.py index 3cb51785d..c3560929d 100644 --- a/telegram/base.py +++ b/telegram/base.py @@ -22,10 +22,10 @@ try: except ImportError: import json # type: ignore[no-redef] -import warnings from typing import TYPE_CHECKING, List, Optional, Type, TypeVar, Tuple from telegram.utils.types import JSONDict +from telegram.utils.warnings import warn if TYPE_CHECKING: from telegram import Bot @@ -140,14 +140,16 @@ class TelegramObject: # pylint: disable=no-member if isinstance(other, self.__class__): if self._id_attrs == (): - warnings.warn( + warn( f"Objects of type {self.__class__.__name__} can not be meaningfully tested for" - " equivalence." + " equivalence.", + stacklevel=2, ) if other._id_attrs == (): - warnings.warn( + warn( f"Objects of type {other.__class__.__name__} can not be meaningfully tested" - " for equivalence." + " for equivalence.", + stacklevel=2, ) return self._id_attrs == other._id_attrs return super().__eq__(other) diff --git a/telegram/bot.py b/telegram/bot.py index 24f254cfb..c0fbdc8af 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -21,7 +21,6 @@ import functools import logging -import warnings from datetime import datetime from typing import ( @@ -91,7 +90,7 @@ from telegram import ( ) from telegram.constants import MAX_INLINE_QUERY_RESULTS from telegram.error import InvalidToken, TelegramError -from telegram.utils.deprecate import TelegramDeprecationWarning +from telegram.utils.warnings import PTBDeprecationWarning, warn from telegram.utils.helpers import ( DEFAULT_NONE, DefaultValue, @@ -198,10 +197,10 @@ class Bot(TelegramObject): self.defaults = defaults if self.defaults: - warnings.warn( + warn( 'Passing Defaults to telegram.Bot is deprecated. Use telegram.ext.ExtBot instead.', - TelegramDeprecationWarning, - stacklevel=3, + PTBDeprecationWarning, + stacklevel=4, ) if base_url is None: diff --git a/telegram/ext/basepersistence.py b/telegram/ext/basepersistence.py index 764d27136..2186d4743 100644 --- a/telegram/ext/basepersistence.py +++ b/telegram/ext/basepersistence.py @@ -17,7 +17,6 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the BasePersistence class.""" -import warnings from abc import ABC, abstractmethod from copy import copy from typing import Dict, Optional, Tuple, cast, ClassVar, Generic, DefaultDict, NamedTuple @@ -26,6 +25,7 @@ from telegram import Bot import telegram.ext.extbot from telegram.ext.utils.types import UD, CD, BD, ConversationDict, CDCData +from telegram.utils.warnings import warn, PTBRuntimeWarning class PersistenceInput(NamedTuple): @@ -230,10 +230,10 @@ class BasePersistence(Generic[UD, CD, BD], ABC): return new_immutable if isinstance(obj, type): # classes usually do have a __dict__, but it's not writable - warnings.warn( - 'BasePersistence.replace_bot does not handle classes. See ' - 'the docs of BasePersistence.replace_bot for more information.', - RuntimeWarning, + warn( + f'BasePersistence.replace_bot does not handle classes such as {obj.__name__!r}. ' + 'See the docs of BasePersistence.replace_bot for more information.', + PTBRuntimeWarning, ) return obj @@ -241,10 +241,10 @@ class BasePersistence(Generic[UD, CD, BD], ABC): new_obj = copy(obj) memo[obj_id] = new_obj except Exception: - warnings.warn( + warn( 'BasePersistence.replace_bot does not handle objects that can not be copied. See ' 'the docs of BasePersistence.replace_bot for more information.', - RuntimeWarning, + PTBRuntimeWarning, ) memo[obj_id] = obj return obj @@ -282,10 +282,10 @@ class BasePersistence(Generic[UD, CD, BD], ABC): memo[obj_id] = new_obj return new_obj except Exception as exception: - warnings.warn( + warn( f'Parsing of an object failed with the following exception: {exception}. ' f'See the docs of BasePersistence.replace_bot for more information.', - RuntimeWarning, + PTBRuntimeWarning, ) memo[obj_id] = obj @@ -333,20 +333,20 @@ class BasePersistence(Generic[UD, CD, BD], ABC): return new_immutable if isinstance(obj, type): # classes usually do have a __dict__, but it's not writable - warnings.warn( - 'BasePersistence.insert_bot does not handle classes. See ' - 'the docs of BasePersistence.insert_bot for more information.', - RuntimeWarning, + warn( + f'BasePersistence.insert_bot does not handle classes such as {obj.__name__!r}. ' + 'See the docs of BasePersistence.insert_bot for more information.', + PTBRuntimeWarning, ) return obj try: new_obj = copy(obj) except Exception: - warnings.warn( + warn( 'BasePersistence.insert_bot does not handle objects that can not be copied. See ' 'the docs of BasePersistence.insert_bot for more information.', - RuntimeWarning, + PTBRuntimeWarning, ) memo[obj_id] = obj return obj @@ -384,10 +384,10 @@ class BasePersistence(Generic[UD, CD, BD], ABC): memo[obj_id] = new_obj return new_obj except Exception as exception: - warnings.warn( + warn( f'Parsing of an object failed with the following exception: {exception}. ' f'See the docs of BasePersistence.insert_bot for more information.', - RuntimeWarning, + PTBRuntimeWarning, ) memo[obj_id] = obj diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index 7e85f6723..30d96400a 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -20,7 +20,6 @@ """This module contains the ConversationHandler.""" import logging -import warnings import functools import datetime from threading import Lock @@ -39,6 +38,7 @@ from telegram.ext import ( from telegram.ext.utils.promise import Promise from telegram.ext.utils.types import ConversationDict from telegram.ext.utils.types import CCT +from telegram.utils.warnings import warn if TYPE_CHECKING: from telegram.ext import Dispatcher, Job @@ -259,9 +259,10 @@ class ConversationHandler(Handler[Update, CCT]): raise ValueError("'per_user', 'per_chat' and 'per_message' can't all be 'False'") if self.per_message and not self.per_chat: - warnings.warn( + warn( "If 'per_message=True' is used, 'per_chat=True' should also be used, " - "since message IDs are not globally unique." + "since message IDs are not globally unique.", + stacklevel=2, ) all_handlers: List[Handler] = [] @@ -274,37 +275,41 @@ class ConversationHandler(Handler[Update, CCT]): if self.per_message: for handler in all_handlers: if not isinstance(handler, CallbackQueryHandler): - warnings.warn( - "If 'per_message=True', all entry points and state handlers" + warn( + "If 'per_message=True', all entry points, state handlers, and fallbacks" " must be 'CallbackQueryHandler', since no other handlers " - "have a message context." + "have a message context.", + stacklevel=2, ) break else: for handler in all_handlers: if isinstance(handler, CallbackQueryHandler): - warnings.warn( + warn( "If 'per_message=False', 'CallbackQueryHandler' will not be " - "tracked for every message." + "tracked for every message.", + stacklevel=2, ) break if self.per_chat: for handler in all_handlers: if isinstance(handler, (InlineQueryHandler, ChosenInlineResultHandler)): - warnings.warn( + warn( "If 'per_chat=True', 'InlineQueryHandler' can not be used, " - "since inline queries have no chat context." + "since inline queries have no chat context.", + stacklevel=2, ) break if self.conversation_timeout: for handler in all_handlers: if isinstance(handler, self.__class__): - warnings.warn( + warn( "Using `conversation_timeout` with nested conversations is currently not " "supported. You can still try to use it, but it will likely behave " - "differently from what you expect." + "differently from what you expect.", + stacklevel=2, ) break @@ -644,8 +649,8 @@ class ConversationHandler(Handler[Update, CCT]): new_state, dispatcher, update, context, conversation_key ) else: - self.logger.warning( - "Ignoring `conversation_timeout` because the Dispatcher has no JobQueue." + warn( + "Ignoring `conversation_timeout` because the Dispatcher has no JobQueue.", ) if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent: @@ -680,9 +685,9 @@ class ConversationHandler(Handler[Update, CCT]): elif new_state is not None: if new_state not in self.states: - warnings.warn( + warn( f"Handler returned state {new_state} which is unknown to the " - f"ConversationHandler{' ' + self.name if self.name is not None else ''}." + f"ConversationHandler{' ' + self.name if self.name is not None else ''}.", ) with self._conversations_lock: self.conversations[key] = new_state @@ -711,9 +716,9 @@ class ConversationHandler(Handler[Update, CCT]): try: handler.handle_update(ctxt.update, ctxt.dispatcher, check, callback_context) except DispatcherHandlerStop: - self.logger.warning( + warn( 'DispatcherHandlerStop in TIMEOUT state of ' - 'ConversationHandler has no effect. Ignoring.' + 'ConversationHandler has no effect. Ignoring.', ) self._update_state(self.END, ctxt.conversation_key) diff --git a/telegram/ext/dispatcher.py b/telegram/ext/dispatcher.py index 54793ea67..d53862133 100644 --- a/telegram/ext/dispatcher.py +++ b/telegram/ext/dispatcher.py @@ -19,7 +19,6 @@ """This module contains the Dispatcher class.""" import logging -import warnings import weakref from collections import defaultdict from queue import Empty, Queue @@ -47,6 +46,7 @@ from telegram.ext.handler import Handler import telegram.ext.extbot from telegram.ext.callbackdatacache import CallbackDataCache from telegram.ext.utils.promise import Promise +from telegram.utils.warnings import warn from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE from telegram.ext.utils.types import CCT, UD, CD, BD @@ -199,8 +199,9 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): self.context_types = cast(ContextTypes[CCT, UD, CD, BD], context_types or ContextTypes()) if self.workers < 1: - warnings.warn( - 'Asynchronous callbacks can not be processed without at least one worker thread.' + warn( + 'Asynchronous callbacks can not be processed without at least one worker thread.', + stacklevel=2, ) self.user_data: DefaultDict[int, UD] = defaultdict(self.context_types.user_data) @@ -315,9 +316,9 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): continue if isinstance(promise.exception, DispatcherHandlerStop): - self.logger.warning( - 'DispatcherHandlerStop is not supported with async functions; func: %s', - promise.pooled_function.__name__, + warn( + 'DispatcherHandlerStop is not supported with async functions; ' + f'func: {promise.pooled_function.__name__}', ) continue diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index 96d481401..0a05fa915 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -20,7 +20,6 @@ import logging import ssl -import warnings from queue import Queue from signal import SIGABRT, SIGINT, SIGTERM, signal from threading import Event, Lock, Thread, current_thread @@ -42,7 +41,7 @@ from typing import ( from telegram import Bot, TelegramError from telegram.error import InvalidToken, RetryAfter, TimedOut, Unauthorized from telegram.ext import Dispatcher, JobQueue, ContextTypes, ExtBot -from telegram.utils.deprecate import TelegramDeprecationWarning +from telegram.utils.warnings import PTBDeprecationWarning, warn from telegram.utils.helpers import get_signal_name, DEFAULT_FALSE, DefaultValue from telegram.utils.request import Request from telegram.ext.utils.types import CCT, UD, CD, BD @@ -211,14 +210,14 @@ class Updater(Generic[CCT, UD, CD, BD]): ): if defaults and bot: - warnings.warn( + warn( 'Passing defaults to an Updater has no effect when a Bot is passed ' 'as well. Pass them to the Bot instead.', - TelegramDeprecationWarning, + PTBDeprecationWarning, stacklevel=2, ) if arbitrary_callback_data is not DEFAULT_FALSE and bot: - warnings.warn( + warn( 'Passing arbitrary_callback_data to an Updater has no ' 'effect when a Bot is passed as well. Pass them to the Bot instead.', stacklevel=2, @@ -250,9 +249,10 @@ class Updater(Generic[CCT, UD, CD, BD]): if bot is not None: self.bot = bot if bot.request.con_pool_size < con_pool_size: - self.logger.warning( - 'Connection pool of Request object is smaller than optimal value (%s)', - con_pool_size, + warn( + f'Connection pool of Request object is smaller than optimal value ' + f'{con_pool_size}', + stacklevel=2, ) else: # we need a connection pool the size of: @@ -299,9 +299,10 @@ class Updater(Generic[CCT, UD, CD, BD]): self.bot = dispatcher.bot if self.bot.request.con_pool_size < con_pool_size: - self.logger.warning( - 'Connection pool of Request object is smaller than optimal value (%s)', - con_pool_size, + warn( + f'Connection pool of Request object is smaller than optimal value ' + f'{con_pool_size}', + stacklevel=2, ) self.update_queue = dispatcher.update_queue self.__exception_event = dispatcher.exception_event diff --git a/telegram/utils/deprecate.py b/telegram/utils/deprecate.py deleted file mode 100644 index d08a9ec75..000000000 --- a/telegram/utils/deprecate.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2022 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains a class which is used for deprecation warnings.""" - - -# We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it -# seem like it's the user that issued the warning -# We name it something else so that you don't get confused when you attempt to suppress it -class TelegramDeprecationWarning(Warning): - """Custom warning class for deprecations in this library.""" - - __slots__ = () diff --git a/telegram/utils/warnings.py b/telegram/utils/warnings.py new file mode 100644 index 000000000..fe709c83b --- /dev/null +++ b/telegram/utils/warnings.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2021 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains classes used for warnings.""" +import warnings +from typing import Type + + +class PTBUserWarning(UserWarning): + """ + Custom user warning class used for warnings in this library. + + .. versionadded:: 14.0 + """ + + __slots__ = () + + +class PTBRuntimeWarning(PTBUserWarning, RuntimeWarning): + """ + Custom runtime warning class used for warnings in this library. + + .. versionadded:: 14.0 + """ + + __slots__ = () + + +# https://www.python.org/dev/peps/pep-0565/ recommends to use a custom warning class derived from +# DeprecationWarning. We also subclass from TGUserWarning so users can easily 'switch off' warnings +class PTBDeprecationWarning(PTBUserWarning, DeprecationWarning): + """ + Custom warning class for deprecations in this library. + + .. versionchanged:: 14.0 + Renamed TelegramDeprecationWarning to PTBDeprecationWarning. + """ + + __slots__ = () + + +def warn(message: str, category: Type[Warning] = PTBUserWarning, stacklevel: int = 0) -> None: + """ + Helper function used as a shortcut for warning with default values. + + .. versionadded:: 14.0 + + Args: + category (:obj:`Type[Warning]`): Specify the Warning class to pass to ``warnings.warn()``. + stacklevel (:obj:`int`): Specify the stacklevel to pass to ``warnings.warn()``. Pass the + same value as you'd pass directly to ``warnings.warn()``. + """ + warnings.warn(message, category=category, stacklevel=stacklevel + 1) diff --git a/tests/conftest.py b/tests/conftest.py index e5b1567b1..fc0964bc7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -64,7 +64,7 @@ from tests.bots import get_bot # This is here instead of in setup.cfg due to https://github.com/pytest-dev/pytest/issues/8343 def pytest_runtestloop(session): session.add_marker( - pytest.mark.filterwarnings('ignore::telegram.utils.deprecate.TelegramDeprecationWarning') + pytest.mark.filterwarnings('ignore::telegram.utils.warnings.PTBDeprecationWarning') ) @@ -110,7 +110,7 @@ class DictDispatcher(Dispatcher): @pytest.fixture(scope='session') def bot(bot_info): - return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY, request=DictRequest()) + return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY, request=DictRequest(8)) DEFAULT_BOTS = {} diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 5e6f37b3d..036c65409 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -788,7 +788,7 @@ class TestConversationHandler: assert not handler.check_update(Update(0, pre_checkout_query=pre_checkout_query)) assert not handler.check_update(Update(0, shipping_query=shipping_query)) - def test_no_jobqueue_warning(self, dp, bot, user1, caplog): + def test_no_jobqueue_warning(self, dp, bot, user1, recwarn): handler = ConversationHandler( entry_points=self.entry_points, states=self.states, @@ -813,12 +813,11 @@ class TestConversationHandler: bot=bot, ) - with caplog.at_level(logging.WARNING): - dp.process_update(Update(update_id=0, message=message)) - sleep(0.5) - assert len(caplog.records) == 1 + dp.process_update(Update(update_id=0, message=message)) + sleep(0.5) + assert len(recwarn) == 1 assert ( - caplog.records[0].message + str(recwarn[0].message) == "Ignoring `conversation_timeout` because the Dispatcher has no JobQueue." ) # now set dp.job_queue back to it's original value @@ -990,7 +989,7 @@ class TestConversationHandler: # assert timeout handler didn't got called assert self.test_flag is False - def test_conversation_timeout_dispatcher_handler_stop(self, dp, bot, user1, caplog): + def test_conversation_timeout_dispatcher_handler_stop(self, dp, bot, user1, recwarn): handler = ConversationHandler( entry_points=self.entry_points, states=self.states, @@ -1017,14 +1016,12 @@ class TestConversationHandler: bot=bot, ) - with caplog.at_level(logging.WARNING): - dp.process_update(Update(update_id=0, message=message)) - assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY - sleep(0.9) - assert handler.conversations.get((self.group.id, user1.id)) is None - assert len(caplog.records) == 1 - rec = caplog.records[-1] - assert rec.getMessage().startswith('DispatcherHandlerStop in TIMEOUT') + dp.process_update(Update(update_id=0, message=message)) + assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY + sleep(0.9) + assert handler.conversations.get((self.group.id, user1.id)) is None + assert len(recwarn) == 1 + assert str(recwarn[0].message).startswith('DispatcherHandlerStop in TIMEOUT') def test_conversation_handler_timeout_update_and_context(self, dp, bot, user1): context = None @@ -1360,6 +1357,7 @@ class TestConversationHandler: "supported. You can still try to use it, but it will likely behave " "differently from what you expect." ) + assert recwarn[0].filename == __file__, "incorrect stacklevel!" def test_per_message_warning_is_only_shown_once(self, recwarn): ConversationHandler( @@ -1373,10 +1371,28 @@ class TestConversationHandler: ) assert len(recwarn) == 1 assert str(recwarn[0].message) == ( - "If 'per_message=True', all entry points and state handlers" + "If 'per_message=True', all entry points, state handlers, and fallbacks" " must be 'CallbackQueryHandler', since no other handlers" " have a message context." ) + assert recwarn[0].filename == __file__, "incorrect stacklevel!" + + def test_per_message_but_not_per_chat_warning(self, recwarn): + ConversationHandler( + entry_points=[CallbackQueryHandler(self.code, "code")], + states={ + self.BREWING: [CallbackQueryHandler(self.code, "code")], + }, + fallbacks=[CallbackQueryHandler(self.code, "code")], + per_message=True, + per_chat=False, + ) + assert len(recwarn) == 1 + assert str(recwarn[0].message) == ( + "If 'per_message=True' is used, 'per_chat=True' should also be used, " + "since message IDs are not globally unique." + ) + assert recwarn[0].filename == __file__, "incorrect stacklevel!" def test_per_message_false_warning_is_only_shown_once(self, recwarn): ConversationHandler( @@ -1393,6 +1409,7 @@ class TestConversationHandler: "If 'per_message=False', 'CallbackQueryHandler' will not be " "tracked for every message." ) + assert recwarn[0].filename == __file__, "incorrect stacklevel!" def test_warnings_per_chat_is_only_shown_once(self, recwarn): def hello(update, context): @@ -1415,6 +1432,7 @@ class TestConversationHandler: "If 'per_chat=True', 'InlineQueryHandler' can not be used," " since inline queries have no chat context." ) + assert recwarn[0].filename == __file__, "incorrect stacklevel!" def test_nested_conversation_handler(self, dp, bot, user1, user2): self.nested_states[self.DRINKING] = [ diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index 4dc064edb..0bef5da94 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -110,6 +110,7 @@ class TestDispatcher: str(recwarn[0].message) == 'Asynchronous callbacks can not be processed without at least one worker thread.' ) + assert recwarn[0].filename == __file__, "stacklevel is incorrect!" def test_one_context_per_update(self, dp): def one(update, context): @@ -243,21 +244,18 @@ class TestDispatcher: assert name1 != name2 - def test_async_raises_dispatcher_handler_stop(self, dp, caplog): + def test_async_raises_dispatcher_handler_stop(self, dp, recwarn): def callback(update, context): raise DispatcherHandlerStop() dp.add_handler(MessageHandler(Filters.all, callback, run_async=True)) - with caplog.at_level(logging.WARNING): - dp.update_queue.put(self.message_update) - sleep(0.1) - assert len(caplog.records) == 1 - assert ( - caplog.records[-1] - .getMessage() - .startswith('DispatcherHandlerStop is not supported with async functions') - ) + dp.update_queue.put(self.message_update) + sleep(0.1) + assert len(recwarn) == 1 + assert str(recwarn[-1].message).startswith( + 'DispatcherHandlerStop is not supported with async functions' + ) def test_add_async_handler(self, dp): dp.add_handler( diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 159025de9..57357fafa 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -774,10 +774,10 @@ class TestBasePersistence: assert len(recwarn) == 2 assert str(recwarn[0].message).startswith( - "BasePersistence.replace_bot does not handle classes." + "BasePersistence.replace_bot does not handle classes such as 'CustomClass'" ) assert str(recwarn[1].message).startswith( - "BasePersistence.insert_bot does not handle classes." + "BasePersistence.insert_bot does not handle classes such as 'CustomClass'" ) def test_bot_replace_insert_bot_objects_with_faulty_equality(self, bot, bot_persistence): diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index d326ebb6a..603b3e4cf 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -101,9 +101,9 @@ class TestTelegramObject: a = TGO() b = TGO() assert a == b - assert len(recwarn) == 2 + assert len(recwarn) == 1 assert str(recwarn[0].message) == expected_warning - assert str(recwarn[1].message) == expected_warning + assert recwarn[0].filename == __file__, "wrong stacklevel" def test_meaningful_comparison(self, recwarn): class TGO(TelegramObject): diff --git a/tests/test_updater.py b/tests/test_updater.py index e99534f86..fda3fd09f 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -56,7 +56,7 @@ from telegram.ext import ( InvalidCallbackData, ExtBot, ) -from telegram.utils.deprecate import TelegramDeprecationWarning +from telegram.utils.warnings import PTBDeprecationWarning from telegram.ext.utils.webhookhandler import WebhookServer signalskip = pytest.mark.skipif( @@ -119,6 +119,16 @@ class TestUpdater: assert len(recwarn) == 1 assert 'Passing arbitrary_callback_data to an Updater' in str(recwarn[0].message) + def test_warn_con_pool(self, bot, recwarn, dp): + dp = Dispatcher(bot, Queue(), workers=5) + Updater(bot=bot, workers=8) + Updater(dispatcher=dp, workers=None) + assert len(recwarn) == 2 + for idx, value in enumerate((12, 9)): + warning = f'Connection pool of Request object is smaller than optimal value {value}' + assert str(recwarn[idx].message) == warning + assert recwarn[idx].filename == __file__, "wrong stacklevel!" + @pytest.mark.parametrize( ('error',), argvalues=[(TelegramError('Test Error 2'),), (Unauthorized('Test Unauthorized'),)], @@ -647,5 +657,5 @@ class TestUpdater: Updater(dispatcher=dispatcher, context_types=True) def test_defaults_warning(self, bot): - with pytest.warns(TelegramDeprecationWarning, match='no effect when a Bot is passed'): + with pytest.warns(PTBDeprecationWarning, match='no effect when a Bot is passed'): Updater(bot=bot, defaults=Defaults())