From 8f0031e9c8a733aa88cc84728e8763b5a06453f2 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 30 Aug 2021 16:31:19 +0200 Subject: [PATCH] Remove Deprecated Functionality (#2644, #2740, #2745) --- docs/source/telegram.ext.delayqueue.rst | 9 - docs/source/telegram.ext.messagequeue.rst | 9 - docs/source/telegram.ext.rst | 2 - telegram/bot.py | 93 +----- telegram/chat.py | 51 +--- telegram/chataction.py | 18 +- telegram/constants.py | 12 +- telegram/ext/__init__.py | 7 +- telegram/ext/dispatcher.py | 59 +--- telegram/ext/filters.py | 44 --- telegram/ext/jobqueue.py | 25 +- telegram/ext/messagequeue.py | 334 ---------------------- telegram/ext/updater.py | 59 +--- telegram/ext/utils/promise.py | 11 +- telegram/utils/promise.py | 38 --- telegram/utils/webhookhandler.py | 35 --- tests/conftest.py | 19 +- tests/test_bot.py | 66 +---- tests/test_chat.py | 21 -- tests/test_commandhandler.py | 4 +- tests/test_dispatcher.py | 63 +--- tests/test_filters.py | 24 +- tests/test_jobqueue.py | 1 - tests/test_messagehandler.py | 2 +- tests/test_messagequeue.py | 69 ----- tests/test_slots.py | 1 - tests/test_updater.py | 52 ---- tests/test_utils.py | 37 --- 28 files changed, 68 insertions(+), 1097 deletions(-) delete mode 100644 docs/source/telegram.ext.delayqueue.rst delete mode 100644 docs/source/telegram.ext.messagequeue.rst delete mode 100644 telegram/ext/messagequeue.py delete mode 100644 telegram/utils/promise.py delete mode 100644 telegram/utils/webhookhandler.py delete mode 100644 tests/test_messagequeue.py delete mode 100644 tests/test_utils.py diff --git a/docs/source/telegram.ext.delayqueue.rst b/docs/source/telegram.ext.delayqueue.rst deleted file mode 100644 index cf64f2bc7..000000000 --- a/docs/source/telegram.ext.delayqueue.rst +++ /dev/null @@ -1,9 +0,0 @@ -:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/messagequeue.py - -telegram.ext.DelayQueue -======================= - -.. autoclass:: telegram.ext.DelayQueue - :members: - :show-inheritance: - :special-members: diff --git a/docs/source/telegram.ext.messagequeue.rst b/docs/source/telegram.ext.messagequeue.rst deleted file mode 100644 index 0b824f1e9..000000000 --- a/docs/source/telegram.ext.messagequeue.rst +++ /dev/null @@ -1,9 +0,0 @@ -:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/messagequeue.py - -telegram.ext.MessageQueue -========================= - -.. autoclass:: telegram.ext.MessageQueue - :members: - :show-inheritance: - :special-members: diff --git a/docs/source/telegram.ext.rst b/docs/source/telegram.ext.rst index 9d68efdce..447792421 100644 --- a/docs/source/telegram.ext.rst +++ b/docs/source/telegram.ext.rst @@ -10,8 +10,6 @@ telegram.ext package telegram.ext.callbackcontext telegram.ext.job telegram.ext.jobqueue - telegram.ext.messagequeue - telegram.ext.delayqueue telegram.ext.contexttypes telegram.ext.defaults diff --git a/telegram/bot.py b/telegram/bot.py index 650a62477..24f254cfb 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -148,6 +148,11 @@ class Bot(TelegramObject): incorporated into PTB. However, this is not guaranteed to work, i.e. it will fail for passing files. + .. versionchanged:: 14.0 + * Removed the deprecated methods ``kick_chat_member``, ``kickChatMember``, + ``get_chat_members_count`` and ``getChatMembersCount``. + * Removed the deprecated property ``commands``. + Args: token (:obj:`str`): Bot's unique authentication. base_url (:obj:`str`, optional): Telegram Bot API service URL. @@ -173,7 +178,6 @@ class Bot(TelegramObject): 'private_key', 'defaults', '_bot', - '_commands', '_request', 'logger', ) @@ -209,7 +213,6 @@ class Bot(TelegramObject): self.base_url = str(base_url) + str(self.token) self.base_file_url = str(base_file_url) + str(self.token) self._bot: Optional[User] = None - self._commands: Optional[List[BotCommand]] = None self._request = request or Request() self.private_key = None self.logger = logging.getLogger(__name__) @@ -395,26 +398,6 @@ class Bot(TelegramObject): """:obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute.""" return self.bot.supports_inline_queries # type: ignore - @property - def commands(self) -> List[BotCommand]: - """ - List[:class:`BotCommand`]: Bot's commands as available in the default scope. - - .. deprecated:: 13.7 - This property has been deprecated since there can be different commands available for - different scopes. - """ - warnings.warn( - "Bot.commands has been deprecated since there can be different command " - "lists for different scopes.", - TelegramDeprecationWarning, - stacklevel=2, - ) - - if self._commands is None: - self._commands = self.get_my_commands() - return self._commands - @property def name(self) -> str: """:obj:`str`: Bot's @username.""" @@ -2424,36 +2407,6 @@ class Bot(TelegramObject): return File.de_json(result, self) # type: ignore[return-value, arg-type] - @log - def kick_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Deprecated, use :func:`~telegram.Bot.ban_chat_member` instead. - - .. deprecated:: 13.7 - - """ - warnings.warn( - '`bot.kick_chat_member` is deprecated. Use `bot.ban_chat_member` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return self.ban_chat_member( - chat_id=chat_id, - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - @log def ban_chat_member( self, @@ -3284,26 +3237,6 @@ class Bot(TelegramObject): return ChatMember.de_list(result, self) # type: ignore - @log - def get_chat_members_count( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> int: - """ - Deprecated, use :func:`~telegram.Bot.get_chat_member_count` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`bot.get_chat_members_count` is deprecated. ' - 'Use `bot.get_chat_member_count` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return self.get_chat_member_count(chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs) - @log def get_chat_member_count( self, @@ -5420,10 +5353,6 @@ class Bot(TelegramObject): result = self._post('getMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = BotCommand.de_list(result, self) # type: ignore[assignment,arg-type] - return self._commands # type: ignore[return-value] - return BotCommand.de_list(result, self) # type: ignore[return-value,arg-type] @log @@ -5480,11 +5409,6 @@ class Bot(TelegramObject): result = self._post('setMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - # Set commands only for default scope. No need to check for outcome. - # If request failed, we won't come this far - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = cmds - return result # type: ignore[return-value] @log @@ -5532,9 +5456,6 @@ class Bot(TelegramObject): result = self._post('deleteMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = [] - return result # type: ignore[return-value] @log @@ -5736,8 +5657,6 @@ class Bot(TelegramObject): """Alias for :meth:`ban_chat_member`""" banChatSenderChat = ban_chat_sender_chat """Alias for :meth:`ban_chat_sender_chat`""" - kickChatMember = kick_chat_member - """Alias for :meth:`kick_chat_member`""" unbanChatMember = unban_chat_member """Alias for :meth:`unban_chat_member`""" unbanChatSenderChat = unban_chat_sender_chat @@ -5772,8 +5691,6 @@ class Bot(TelegramObject): """Alias for :meth:`delete_chat_sticker_set`""" getChatMemberCount = get_chat_member_count """Alias for :meth:`get_chat_member_count`""" - getChatMembersCount = get_chat_members_count - """Alias for :meth:`get_chat_members_count`""" getWebhookInfo = get_webhook_info """Alias for :meth:`get_webhook_info`""" setGameScore = set_game_score diff --git a/telegram/chat.py b/telegram/chat.py index 20c56c0b0..ad4ad4d9d 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -18,13 +18,11 @@ # 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 an object that represents a Telegram Chat.""" -import warnings from datetime import datetime from typing import TYPE_CHECKING, List, Optional, ClassVar, Union, Tuple, Any from telegram import ChatPhoto, TelegramObject, constants from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput -from telegram.utils.deprecate import TelegramDeprecationWarning from .chatpermissions import ChatPermissions from .chatlocation import ChatLocation @@ -65,6 +63,9 @@ class Chat(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`id` is equal. + .. versionchanged:: 14.0 + Removed the deprecated methods ``kick_member`` and ``get_members_count``. + Args: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. @@ -341,25 +342,6 @@ class Chat(TelegramObject): api_kwargs=api_kwargs, ) - def get_members_count( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> int: - """ - Deprecated, use :func:`~telegram.Chat.get_member_count` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`Chat.get_members_count` is deprecated. Use `Chat.get_member_count` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - - return self.get_member_count( - timeout=timeout, - api_kwargs=api_kwargs, - ) - def get_member_count( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None ) -> int: @@ -402,33 +384,6 @@ class Chat(TelegramObject): api_kwargs=api_kwargs, ) - def kick_member( - self, - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Deprecated, use :func:`~telegram.Chat.ban_member` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`Chat.kick_member` is deprecated. Use `Chat.ban_member` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - - return self.ban_member( - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - def ban_member( self, user_id: Union[str, int], diff --git a/telegram/chataction.py b/telegram/chataction.py index d6d1bdb77..af5d65561 100644 --- a/telegram/chataction.py +++ b/telegram/chataction.py @@ -23,17 +23,15 @@ from telegram import constants class ChatAction: - """Helper class to provide constants for different chat actions.""" + """Helper class to provide constants for different chat actions. + + .. versionchanged:: 14.0 + Removed the deprecated constants ``RECORD_AUDIO`` and ``UPLOAD_AUDIO``. + """ __slots__ = () FIND_LOCATION: ClassVar[str] = constants.CHATACTION_FIND_LOCATION """:const:`telegram.constants.CHATACTION_FIND_LOCATION`""" - RECORD_AUDIO: ClassVar[str] = constants.CHATACTION_RECORD_AUDIO - """:const:`telegram.constants.CHATACTION_RECORD_AUDIO` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :attr:`RECORD_VOICE` instead. - """ RECORD_VOICE: ClassVar[str] = constants.CHATACTION_RECORD_VOICE """:const:`telegram.constants.CHATACTION_RECORD_VOICE` @@ -45,12 +43,6 @@ class ChatAction: """:const:`telegram.constants.CHATACTION_RECORD_VIDEO_NOTE`""" TYPING: ClassVar[str] = constants.CHATACTION_TYPING """:const:`telegram.constants.CHATACTION_TYPING`""" - UPLOAD_AUDIO: ClassVar[str] = constants.CHATACTION_UPLOAD_AUDIO - """:const:`telegram.constants.CHATACTION_UPLOAD_AUDIO` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :attr:`UPLOAD_VOICE` instead. - """ UPLOAD_VOICE: ClassVar[str] = constants.CHATACTION_UPLOAD_VOICE """:const:`telegram.constants.CHATACTION_UPLOAD_VOICE` diff --git a/telegram/constants.py b/telegram/constants.py index b7600a72d..c157c6488 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -70,12 +70,11 @@ Attributes: :class:`telegram.ChatAction`: +.. versionchanged:: 14.0 + Removed the deprecated constants ``CHATACTION_RECORD_AUDIO`` and ``CHATACTION_UPLOAD_AUDIO``. + Attributes: CHATACTION_FIND_LOCATION (:obj:`str`): ``'find_location'`` - CHATACTION_RECORD_AUDIO (:obj:`str`): ``'record_audio'`` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :const:`CHATACTION_RECORD_VOICE` instead. CHATACTION_RECORD_VOICE (:obj:`str`): ``'record_voice'`` .. versionadded:: 13.5 @@ -83,9 +82,6 @@ Attributes: CHATACTION_RECORD_VIDEO_NOTE (:obj:`str`): ``'record_video_note'`` CHATACTION_TYPING (:obj:`str`): ``'typing'`` CHATACTION_UPLOAD_AUDIO (:obj:`str`): ``'upload_audio'`` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :const:`CHATACTION_UPLOAD_VOICE` instead. CHATACTION_UPLOAD_VOICE (:obj:`str`): ``'upload_voice'`` .. versionadded:: 13.5 @@ -274,12 +270,10 @@ CHAT_SUPERGROUP: str = 'supergroup' CHAT_CHANNEL: str = 'channel' CHATACTION_FIND_LOCATION: str = 'find_location' -CHATACTION_RECORD_AUDIO: str = 'record_audio' CHATACTION_RECORD_VOICE: str = 'record_voice' CHATACTION_RECORD_VIDEO: str = 'record_video' CHATACTION_RECORD_VIDEO_NOTE: str = 'record_video_note' CHATACTION_TYPING: str = 'typing' -CHATACTION_UPLOAD_AUDIO: str = 'upload_audio' CHATACTION_UPLOAD_VOICE: str = 'upload_voice' CHATACTION_UPLOAD_DOCUMENT: str = 'upload_document' CHATACTION_CHOOSE_STICKER: str = 'choose_sticker' diff --git a/telegram/ext/__init__.py b/telegram/ext/__init__.py index b911acb8a..c5131adf7 100644 --- a/telegram/ext/__init__.py +++ b/telegram/ext/__init__.py @@ -25,7 +25,7 @@ from .dictpersistence import DictPersistence from .handler import Handler from .callbackcontext import CallbackContext from .contexttypes import ContextTypes -from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async +from .dispatcher import Dispatcher, DispatcherHandlerStop from .jobqueue import JobQueue, Job from .updater import Updater @@ -41,8 +41,6 @@ from .typehandler import TypeHandler from .conversationhandler import ConversationHandler from .precheckoutqueryhandler import PreCheckoutQueryHandler from .shippingqueryhandler import ShippingQueryHandler -from .messagequeue import MessageQueue -from .messagequeue import DelayQueue from .pollanswerhandler import PollAnswerHandler from .pollhandler import PollHandler from .chatmemberhandler import ChatMemberHandler @@ -63,7 +61,6 @@ __all__ = ( 'ContextTypes', 'ConversationHandler', 'Defaults', - 'DelayQueue', 'DictPersistence', 'Dispatcher', 'DispatcherHandlerStop', @@ -76,7 +73,6 @@ __all__ = ( 'JobQueue', 'MessageFilter', 'MessageHandler', - 'MessageQueue', 'PersistenceInput', 'PicklePersistence', 'PollAnswerHandler', @@ -89,5 +85,4 @@ __all__ = ( 'TypeHandler', 'UpdateFilter', 'Updater', - 'run_async', ) diff --git a/telegram/ext/dispatcher.py b/telegram/ext/dispatcher.py index 0507ba734..6b0b109d9 100644 --- a/telegram/ext/dispatcher.py +++ b/telegram/ext/dispatcher.py @@ -22,7 +22,6 @@ import logging import warnings import weakref from collections import defaultdict -from functools import wraps from queue import Empty, Queue from threading import BoundedSemaphore, Event, Lock, Thread, current_thread from time import sleep @@ -44,11 +43,9 @@ from uuid import uuid4 from telegram import TelegramError, Update from telegram.ext import BasePersistence, ContextTypes -from telegram.ext.callbackcontext import CallbackContext from telegram.ext.handler import Handler import telegram.ext.extbot from telegram.ext.callbackdatacache import CallbackDataCache -from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.ext.utils.promise import Promise from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE from telegram.ext.utils.types import CCT, UD, CD, BD @@ -56,46 +53,13 @@ from telegram.ext.utils.types import CCT, UD, CD, BD if TYPE_CHECKING: from telegram import Bot from telegram.ext import JobQueue + from telegram.ext.callbackcontext import CallbackContext DEFAULT_GROUP: int = 0 UT = TypeVar('UT') -def run_async( - func: Callable[[Update, CallbackContext], object] -) -> Callable[[Update, CallbackContext], object]: - """ - Function decorator that will run the function in a new thread. - - Will run :attr:`telegram.ext.Dispatcher.run_async`. - - Using this decorator is only possible when only a single Dispatcher exist in the system. - - Note: - DEPRECATED. Use :attr:`telegram.ext.Dispatcher.run_async` directly instead or the - :attr:`Handler.run_async` parameter. - - Warning: - If you're using ``@run_async`` you cannot rely on adding custom attributes to - :class:`telegram.ext.CallbackContext`. See its docs for more info. - """ - - @wraps(func) - def async_func(*args: object, **kwargs: object) -> object: - warnings.warn( - 'The @run_async decorator is deprecated. Use the `run_async` parameter of ' - 'your Handler or `Dispatcher.run_async` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return Dispatcher.get_instance()._run_async( # pylint: disable=W0212 - func, *args, update=None, error_handling=False, **kwargs - ) - - return async_func - - class DispatcherHandlerStop(Exception): """ Raise this in handler to prevent execution of any other handler (even in different group). @@ -180,7 +144,6 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): '__async_queue', '__async_threads', 'bot', - '__dict__', '__weakref__', 'context_types', ) @@ -359,13 +322,6 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): self.logger.error('An uncaught error was raised while handling the error.') continue - # Don't perform error handling for a `Promise` with deactivated error handling. This - # should happen only via the deprecated `@run_async` decorator or `Promises` created - # within error handlers - if not promise.error_handling: - self.logger.error('A promise with deactivated error handling raised an error.') - continue - # If we arrive here, an exception happened in the promise and was neither # DispatcherHandlerStop nor raised by an error handler. So we can and must handle it try: @@ -399,18 +355,7 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): Promise """ - return self._run_async(func, *args, update=update, error_handling=True, **kwargs) - - def _run_async( - self, - func: Callable[..., object], - *args: object, - update: object = None, - error_handling: bool = True, - **kwargs: object, - ) -> Promise: - # TODO: Remove error_handling parameter once we drop the @run_async decorator - promise = Promise(func, args, kwargs, update=update, error_handling=error_handling) + promise = Promise(func, args, kwargs, update=update) self.__async_queue.put(promise) return promise diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index abfdd9694..c99a4a464 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -20,7 +20,6 @@ """This module contains the Filters for use with the MessageHandler class.""" import re -import warnings from abc import ABC, abstractmethod from threading import Lock @@ -50,7 +49,6 @@ __all__ = [ 'XORFilter', ] -from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.types import SLT DataDict = Dict[str, list] @@ -1307,48 +1305,6 @@ officedocument.wordprocessingml.document")``. """""" # remove method from docs return any(entity.type == self.entity_type for entity in message.caption_entities) - class _Private(MessageFilter): - __slots__ = () - name = 'Filters.private' - - def filter(self, message: Message) -> bool: - warnings.warn( - 'Filters.private is deprecated. Use Filters.chat_type.private instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return message.chat.type == Chat.PRIVATE - - private = _Private() - """ - Messages sent in a private chat. - - Note: - DEPRECATED. Use - :attr:`telegram.ext.Filters.chat_type.private` instead. - """ - - class _Group(MessageFilter): - __slots__ = () - name = 'Filters.group' - - def filter(self, message: Message) -> bool: - warnings.warn( - 'Filters.group is deprecated. Use Filters.chat_type.groups instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] - - group = _Group() - """ - Messages sent in a group or a supergroup chat. - - Note: - DEPRECATED. Use - :attr:`telegram.ext.Filters.chat_type.groups` instead. - """ - class _ChatType(MessageFilter): __slots__ = () name = 'Filters.chat_type' diff --git a/telegram/ext/jobqueue.py b/telegram/ext/jobqueue.py index 3c5351bbf..05203c0e1 100644 --- a/telegram/ext/jobqueue.py +++ b/telegram/ext/jobqueue.py @@ -176,7 +176,7 @@ class JobQueue: job_kwargs = {} name = name or callback.__name__ - job = Job(callback, context, name, self) + job = Job(callback, context, name) date_time = self._parse_time_input(when, shift_day=True) j = self.scheduler.add_job( @@ -260,7 +260,7 @@ class JobQueue: job_kwargs = {} name = name or callback.__name__ - job = Job(callback, context, name, self) + job = Job(callback, context, name) dt_first = self._parse_time_input(first) dt_last = self._parse_time_input(last) @@ -325,7 +325,7 @@ class JobQueue: job_kwargs = {} name = name or callback.__name__ - job = Job(callback, context, name, self) + job = Job(callback, context, name) j = self.scheduler.add_job( callback, @@ -382,7 +382,7 @@ class JobQueue: job_kwargs = {} name = name or callback.__name__ - job = Job(callback, context, name, self) + job = Job(callback, context, name) j = self.scheduler.add_job( callback, @@ -425,7 +425,7 @@ class JobQueue: """ name = name or callback.__name__ - job = Job(callback, context, name, self) + job = Job(callback, context, name) j = self.scheduler.add_job(callback, args=self._build_args(job), name=name, **job_kwargs) @@ -445,7 +445,7 @@ class JobQueue: def jobs(self) -> Tuple['Job', ...]: """Returns a tuple of all *scheduled* jobs that are currently in the ``JobQueue``.""" return tuple( - Job._from_aps_job(job, self) # pylint: disable=W0212 + Job._from_aps_job(job) # pylint: disable=protected-access for job in self.scheduler.get_jobs() ) @@ -472,21 +472,21 @@ class Job: * If :attr:`job` isn't passed on initialization, it must be set manually afterwards for this :class:`telegram.ext.Job` to be useful. + .. versionchanged:: 14.0 + Removed argument and attribute :attr:`job_queue`. + Args: callback (:obj:`callable`): The callback function that should be executed by the new job. Callback signature: ``def callback(update: Update, context: CallbackContext)`` context (:obj:`object`, optional): Additional data needed for the callback function. Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. name (:obj:`str`, optional): The name of the new job. Defaults to ``callback.__name__``. - job_queue (:class:`telegram.ext.JobQueue`, optional): The ``JobQueue`` this job belongs to. - Only optional for backward compatibility with ``JobQueue.put()``. job (:class:`apscheduler.job.Job`, optional): The APS Job this job is a wrapper for. Attributes: callback (:obj:`callable`): The callback function that should be executed by the new job. context (:obj:`object`): Optional. Additional data needed for the callback function. name (:obj:`str`): Optional. The name of the new job. - job_queue (:class:`telegram.ext.JobQueue`): Optional. The ``JobQueue`` this job belongs to. job (:class:`apscheduler.job.Job`): Optional. The APS Job this job is a wrapper for. """ @@ -494,7 +494,6 @@ class Job: 'callback', 'context', 'name', - 'job_queue', '_removed', '_enabled', 'job', @@ -505,14 +504,12 @@ class Job: callback: Callable[['CallbackContext'], None], context: object = None, name: str = None, - job_queue: JobQueue = None, job: APSJob = None, ): self.callback = callback self.context = context self.name = name or callback.__name__ - self.job_queue = job_queue self._removed = False self._enabled = False @@ -570,13 +567,13 @@ class Job: return self.job.next_run_time @classmethod - def _from_aps_job(cls, job: APSJob, job_queue: JobQueue) -> 'Job': + def _from_aps_job(cls, job: APSJob) -> 'Job': # context based callbacks if len(job.args) == 1: context = job.args[0].job.context else: context = job.args[1].context - return cls(job.func, context=context, name=job.name, job_queue=job_queue, job=job) + return cls(job.func, context=context, name=job.name, job=job) def __getattr__(self, item: str) -> object: return getattr(self.job, item) diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py deleted file mode 100644 index da2a7348d..000000000 --- a/telegram/ext/messagequeue.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env python -# -# Module author: -# Tymofii A. Khodniev (thodnev) -# -# 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/] -"""A throughput-limiting message processor for Telegram bots.""" -import functools -import queue as q -import threading -import time -import warnings -from typing import TYPE_CHECKING, Callable, List, NoReturn - -from telegram.ext.utils.promise import Promise -from telegram.utils.deprecate import TelegramDeprecationWarning - -if TYPE_CHECKING: - from telegram import Bot - -# We need to count < 1s intervals, so the most accurate timer is needed -curtime = time.perf_counter - - -class DelayQueueError(RuntimeError): - """Indicates processing errors.""" - - __slots__ = () - - -class DelayQueue(threading.Thread): - """ - Processes callbacks from queue with specified throughput limits. Creates a separate thread to - process callbacks with delays. - - .. deprecated:: 13.3 - :class:`telegram.ext.DelayQueue` in its current form is deprecated and will be reinvented - in a future release. See `this thread `_ for a list of known bugs. - - Args: - queue (:obj:`Queue`, optional): Used to pass callbacks to thread. Creates ``Queue`` - implicitly if not provided. - burst_limit (:obj:`int`, optional): Number of maximum callbacks to process per time-window - defined by :attr:`time_limit_ms`. Defaults to 30. - time_limit_ms (:obj:`int`, optional): Defines width of time-window used when each - processing limit is calculated. Defaults to 1000. - exc_route (:obj:`callable`, optional): A callable, accepting 1 positional argument; used to - route exceptions from processor thread to main thread; is called on `Exception` - subclass exceptions. If not provided, exceptions are routed through dummy handler, - which re-raises them. - autostart (:obj:`bool`, optional): If :obj:`True`, processor is started immediately after - object's creation; if :obj:`False`, should be started manually by `start` method. - Defaults to :obj:`True`. - name (:obj:`str`, optional): Thread's name. Defaults to ``'DelayQueue-N'``, where N is - sequential number of object created. - - Attributes: - burst_limit (:obj:`int`): Number of maximum callbacks to process per time-window. - time_limit (:obj:`int`): Defines width of time-window used when each processing limit is - calculated. - exc_route (:obj:`callable`): A callable, accepting 1 positional argument; used to route - exceptions from processor thread to main thread; - name (:obj:`str`): Thread's name. - - """ - - _instcnt = 0 # instance counter - - def __init__( - self, - queue: q.Queue = None, - burst_limit: int = 30, - time_limit_ms: int = 1000, - exc_route: Callable[[Exception], None] = None, - autostart: bool = True, - name: str = None, - ): - warnings.warn( - 'DelayQueue in its current form is deprecated and will be reinvented in a future ' - 'release. See https://git.io/JtDbF for a list of known bugs.', - category=TelegramDeprecationWarning, - ) - - self._queue = queue if queue is not None else q.Queue() - self.burst_limit = burst_limit - self.time_limit = time_limit_ms / 1000 - self.exc_route = exc_route if exc_route is not None else self._default_exception_handler - self.__exit_req = False # flag to gently exit thread - self.__class__._instcnt += 1 - if name is None: - name = f'{self.__class__.__name__}-{self.__class__._instcnt}' - super().__init__(name=name) - self.daemon = False - if autostart: # immediately start processing - super().start() - - def run(self) -> None: - """ - Do not use the method except for unthreaded testing purposes, the method normally is - automatically called by autostart argument. - - """ - times: List[float] = [] # used to store each callable processing time - while True: - item = self._queue.get() - if self.__exit_req: - return # shutdown thread - # delay routine - now = time.perf_counter() - t_delta = now - self.time_limit # calculate early to improve perf. - if times and t_delta > times[-1]: - # if last call was before the limit time-window - # used to impr. perf. in long-interval calls case - times = [now] - else: - # collect last in current limit time-window - times = [t for t in times if t >= t_delta] - times.append(now) - if len(times) >= self.burst_limit: # if throughput limit was hit - time.sleep(times[1] - t_delta) - # finally process one - try: - func, args, kwargs = item - func(*args, **kwargs) - except Exception as exc: # re-route any exceptions - self.exc_route(exc) # to prevent thread exit - - def stop(self, timeout: float = None) -> None: - """Used to gently stop processor and shutdown its thread. - - Args: - timeout (:obj:`float`): Indicates maximum time to wait for processor to stop and its - thread to exit. If timeout exceeds and processor has not stopped, method silently - returns. :attr:`is_alive` could be used afterwards to check the actual status. - ``timeout`` set to :obj:`None`, blocks until processor is shut down. - Defaults to :obj:`None`. - - """ - self.__exit_req = True # gently request - self._queue.put(None) # put something to unfreeze if frozen - super().join(timeout=timeout) - - @staticmethod - def _default_exception_handler(exc: Exception) -> NoReturn: - """ - Dummy exception handler which re-raises exception in thread. Could be possibly overwritten - by subclasses. - - """ - raise exc - - def __call__(self, func: Callable, *args: object, **kwargs: object) -> None: - """Used to process callbacks in throughput-limiting thread through queue. - - Args: - func (:obj:`callable`): The actual function (or any callable) that is processed through - queue. - *args (:obj:`list`): Variable-length `func` arguments. - **kwargs (:obj:`dict`): Arbitrary keyword-arguments to `func`. - - """ - if not self.is_alive() or self.__exit_req: - raise DelayQueueError('Could not process callback in stopped thread') - self._queue.put((func, args, kwargs)) - - -# The most straightforward way to implement this is to use 2 sequential delay -# queues, like on classic delay chain schematics in electronics. -# So, message path is: -# msg --> group delay if group msg, else no delay --> normal msg delay --> out -# This way OS threading scheduler cares of timings accuracy. -# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org) -class MessageQueue: - """ - Implements callback processing with proper delays to avoid hitting Telegram's message limits. - Contains two ``DelayQueue``, for group and for all messages, interconnected in delay chain. - Callables are processed through *group* ``DelayQueue``, then through *all* ``DelayQueue`` for - group-type messages. For non-group messages, only the *all* ``DelayQueue`` is used. - - .. deprecated:: 13.3 - :class:`telegram.ext.MessageQueue` in its current form is deprecated and will be reinvented - in a future release. See `this thread `_ for a list of known bugs. - - Args: - all_burst_limit (:obj:`int`, optional): Number of maximum *all-type* callbacks to process - per time-window defined by :attr:`all_time_limit_ms`. Defaults to 30. - all_time_limit_ms (:obj:`int`, optional): Defines width of *all-type* time-window used when - each processing limit is calculated. Defaults to 1000 ms. - group_burst_limit (:obj:`int`, optional): Number of maximum *group-type* callbacks to - process per time-window defined by :attr:`group_time_limit_ms`. Defaults to 20. - group_time_limit_ms (:obj:`int`, optional): Defines width of *group-type* time-window used - when each processing limit is calculated. Defaults to 60000 ms. - exc_route (:obj:`callable`, optional): A callable, accepting one positional argument; used - to route exceptions from processor threads to main thread; is called on ``Exception`` - subclass exceptions. If not provided, exceptions are routed through dummy handler, - which re-raises them. - autostart (:obj:`bool`, optional): If :obj:`True`, processors are started immediately after - object's creation; if :obj:`False`, should be started manually by :attr:`start` method. - Defaults to :obj:`True`. - - """ - - def __init__( - self, - all_burst_limit: int = 30, - all_time_limit_ms: int = 1000, - group_burst_limit: int = 20, - group_time_limit_ms: int = 60000, - exc_route: Callable[[Exception], None] = None, - autostart: bool = True, - ): - warnings.warn( - 'MessageQueue in its current form is deprecated and will be reinvented in a future ' - 'release. See https://git.io/JtDbF for a list of known bugs.', - category=TelegramDeprecationWarning, - ) - - # create according delay queues, use composition - self._all_delayq = DelayQueue( - burst_limit=all_burst_limit, - time_limit_ms=all_time_limit_ms, - exc_route=exc_route, - autostart=autostart, - ) - self._group_delayq = DelayQueue( - burst_limit=group_burst_limit, - time_limit_ms=group_time_limit_ms, - exc_route=exc_route, - autostart=autostart, - ) - - def start(self) -> None: - """Method is used to manually start the ``MessageQueue`` processing.""" - self._all_delayq.start() - self._group_delayq.start() - - def stop(self, timeout: float = None) -> None: - """Stops the ``MessageQueue``.""" - self._group_delayq.stop(timeout=timeout) - self._all_delayq.stop(timeout=timeout) - - stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docstring if any - - def __call__(self, promise: Callable, is_group_msg: bool = False) -> Callable: - """ - Processes callables in throughput-limiting queues to avoid hitting limits (specified with - :attr:`burst_limit` and :attr:`time_limit`. - - Args: - promise (:obj:`callable`): Mainly the ``telegram.utils.promise.Promise`` (see Notes for - other callables), that is processed in delay queues. - is_group_msg (:obj:`bool`, optional): Defines whether ``promise`` would be processed in - group*+*all* ``DelayQueue``s (if set to :obj:`True`), or only through *all* - ``DelayQueue`` (if set to :obj:`False`), resulting in needed delays to avoid - hitting specified limits. Defaults to :obj:`False`. - - Note: - Method is designed to accept ``telegram.utils.promise.Promise`` as ``promise`` - argument, but other callables could be used too. For example, lambdas or simple - functions could be used to wrap original func to be called with needed args. In that - case, be sure that either wrapper func does not raise outside exceptions or the proper - :attr:`exc_route` handler is provided. - - Returns: - :obj:`callable`: Used as ``promise`` argument. - - """ - if not is_group_msg: # ignore middle group delay - self._all_delayq(promise) - else: # use middle group delay - self._group_delayq(self._all_delayq, promise) - return promise - - -def queuedmessage(method: Callable) -> Callable: - """A decorator to be used with :attr:`telegram.Bot` send* methods. - - Note: - As it probably wouldn't be a good idea to make this decorator a property, it has been coded - as decorator function, so it implies that first positional argument to wrapped MUST be - self. - - The next object attributes are used by decorator: - - Attributes: - self._is_messages_queued_default (:obj:`bool`): Value to provide class-defaults to - ``queued`` kwarg if not provided during wrapped method call. - self._msg_queue (:class:`telegram.ext.messagequeue.MessageQueue`): The actual - ``MessageQueue`` used to delay outbound messages according to specified time-limits. - - Wrapped method starts accepting the next kwargs: - - Args: - queued (:obj:`bool`, optional): If set to :obj:`True`, the ``MessageQueue`` is used to - process output messages. Defaults to `self._is_queued_out`. - isgroup (:obj:`bool`, optional): If set to :obj:`True`, the message is meant to be - group-type(as there's no obvious way to determine its type in other way at the moment). - Group-type messages could have additional processing delay according to limits set - in `self._out_queue`. Defaults to :obj:`False`. - - Returns: - ``telegram.utils.promise.Promise``: In case call is queued or original method's return - value if it's not. - - """ - - @functools.wraps(method) - def wrapped(self: 'Bot', *args: object, **kwargs: object) -> object: - # pylint: disable=W0212 - queued = kwargs.pop( - 'queued', self._is_messages_queued_default # type: ignore[attr-defined] - ) - isgroup = kwargs.pop('isgroup', False) - if queued: - prom = Promise(method, (self,) + args, kwargs) - return self._msg_queue(prom, isgroup) # type: ignore[attr-defined] - return method(self, *args, **kwargs) - - return wrapped diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index ee0742ab2..96d481401 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -342,7 +342,6 @@ class Updater(Generic[CCT, UD, CD, BD]): self, poll_interval: float = 0.0, timeout: float = 10, - clean: bool = None, bootstrap_retries: int = -1, read_latency: float = 2.0, allowed_updates: List[str] = None, @@ -350,6 +349,9 @@ class Updater(Generic[CCT, UD, CD, BD]): ) -> Optional[Queue]: """Starts polling updates from Telegram. + .. versionchanged:: 14.0 + Removed the ``clean`` argument in favor of ``drop_pending_updates``. + Args: poll_interval (:obj:`float`, optional): Time to wait between polling updates from Telegram in seconds. Default is ``0.0``. @@ -358,10 +360,6 @@ class Updater(Generic[CCT, UD, CD, BD]): Telegram servers before actually starting to poll. Default is :obj:`False`. .. versionadded :: 13.4 - clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``. - - .. deprecated:: 13.4 - Use ``drop_pending_updates`` instead. bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the :class:`telegram.ext.Updater` will retry on failures on the Telegram server. @@ -379,19 +377,6 @@ class Updater(Generic[CCT, UD, CD, BD]): :obj:`Queue`: The update queue that can be filled from the main thread. """ - if (clean is not None) and (drop_pending_updates is not None): - raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.') - - if clean is not None: - warnings.warn( - 'The argument `clean` of `start_polling` is deprecated. Please use ' - '`drop_pending_updates` instead.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean - with self.__lock: if not self.running: self.running = True @@ -428,11 +413,9 @@ class Updater(Generic[CCT, UD, CD, BD]): url_path: str = '', cert: str = None, key: str = None, - clean: bool = None, bootstrap_retries: int = 0, webhook_url: str = None, allowed_updates: List[str] = None, - force_event_loop: bool = None, drop_pending_updates: bool = None, ip_address: str = None, max_connections: int = 40, @@ -448,6 +431,10 @@ class Updater(Generic[CCT, UD, CD, BD]): :meth:`start_webhook` now *always* calls :meth:`telegram.Bot.set_webhook`, so pass ``webhook_url`` instead of calling ``updater.bot.set_webhook(webhook_url)`` manually. + .. versionchanged:: 14.0 + Removed the ``clean`` argument in favor of ``drop_pending_updates`` and removed the + deprecated argument ``force_event_loop``. + Args: listen (:obj:`str`, optional): IP-Address to listen on. Default ``127.0.0.1``. port (:obj:`int`, optional): Port the bot should be listening on. Default ``80``. @@ -458,10 +445,6 @@ class Updater(Generic[CCT, UD, CD, BD]): Telegram servers before actually starting to poll. Default is :obj:`False`. .. versionadded :: 13.4 - clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``. - - .. deprecated:: 13.4 - Use ``drop_pending_updates`` instead. bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the :class:`telegram.ext.Updater` will retry on failures on the Telegram server. @@ -477,13 +460,6 @@ class Updater(Generic[CCT, UD, CD, BD]): .. versionadded :: 13.4 allowed_updates (List[:obj:`str`], optional): Passed to :meth:`telegram.Bot.set_webhook`. - force_event_loop (:obj:`bool`, optional): Legacy parameter formerly used for a - workaround on Windows + Python 3.8+. No longer has any effect. - - .. deprecated:: 13.6 - Since version 13.6, ``tornade>=6.1`` is required, which resolves the former - issue. - max_connections (:obj:`int`, optional): Passed to :meth:`telegram.Bot.set_webhook`. @@ -493,27 +469,6 @@ class Updater(Generic[CCT, UD, CD, BD]): :obj:`Queue`: The update queue that can be filled from the main thread. """ - if (clean is not None) and (drop_pending_updates is not None): - raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.') - - if clean is not None: - warnings.warn( - 'The argument `clean` of `start_webhook` is deprecated. Please use ' - '`drop_pending_updates` instead.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - if force_event_loop is not None: - warnings.warn( - 'The argument `force_event_loop` of `start_webhook` is deprecated and no longer ' - 'has any effect.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean - with self.__lock: if not self.running: self.running = True diff --git a/telegram/ext/utils/promise.py b/telegram/ext/utils/promise.py index 66ac30db4..88bfa2c6b 100644 --- a/telegram/ext/utils/promise.py +++ b/telegram/ext/utils/promise.py @@ -33,14 +33,15 @@ logger = logging.getLogger(__name__) class Promise: """A simple Promise implementation for use with the run_async decorator, DelayQueue etc. + .. versionchanged:: 14.0 + Removed the argument and attribute ``error_handler``. + Args: pooled_function (:obj:`callable`): The callable that will be called concurrently. args (:obj:`list` | :obj:`tuple`): Positional arguments for :attr:`pooled_function`. kwargs (:obj:`dict`): Keyword arguments for :attr:`pooled_function`. update (:class:`telegram.Update` | :obj:`object`, optional): The update this promise is associated with. - error_handling (:obj:`bool`, optional): Whether exceptions raised by :attr:`func` - may be handled by error handlers. Defaults to :obj:`True`. Attributes: pooled_function (:obj:`callable`): The callable that will be called concurrently. @@ -49,8 +50,6 @@ class Promise: done (:obj:`threading.Event`): Is set when the result is available. update (:class:`telegram.Update` | :obj:`object`): Optional. The update this promise is associated with. - error_handling (:obj:`bool`): Optional. Whether exceptions raised by :attr:`func` - may be handled by error handlers. Defaults to :obj:`True`. """ @@ -59,27 +58,23 @@ class Promise: 'args', 'kwargs', 'update', - 'error_handling', 'done', '_done_callback', '_result', '_exception', ) - # TODO: Remove error_handling parameter once we drop the @run_async decorator def __init__( self, pooled_function: Callable[..., RT], args: Union[List, Tuple], kwargs: JSONDict, update: object = None, - error_handling: bool = True, ): self.pooled_function = pooled_function self.args = args self.kwargs = kwargs self.update = update - self.error_handling = error_handling self.done = Event() self._done_callback: Optional[Callable] = None self._result: Optional[RT] = None diff --git a/telegram/utils/promise.py b/telegram/utils/promise.py deleted file mode 100644 index 01bef3348..000000000 --- a/telegram/utils/promise.py +++ /dev/null @@ -1,38 +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 the :class:`telegram.ext.utils.promise.Promise` class for backwards -compatibility. -""" -import warnings - -import telegram.ext.utils.promise as promise -from telegram.utils.deprecate import TelegramDeprecationWarning - -warnings.warn( - 'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.', - TelegramDeprecationWarning, -) - -Promise = promise.Promise -""" -:class:`telegram.ext.utils.promise.Promise` - -.. deprecated:: v13.2 - Use :class:`telegram.ext.utils.promise.Promise` instead. -""" diff --git a/telegram/utils/webhookhandler.py b/telegram/utils/webhookhandler.py deleted file mode 100644 index 88e3c5ac4..000000000 --- a/telegram/utils/webhookhandler.py +++ /dev/null @@ -1,35 +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 the :class:`telegram.ext.utils.webhookhandler.WebhookHandler` class for -backwards compatibility. -""" -import warnings - -import telegram.ext.utils.webhookhandler as webhook_handler -from telegram.utils.deprecate import TelegramDeprecationWarning - -warnings.warn( - 'telegram.utils.webhookhandler is deprecated. Please use telegram.ext.utils.webhookhandler ' - 'instead.', - TelegramDeprecationWarning, -) - -WebhookHandler = webhook_handler.WebhookHandler -WebhookServer = webhook_handler.WebhookServer -WebhookAppClass = webhook_handler.WebhookAppClass diff --git a/tests/conftest.py b/tests/conftest.py index 1d7ddf6f0..e5b1567b1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -104,6 +104,10 @@ class DictBot(Bot): pass +class DictDispatcher(Dispatcher): + pass + + @pytest.fixture(scope='session') def bot(bot_info): return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY, request=DictRequest()) @@ -159,7 +163,7 @@ def provider_token(bot_info): def create_dp(bot): # Dispatcher is heavy to init (due to many threads and such) so we have a single session # scoped one here, but before each test, reset it (dp fixture below) - dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(), workers=2) + dispatcher = DictDispatcher(bot, Queue(), job_queue=JobQueue(), workers=2) dispatcher.job_queue.set_dispatcher(dispatcher) thr = Thread(target=dispatcher.start) thr.start() @@ -188,17 +192,12 @@ def dp(_dp): _dp.handlers = {} _dp.groups = [] _dp.error_handlers = {} - # For some reason if we setattr with the name mangled, then some tests(like async) run forever, - # due to threads not acquiring, (blocking). This adds these attributes to the __dict__. - object.__setattr__(_dp, '__stop_event', Event()) - object.__setattr__(_dp, '__exception_event', Event()) - object.__setattr__(_dp, '__async_queue', Queue()) - object.__setattr__(_dp, '__async_threads', set()) + _dp.__exception_event = Event() + _dp.__stop_event = Event() + _dp.__async_queue = Queue() + _dp.__async_threads = set() _dp.persistence = None - if _dp._Dispatcher__singleton_semaphore.acquire(blocking=0): - Dispatcher._set_singleton(_dp) yield _dp - Dispatcher._Dispatcher__singleton_semaphore.release() @pytest.fixture(scope='function') diff --git a/tests/test_bot.py b/tests/test_bot.py index 458293137..aea2f5475 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -193,7 +193,6 @@ class TestBot: @flaky(3, 1) def test_get_me_and_properties(self, bot): get_me_bot = bot.get_me() - commands = bot.get_my_commands() assert isinstance(get_me_bot, User) assert get_me_bot.id == bot.id @@ -205,9 +204,6 @@ class TestBot: assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages assert get_me_bot.supports_inline_queries == bot.supports_inline_queries assert f'https://t.me/{get_me_bot.username}' == bot.link - assert commands == bot.commands - bot._commands = None - assert commands == bot.commands def test_equality(self): a = Bot(FALLBACKS[0]["token"]) @@ -723,12 +719,10 @@ class TestBot: 'chat_action', [ ChatAction.FIND_LOCATION, - ChatAction.RECORD_AUDIO, ChatAction.RECORD_VIDEO, ChatAction.RECORD_VIDEO_NOTE, ChatAction.RECORD_VOICE, ChatAction.TYPING, - ChatAction.UPLOAD_AUDIO, ChatAction.UPLOAD_DOCUMENT, ChatAction.UPLOAD_PHOTO, ChatAction.UPLOAD_VIDEO, @@ -1039,18 +1033,6 @@ class TestBot: assert bot.ban_chat_sender_chat(2, 32) monkeypatch.delattr(bot.request, 'post') - def test_kick_chat_member_warning(self, monkeypatch, bot, recwarn): - def test(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - user_id = data['user_id'] == 32 - return chat_id and user_id - - monkeypatch.setattr(bot.request, 'post', test) - bot.kick_chat_member(2, 32) - assert len(recwarn) == 1 - assert '`bot.kick_chat_member` is deprecated' in str(recwarn[0].message) - monkeypatch.delattr(bot.request, 'post') - # TODO: Needs improvement. @pytest.mark.parametrize('only_if_banned', [True, False, None]) def test_unban_chat_member(self, monkeypatch, bot, only_if_banned): @@ -1401,16 +1383,6 @@ class TestBot: assert isinstance(count, int) assert count > 3 - def test_get_chat_members_count_warning(self, bot, channel_id, recwarn): - bot.get_chat_members_count(channel_id) - assert len(recwarn) == 1 - assert '`bot.get_chat_members_count` is deprecated' in str(recwarn[0].message) - - def test_bot_command_property_warning(self, bot, recwarn): - _ = bot.commands - assert len(recwarn) == 1 - assert 'Bot.commands has been deprecated since there can' in str(recwarn[0].message) - @flaky(3, 1) def test_get_chat_member(self, bot, channel_id, chat_id): chat_member = bot.get_chat_member(channel_id, chat_id) @@ -2059,39 +2031,14 @@ class TestBot: @flaky(3, 1) def test_set_and_get_my_commands(self, bot): - commands = [ - BotCommand('cmd1', 'descr1'), - BotCommand('cmd2', 'descr2'), - ] + commands = [BotCommand('cmd1', 'descr1'), ['cmd2', 'descr2']] bot.set_my_commands([]) assert bot.get_my_commands() == [] - assert bot.commands == [] assert bot.set_my_commands(commands) - for bc in [bot.get_my_commands(), bot.commands]: - assert len(bc) == 2 - assert bc[0].command == 'cmd1' - assert bc[0].description == 'descr1' - assert bc[1].command == 'cmd2' - assert bc[1].description == 'descr2' - - @flaky(3, 1) - def test_set_and_get_my_commands_strings(self, bot): - commands = [ - ['cmd1', 'descr1'], - ['cmd2', 'descr2'], - ] - bot.set_my_commands([]) - assert bot.get_my_commands() == [] - assert bot.commands == [] - assert bot.set_my_commands(commands) - - for bc in [bot.get_my_commands(), bot.commands]: - assert len(bc) == 2 - assert bc[0].command == 'cmd1' - assert bc[0].description == 'descr1' - assert bc[1].command == 'cmd2' - assert bc[1].description == 'descr2' + for i, bc in enumerate(bot.get_my_commands()): + assert bc.command == f'cmd{i+1}' + assert bc.description == f'descr{i+1}' @flaky(3, 1) def test_get_set_delete_my_commands_with_scope(self, bot, super_group_id, chat_id): @@ -2114,9 +2061,6 @@ class TestBot: assert len(gotten_private_cmd) == len(private_cmds) assert gotten_private_cmd[0].command == private_cmds[0].command - assert len(bot.commands) == 2 # set from previous test. Makes sure this hasn't changed. - assert bot.commands[0].command == 'cmd1' - # Delete command list from that supergroup and private chat- bot.delete_my_commands(private_scope) bot.delete_my_commands(group_scope, 'en') @@ -2129,7 +2073,7 @@ class TestBot: assert len(deleted_priv_cmds) == 0 == len(private_cmds) - 1 bot.delete_my_commands() # Delete commands from default scope - assert not bot.commands # Check if this has been updated to reflect the deletion. + assert len(bot.get_my_commands()) == 0 def test_log_out(self, monkeypatch, bot): # We don't actually make a request as to not break the test setup diff --git a/tests/test_chat.py b/tests/test_chat.py index e59aa01b8..bc029e974 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -196,15 +196,6 @@ class TestChat: monkeypatch.setattr(chat.bot, 'get_chat_member_count', make_assertion) assert chat.get_member_count() - def test_get_members_count_warning(self, chat, monkeypatch, recwarn): - def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id - - monkeypatch.setattr(chat.bot, 'get_chat_member_count', make_assertion) - assert chat.get_members_count() - assert len(recwarn) == 1 - assert '`Chat.get_members_count` is deprecated' in str(recwarn[0].message) - def test_get_member(self, monkeypatch, chat): def make_assertion(*_, **kwargs): chat_id = kwargs['chat_id'] == chat.id @@ -262,18 +253,6 @@ class TestChat: monkeypatch.setattr(chat.bot, 'ban_chat_sender_chat', make_assertion) assert chat.ban_chat(42) - def test_kick_member_warning(self, chat, monkeypatch, recwarn): - def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - until = kwargs['until_date'] == 43 - return chat_id and user_id and until - - monkeypatch.setattr(chat.bot, 'ban_chat_member', make_assertion) - assert chat.kick_member(user_id=42, until_date=43) - assert len(recwarn) == 1 - assert '`Chat.kick_member` is deprecated' in str(recwarn[0].message) - @pytest.mark.parametrize('only_if_banned', [True, False, None]) def test_unban_member(self, monkeypatch, chat, only_if_banned): def make_assertion(*_, **kwargs): diff --git a/tests/test_commandhandler.py b/tests/test_commandhandler.py index 15a6d3d2a..0b639b72e 100644 --- a/tests/test_commandhandler.py +++ b/tests/test_commandhandler.py @@ -197,7 +197,7 @@ class TestCommandHandler(BaseTest): def test_with_filter(self, command): """Test that a CH with a (generic) filter responds if its filters match""" - handler = self.make_default_handler(filters=Filters.group) + handler = self.make_default_handler(filters=Filters.chat_type.group) assert is_match(handler, make_command_update(command, chat=Chat(-23, Chat.GROUP))) assert not is_match(handler, make_command_update(command, chat=Chat(23, Chat.PRIVATE))) @@ -321,7 +321,7 @@ class TestPrefixHandler(BaseTest): self._test_edited(prefix_message, handler_edited, handler_no_edited) def test_with_filter(self, prefix_message_text): - handler = self.make_default_handler(filters=Filters.group) + handler = self.make_default_handler(filters=Filters.chat_type.group) text = prefix_message_text assert is_match(handler, make_message_update(text, chat=Chat(-23, Chat.GROUP))) assert not is_match(handler, make_message_update(text, chat=Chat(23, Chat.PRIVATE))) diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index 7f28dfa44..36a630d16 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -35,8 +35,7 @@ from telegram.ext import ( ContextTypes, ) from telegram.ext import PersistenceInput -from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop -from telegram.utils.deprecate import TelegramDeprecationWarning +from telegram.ext.dispatcher import Dispatcher, DispatcherHandlerStop from telegram.utils.helpers import DEFAULT_FALSE from tests.conftest import create_dp from collections import defaultdict @@ -58,12 +57,6 @@ class TestDispatcher: received = None count = 0 - def test_slot_behaviour(self, dp2, mro_slots): - for at in dp2.__slots__: - at = f"_Dispatcher{at}" if at.startswith('__') and not at.endswith('__') else at - assert getattr(dp2, at, 'err') != 'err', f"got extra slot '{at}'" - assert len(mro_slots(dp2)) == len(set(mro_slots(dp2))), "duplicate slot" - @pytest.fixture(autouse=True, name='reset') def reset_fixture(self): self.reset() @@ -103,6 +96,13 @@ class TestDispatcher: ): self.received = context.error.message + def test_slot_behaviour(self, bot, mro_slots): + dp = Dispatcher(bot=bot, update_queue=None) + for at in dp.__slots__: + at = f"_Dispatcher{at}" if at.startswith('__') and not at.endswith('__') else at + assert getattr(dp, at, 'err') != 'err', f"got extra slot '{at}'" + assert len(mro_slots(dp)) == len(set(mro_slots(dp))), "duplicate slot" + def test_less_than_one_worker_warning(self, dp, recwarn): Dispatcher(dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0) assert len(recwarn) == 1 @@ -243,37 +243,11 @@ class TestDispatcher: assert name1 != name2 - def test_multiple_run_async_decorator(self, dp, dp2): - # Make sure we got two dispatchers and that they are not the same - assert isinstance(dp, Dispatcher) - assert isinstance(dp2, Dispatcher) - assert dp is not dp2 - - @run_async - def must_raise_runtime_error(): - pass - - with pytest.raises(RuntimeError): - must_raise_runtime_error() - - def test_multiple_run_async_deprecation(self, dp): - assert isinstance(dp, Dispatcher) - - @run_async - def callback(update, context): - pass - - dp.add_handler(MessageHandler(Filters.all, callback)) - - with pytest.warns(TelegramDeprecationWarning, match='@run_async decorator'): - dp.process_update(self.message_update) - def test_async_raises_dispatcher_handler_stop(self, dp, caplog): - @run_async def callback(update, context): raise DispatcherHandlerStop() - dp.add_handler(MessageHandler(Filters.all, callback)) + dp.add_handler(MessageHandler(Filters.all, callback, run_async=True)) with caplog.at_level(logging.WARNING): dp.update_queue.put(self.message_update) @@ -282,24 +256,7 @@ class TestDispatcher: assert ( caplog.records[-1] .getMessage() - .startswith('DispatcherHandlerStop is not supported ' 'with async functions') - ) - - def test_async_raises_exception(self, dp, caplog): - @run_async - def callback(update, context): - raise RuntimeError('async raising exception') - - dp.add_handler(MessageHandler(Filters.all, callback)) - - 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('A promise with deactivated error handling') + .startswith('DispatcherHandlerStop is not supported with async functions') ) def test_add_async_handler(self, dp): diff --git a/tests/test_filters.py b/tests/test_filters.py index d72a47908..9a231206d 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -26,8 +26,6 @@ from telegram.ext import Filters, BaseFilter, MessageFilter, UpdateFilter import inspect import re -from telegram.utils.deprecate import TelegramDeprecationWarning - @pytest.fixture(scope='function') def update(): @@ -971,26 +969,6 @@ class TestFilters: assert Filters.caption_entity(message_entity.type)(update) assert not Filters.entity(message_entity.type)(update) - def test_private_filter(self, update): - assert Filters.private(update) - update.message.chat.type = 'group' - assert not Filters.private(update) - - def test_private_filter_deprecation(self, update): - with pytest.warns(TelegramDeprecationWarning): - Filters.private(update) - - def test_group_filter(self, update): - assert not Filters.group(update) - update.message.chat.type = 'group' - assert Filters.group(update) - update.message.chat.type = 'supergroup' - assert Filters.group(update) - - def test_group_filter_deprecation(self, update): - with pytest.warns(TelegramDeprecationWarning): - Filters.group(update) - @pytest.mark.parametrize( ('chat_type, results'), [ @@ -1832,7 +1810,7 @@ class TestFilters: update.message.text = 'test' update.message.forward_date = datetime.datetime.utcnow() - assert (Filters.text & Filters.forwarded & Filters.private)(update) + assert (Filters.text & Filters.forwarded & Filters.chat_type.private)(update) def test_or_filters(self, update): update.message.text = 'test' diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index bb9d979f5..aa3ffca8e 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -91,7 +91,6 @@ class TestJobQueue: and context.chat_data is None and context.user_data is None and isinstance(context.bot_data, dict) - and context.job_queue is not context.job.job_queue ): self.result += 1 diff --git a/tests/test_messagehandler.py b/tests/test_messagehandler.py index 333cd54c7..8bd22be06 100644 --- a/tests/test_messagehandler.py +++ b/tests/test_messagehandler.py @@ -120,7 +120,7 @@ class TestMessageHandler: self.test_flag = types and num def test_with_filter(self, message): - handler = MessageHandler(Filters.group, self.callback_context) + handler = MessageHandler(Filters.chat_type.group, self.callback_context) message.chat.type = 'group' assert handler.check_update(Update(0, message)) diff --git a/tests/test_messagequeue.py b/tests/test_messagequeue.py deleted file mode 100644 index 606e19f62..000000000 --- a/tests/test_messagequeue.py +++ /dev/null @@ -1,69 +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/]. -import os -from time import sleep, perf_counter - -import pytest - -import telegram.ext.messagequeue as mq - - -@pytest.mark.skipif( - os.getenv('GITHUB_ACTIONS', False) and os.name == 'nt', - reason="On windows precise timings are not accurate.", -) -class TestDelayQueue: - N = 128 - burst_limit = 30 - time_limit_ms = 1000 - margin_ms = 0 - testtimes = [] - - def call(self): - self.testtimes.append(perf_counter()) - - def test_delayqueue_limits(self): - dsp = mq.DelayQueue( - burst_limit=self.burst_limit, time_limit_ms=self.time_limit_ms, autostart=True - ) - assert dsp.is_alive() is True - - for _ in range(self.N): - dsp(self.call) - - starttime = perf_counter() - # wait up to 20 sec more than needed - app_endtime = (self.N * self.burst_limit / (1000 * self.time_limit_ms)) + starttime + 20 - while not dsp._queue.empty() and perf_counter() < app_endtime: - sleep(1) - assert dsp._queue.empty() is True # check loop exit condition - - dsp.stop() - assert dsp.is_alive() is False - - assert self.testtimes or self.N == 0 - passes, fails = [], [] - delta = (self.time_limit_ms - self.margin_ms) / 1000 - for start, stop in enumerate(range(self.burst_limit + 1, len(self.testtimes))): - part = self.testtimes[start:stop] - if (part[-1] - part[0]) >= delta: - passes.append(part) - else: - fails.append(part) - assert not fails diff --git a/tests/test_slots.py b/tests/test_slots.py index 4da5da352..a702a7f40 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -27,7 +27,6 @@ import inspect included = { # These modules/classes intentionally have __dict__. 'CallbackContext', 'BasePersistence', - 'Dispatcher', } diff --git a/tests/test_updater.py b/tests/test_updater.py index f8eaec2a7..e99534f86 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -301,7 +301,6 @@ class TestUpdater: monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) # prevent api calls from @info decorator when updater.bot.id is used in thread names monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) - monkeypatch.setattr(updater.bot, '_commands', []) ip = '127.0.0.1' port = randrange(1024, 49152) # Select random port @@ -480,57 +479,6 @@ class TestUpdater: ) assert self.test_flag is True - def test_deprecation_warnings_start_webhook(self, recwarn, updater, monkeypatch): - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - # prevent api calls from @info decorator when updater.bot.id is used in thread names - monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) - monkeypatch.setattr(updater.bot, '_commands', []) - - ip = '127.0.0.1' - port = randrange(1024, 49152) # Select random port - updater.start_webhook(ip, port, clean=True, force_event_loop=False) - updater.stop() - - for warning in recwarn: - print(warning) - - try: # This is for flaky tests (there's an unclosed socket sometimes) - recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it - except AssertionError: - pass - - assert len(recwarn) == 2 - assert str(recwarn[0].message).startswith('The argument `clean` of') - assert str(recwarn[1].message).startswith('The argument `force_event_loop` of') - - def test_clean_deprecation_warning_polling(self, recwarn, updater, monkeypatch): - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - # prevent api calls from @info decorator when updater.bot.id is used in thread names - monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) - monkeypatch.setattr(updater.bot, '_commands', []) - - updater.start_polling(clean=True) - updater.stop() - for msg in recwarn: - print(msg) - - try: # This is for flaky tests (there's an unclosed socket sometimes) - recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it - except AssertionError: - pass - - assert len(recwarn) == 1 - assert str(recwarn[0].message).startswith('The argument `clean` of') - - def test_clean_drop_pending_mutually_exclusive(self, updater): - with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'): - updater.start_polling(clean=True, drop_pending_updates=False) - - with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'): - updater.start_webhook(clean=True, drop_pending_updates=False) - @flaky(3, 1) def test_webhook_invalid_posts(self, updater): ip = '127.0.0.1' diff --git a/tests/test_utils.py b/tests/test_utils.py deleted file mode 100644 index 9b70239fe..000000000 --- a/tests/test_utils.py +++ /dev/null @@ -1,37 +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/]. - - -class TestUtils: - def test_promise_deprecation(self, recwarn): - import telegram.utils.promise # noqa: F401 - - assert len(recwarn) == 1 - assert str(recwarn[0].message) == ( - 'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.' - ) - - def test_webhookhandler_deprecation(self, recwarn): - import telegram.utils.webhookhandler # noqa: F401 - - assert len(recwarn) == 1 - assert str(recwarn[0].message) == ( - 'telegram.utils.webhookhandler is deprecated. Please use ' - 'telegram.ext.utils.webhookhandler instead.' - )