From 9997a9f47ea9637eb31bd6d9e657c2bf954d523c Mon Sep 17 00:00:00 2001 From: Harshil <37377066+harshil21@users.noreply.github.com> Date: Sat, 25 Mar 2023 23:48:04 +0530 Subject: [PATCH] Empower `ruff` (#3594) --- .pre-commit-config.yaml | 4 +- examples/chatmemberbot.py | 13 +- examples/errorhandlerbot.py | 2 +- examples/inlinebot.py | 2 +- examples/pollbot.py | 7 +- examples/rawapibot.py | 5 +- pyproject.toml | 4 + telegram/_bot.py | 195 ++++++------------ telegram/_chatjoinrequest.py | 2 +- telegram/_menubutton.py | 3 +- telegram/_message.py | 132 ++++++------ telegram/_passport/passportelementerrors.py | 4 +- telegram/_payment/shippingoption.py | 2 +- telegram/_telegramobject.py | 6 +- telegram/_update.py | 4 +- telegram/error.py | 6 +- telegram/ext/_aioratelimiter.py | 7 +- telegram/ext/_application.py | 10 +- telegram/ext/_applicationbuilder.py | 2 +- telegram/ext/_callbackcontext.py | 2 +- telegram/ext/_conversationhandler.py | 38 ++-- telegram/ext/_dictpersistence.py | 10 +- telegram/ext/_extbot.py | 8 +- telegram/ext/_jobqueue.py | 5 +- telegram/ext/_messagehandler.py | 2 +- telegram/ext/_prefixhandler.py | 10 +- telegram/ext/_updater.py | 7 +- telegram/ext/_utils/webhookhandler.py | 2 +- telegram/ext/filters.py | 6 +- telegram/helpers.py | 10 +- telegram/request/_baserequest.py | 5 +- telegram/request/_httpxrequest.py | 2 +- tests/_files/test_animation.py | 20 +- tests/_files/test_audio.py | 10 +- tests/_files/test_chatphoto.py | 9 +- tests/_files/test_contact.py | 2 +- tests/_files/test_document.py | 21 +- tests/_files/test_inputfile.py | 5 +- tests/_files/test_inputmedia.py | 23 ++- tests/_files/test_inputsticker.py | 6 +- tests/_files/test_location.py | 15 +- tests/_files/test_photo.py | 78 +++---- tests/_files/test_sticker.py | 52 ++--- tests/_files/test_venue.py | 2 +- tests/_files/test_video.py | 28 +-- tests/_files/test_videonote.py | 20 +- tests/_files/test_voice.py | 20 +- tests/_inline/test_inlinekeyboardbutton.py | 2 +- tests/_inline/test_inlinekeyboardmarkup.py | 10 +- tests/_inline/test_inlinequeryhandler.py | 6 +- .../test_inputinvoicemessagecontent.py | 2 +- tests/_passport/test_no_passport.py | 2 +- tests/_payment/test_invoice.py | 10 +- tests/_utils/test_datetime.py | 2 +- tests/_utils/test_defaults.py | 2 +- tests/_utils/test_defaultvalue.py | 5 +- tests/_utils/test_files.py | 11 +- tests/auxil/bot_method_checks.py | 39 ++-- tests/auxil/envvars.py | 4 +- tests/auxil/plugin_github_group.py | 24 +-- tests/auxil/pytest_classes.py | 3 +- tests/auxil/slots.py | 5 +- tests/conftest.py | 18 +- tests/docs/admonition_inserter.py | 12 +- tests/ext/_utils/test_trackingdict.py | 8 +- tests/ext/test_application.py | 74 ++++--- tests/ext/test_applicationbuilder.py | 14 +- tests/ext/test_basepersistence.py | 16 +- tests/ext/test_callbackdatacache.py | 10 +- tests/ext/test_callbackqueryhandler.py | 4 +- tests/ext/test_chatjoinrequesthandler.py | 4 +- tests/ext/test_chatmemberhandler.py | 4 +- tests/ext/test_choseninlineresulthandler.py | 2 +- tests/ext/test_commandhandler.py | 2 +- tests/ext/test_conversationhandler.py | 11 +- tests/ext/test_dictpersistence.py | 24 +-- tests/ext/test_filters.py | 33 +-- tests/ext/test_jobqueue.py | 13 +- tests/ext/test_messagehandler.py | 2 +- tests/ext/test_picklepersistence.py | 71 +++---- tests/ext/test_pollanswerhandler.py | 4 +- tests/ext/test_precheckoutqueryhandler.py | 2 +- tests/ext/test_ratelimiter.py | 10 +- tests/ext/test_shippingqueryhandler.py | 2 +- tests/ext/test_stringcommandhandler.py | 2 +- tests/ext/test_stringregexhandler.py | 4 +- tests/ext/test_typehandler.py | 2 +- tests/ext/test_updater.py | 87 ++++---- tests/request/test_request.py | 21 +- tests/request/test_requestparameter.py | 2 +- tests/test_bot.py | 65 +++--- tests/test_botcommandscope.py | 2 +- tests/test_callbackquery.py | 5 +- tests/test_chat.py | 21 +- tests/test_chatmember.py | 8 +- tests/test_constants.py | 17 +- tests/test_error.py | 9 +- tests/test_forum.py | 8 +- tests/test_helpers.py | 30 +-- tests/test_menubutton.py | 10 +- tests/test_message.py | 19 +- tests/test_meta.py | 4 +- tests/test_official.py | 10 +- tests/test_pollhandler.py | 4 +- tests/test_replykeyboardmarkup.py | 10 +- tests/test_slots.py | 3 +- tests/test_telegramobject.py | 24 ++- tests/test_update.py | 2 +- tests/test_user.py | 2 +- tests/test_version.py | 4 +- tests/test_videochat.py | 2 +- 111 files changed, 776 insertions(+), 896 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 211ea62fb..adc50c281 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -80,11 +80,11 @@ repos: - --diff - --check - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.254' + rev: 'v0.0.259' hooks: - id: ruff name: ruff - files: ^(telegram|examples)/.*\.py$ + files: ^(telegram|examples|tests)/.*\.py$ additional_dependencies: - httpx~=0.23.3 - tornado~=6.2 diff --git a/examples/chatmemberbot.py b/examples/chatmemberbot.py index c9c58c1e2..e5da9824f 100644 --- a/examples/chatmemberbot.py +++ b/examples/chatmemberbot.py @@ -103,13 +103,12 @@ async def track_chats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non elif was_member and not is_member: logger.info("%s removed the bot from the group %s", cause_name, chat.title) context.bot_data.setdefault("group_ids", set()).discard(chat.id) - else: - if not was_member and is_member: - logger.info("%s added the bot to the channel %s", cause_name, chat.title) - context.bot_data.setdefault("channel_ids", set()).add(chat.id) - elif was_member and not is_member: - logger.info("%s removed the bot from the channel %s", cause_name, chat.title) - context.bot_data.setdefault("channel_ids", set()).discard(chat.id) + elif not was_member and is_member: + logger.info("%s added the bot to the channel %s", cause_name, chat.title) + context.bot_data.setdefault("channel_ids", set()).add(chat.id) + elif was_member and not is_member: + logger.info("%s removed the bot from the channel %s", cause_name, chat.title) + context.bot_data.setdefault("channel_ids", set()).discard(chat.id) async def show_chats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: diff --git a/examples/errorhandlerbot.py b/examples/errorhandlerbot.py index 404000e5f..1152dc6da 100644 --- a/examples/errorhandlerbot.py +++ b/examples/errorhandlerbot.py @@ -39,7 +39,7 @@ DEVELOPER_CHAT_ID = 123456789 async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None: """Log the error and send a telegram message to notify the developer.""" # Log the error before we do anything else, so we can see it even if something breaks. - logger.error(msg="Exception while handling an update:", exc_info=context.error) + logger.error("Exception while handling an update:", exc_info=context.error) # traceback.format_exception returns the usual python message about an exception, but as a # list of strings rather than a single string, so we have to join them together. diff --git a/examples/inlinebot.py b/examples/inlinebot.py index 4f07abcaa..14310a6ea 100644 --- a/examples/inlinebot.py +++ b/examples/inlinebot.py @@ -58,7 +58,7 @@ async def inline_query(update: Update, context: ContextTypes.DEFAULT_TYPE) -> No """Handle the inline query. This is run when you type: @botusername """ query = update.inline_query.query - if query == "": + if not query: # empty query should not be handled return results = [ diff --git a/examples/pollbot.py b/examples/pollbot.py index 4ced581b6..9b41832b2 100644 --- a/examples/pollbot.py +++ b/examples/pollbot.py @@ -48,6 +48,9 @@ logging.basicConfig( logger = logging.getLogger(__name__) +TOTAL_VOTER_COUNT = 3 + + async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Inform user about what this bot can do""" await update.message.reply_text( @@ -101,7 +104,7 @@ async def receive_poll_answer(update: Update, context: ContextTypes.DEFAULT_TYPE ) answered_poll["answers"] += 1 # Close poll after three participants voted - if answered_poll["answers"] == 3: + if answered_poll["answers"] == TOTAL_VOTER_COUNT: await context.bot.stop_poll(answered_poll["chat_id"], answered_poll["message_id"]) @@ -123,7 +126,7 @@ async def receive_quiz_answer(update: Update, context: ContextTypes.DEFAULT_TYPE # the bot can receive closed poll updates we don't care about if update.poll.is_closed: return - if update.poll.total_voter_count == 3: + if update.poll.total_voter_count == TOTAL_VOTER_COUNT: try: quiz_data = context.bot_data[update.poll.id] # this means this poll answer update is from an old poll, we can't stop it then diff --git a/examples/rawapibot.py b/examples/rawapibot.py index 19c136e3f..54b1fff39 100644 --- a/examples/rawapibot.py +++ b/examples/rawapibot.py @@ -7,6 +7,7 @@ on the telegram.ext bot framework. This program is dedicated to the public domain under the CC0 license. """ import asyncio +import contextlib import logging from typing import NoReturn @@ -72,7 +73,5 @@ async def echo(bot: Bot, update_id: int) -> int: if __name__ == "__main__": - try: + with contextlib.suppress(KeyboardInterrupt): # Ignore exception when Ctrl-C is pressed asyncio.run(main()) - except KeyboardInterrupt: # Ignore exception when Ctrl-C is pressed - pass diff --git a/pyproject.toml b/pyproject.toml index 6c0e9621a..8460ae00f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,3 +9,7 @@ line_length = 99 [tool.ruff] line-length = 99 target-version = "py37" +show-fixes = true +ignore = ["PLR2004", "PLR0911", "PLR0912", "PLR0913", "PLR0915"] +select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET", "RSE", + "G", "ISC", "PT"] diff --git a/telegram/_bot.py b/telegram/_bot.py index ad24c5960..902443edc 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -19,6 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Bot.""" import asyncio +import contextlib import copy import functools import logging @@ -152,7 +153,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): Examples: :any:`Raw API Bot ` - .. seealso:: :wiki:`Your First Bot `, + .. seealso:: :wiki:`Your First Bot `, :wiki:`Builder Pattern ` .. versionadded:: 13.2 @@ -411,10 +412,10 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): # 1) if isinstance(val, InputMedia): # Copy object as not to edit it in-place - val = copy.copy(val) - with val._unfrozen(): - val.parse_mode = DefaultValue.get_value(val.parse_mode) - data[key] = val + new = copy.copy(val) + with new._unfrozen(): + new.parse_mode = DefaultValue.get_value(new.parse_mode) + data[key] = new elif key == "media" and isinstance(val, Sequence): # Copy objects as not to edit them in-place copy_list = [copy.copy(media) for media in val] @@ -479,10 +480,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): parameters=[RequestParameter.from_input(key, value) for key, value in data.items()], ) - if endpoint == "getUpdates": - request = self._request[0] - else: - request = self._request[1] + request = self._request[0] if endpoint == "getUpdates" else self._request[1] return await request.post( url=f"{self._base_url}/{endpoint}", @@ -861,7 +859,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "message_id": message_id} - result = await self._post( + return await self._post( "deleteMessage", data, read_timeout=read_timeout, @@ -870,7 +868,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def forward_message( @@ -1998,7 +1995,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): :class:`telegram.InputMediaDocument`, :class:`telegram.InputMediaPhoto`,\ :class:`telegram.InputMediaVideo`]): An array describing messages to be sent, must include - :tg-const:`telegram.constants.MediaGroupLimit.MIN_MEDIA_LENGTH`– + :tg-const:`telegram.constants.MediaGroupLimit.MIN_MEDIA_LENGTH`- :tg-const:`telegram.constants.MediaGroupLimit.MAX_MEDIA_LENGTH` items. .. versionchanged:: 20.0 @@ -2676,7 +2673,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "action": action, "message_thread_id": message_thread_id, } - result = await self._post( + return await self._post( "sendChatAction", data, read_timeout=read_timeout, @@ -2685,7 +2682,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result def _effective_inline_results( # skipcq: PYL-R0201 self, @@ -2708,10 +2704,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): if current_offset is not None: # Convert the string input to integer - if current_offset == "": - current_offset_int = 0 - else: - current_offset_int = int(current_offset) + current_offset_int = 0 if not current_offset else int(current_offset) # for now set to empty string, stating that there are no more results # might change later @@ -2726,18 +2719,18 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): # the callback *might* return more results on the next call, so we increment # the page count next_offset = str(current_offset_int + 1) + + elif len(results) > (current_offset_int + 1) * InlineQueryLimit.RESULTS: + # we expect more results for the next page + next_offset_int = current_offset_int + 1 + next_offset = str(next_offset_int) + effective_results = results[ + current_offset_int + * InlineQueryLimit.RESULTS : next_offset_int + * InlineQueryLimit.RESULTS + ] else: - if len(results) > (current_offset_int + 1) * InlineQueryLimit.RESULTS: - # we expect more results for the next page - next_offset_int = current_offset_int + 1 - next_offset = str(next_offset_int) - effective_results = results[ - current_offset_int - * InlineQueryLimit.RESULTS : next_offset_int - * InlineQueryLimit.RESULTS - ] - else: - effective_results = results[current_offset_int * InlineQueryLimit.RESULTS :] + effective_results = results[current_offset_int * InlineQueryLimit.RESULTS :] else: effective_results = results # type: ignore[assignment] @@ -2982,10 +2975,9 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): :class:`telegram.error.TelegramError` """ - try: # Try to get the file_id from the object + # Try to get the file_id from the object, if it fails, assume it's a string + with contextlib.suppress(AttributeError): file_id = file_id.file_id # type: ignore[union-attr] - except AttributeError: # If it fails, assume it's a string - pass data: JSONDict = {"file_id": file_id} @@ -3059,7 +3051,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "until_date": until_date, } - result = await self._post( + return await self._post( "banChatMember", data, read_timeout=read_timeout, @@ -3069,8 +3061,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def ban_chat_sender_chat( self, @@ -3105,7 +3095,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "sender_chat_id": sender_chat_id} - result = await self._post( + return await self._post( "banChatSenderChat", data, read_timeout=read_timeout, @@ -3115,8 +3105,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def unban_chat_member( self, @@ -3152,7 +3140,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "user_id": user_id, "only_if_banned": only_if_banned} - result = await self._post( + return await self._post( "unbanChatMember", data, read_timeout=read_timeout, @@ -3162,8 +3150,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def unban_chat_sender_chat( self, @@ -3195,7 +3181,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "sender_chat_id": sender_chat_id} - result = await self._post( + return await self._post( "unbanChatSenderChat", data, read_timeout=read_timeout, @@ -3205,8 +3191,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def answer_callback_query( self, @@ -3263,7 +3247,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "url": url, } - result = await self._post( + return await self._post( "answerCallbackQuery", data, read_timeout=read_timeout, @@ -3273,8 +3257,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def edit_message_text( self, @@ -3753,7 +3735,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "certificate": self._parse_file_input(certificate), # type: ignore[arg-type] } - result = await self._post( + return await self._post( "setWebhook", data, read_timeout=read_timeout, @@ -3763,8 +3745,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def delete_webhook( self, @@ -3793,7 +3773,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data = {"drop_pending_updates": drop_pending_updates} - result = await self._post( + return await self._post( "deleteWebhook", data, read_timeout=read_timeout, @@ -3803,8 +3783,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def leave_chat( self, @@ -3830,7 +3808,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id} - result = await self._post( + return await self._post( "leaveChat", data, read_timeout=read_timeout, @@ -3840,8 +3818,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def get_chat( self, @@ -3949,7 +3925,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id} - result = await self._post( + return await self._post( "getChatMemberCount", data, read_timeout=read_timeout, @@ -3958,7 +3934,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def get_chat_member( @@ -4024,7 +3999,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): :obj:`bool`: On success, :obj:`True` is returned. """ data: JSONDict = {"chat_id": chat_id, "sticker_set_name": sticker_set_name} - result = await self._post( + return await self._post( "setChatStickerSet", data, read_timeout=read_timeout, @@ -4033,7 +4008,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def delete_chat_sticker_set( @@ -4058,7 +4032,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): :obj:`bool`: On success, :obj:`True` is returned. """ data: JSONDict = {"chat_id": chat_id} - result = await self._post( + return await self._post( "deleteChatStickerSet", data, read_timeout=read_timeout, @@ -4067,7 +4041,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def get_webhook_info( @@ -4455,7 +4428,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "error_message": error_message, } - result = await self._post( + return await self._post( "answerShippingQuery", data, read_timeout=read_timeout, @@ -4465,8 +4438,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def answer_pre_checkout_query( # pylint: disable=invalid-name self, @@ -4514,7 +4485,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "error_message": error_message, } - result = await self._post( + return await self._post( "answerPreCheckoutQuery", data, read_timeout=read_timeout, @@ -4524,8 +4495,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def answer_web_app_query( self, @@ -4637,7 +4606,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "use_independent_chat_permissions": use_independent_chat_permissions, } - result = await self._post( + return await self._post( "restrictChatMember", data, read_timeout=read_timeout, @@ -4647,8 +4616,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def promote_chat_member( self, @@ -4746,7 +4713,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "can_manage_topics": can_manage_topics, } - result = await self._post( + return await self._post( "promoteChatMember", data, read_timeout=read_timeout, @@ -4756,8 +4723,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def set_chat_permissions( self, @@ -4807,7 +4772,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): "permissions": permissions, "use_independent_chat_permissions": use_independent_chat_permissions, } - result = await self._post( + return await self._post( "setChatPermissions", data, read_timeout=read_timeout, @@ -4816,7 +4781,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_chat_administrator_custom_title( @@ -4851,7 +4815,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "user_id": user_id, "custom_title": custom_title} - result = await self._post( + return await self._post( "setChatAdministratorCustomTitle", data, read_timeout=read_timeout, @@ -4861,8 +4825,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def export_chat_invite_link( self, @@ -4897,7 +4859,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id} - result = await self._post( + return await self._post( "exportChatInviteLink", data, read_timeout=read_timeout, @@ -4906,7 +4868,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def create_chat_invite_link( @@ -5139,7 +5100,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "user_id": user_id} - result = await self._post( + return await self._post( "approveChatJoinRequest", data, read_timeout=read_timeout, @@ -5149,8 +5110,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def decline_chat_join_request( self, @@ -5182,7 +5141,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "user_id": user_id} - result = await self._post( + return await self._post( "declineChatJoinRequest", data, read_timeout=read_timeout, @@ -5192,8 +5151,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): api_kwargs=api_kwargs, ) - return result - @_log async def set_chat_photo( self, @@ -5231,7 +5188,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "photo": self._parse_file_input(photo)} - result = await self._post( + return await self._post( "setChatPhoto", data, read_timeout=read_timeout, @@ -5240,7 +5197,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def delete_chat_photo( @@ -5269,7 +5225,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id} - result = await self._post( + return await self._post( "deleteChatPhoto", data, read_timeout=read_timeout, @@ -5278,7 +5234,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_chat_title( @@ -5311,7 +5266,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "title": title} - result = await self._post( + return await self._post( "setChatTitle", data, read_timeout=read_timeout, @@ -5320,7 +5275,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_chat_description( @@ -5354,7 +5308,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ data: JSONDict = {"chat_id": chat_id, "description": description} - result = await self._post( + return await self._post( "setChatDescription", data, read_timeout=read_timeout, @@ -5363,7 +5317,6 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def pin_chat_message( @@ -5882,7 +5835,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. "mask_position": mask_position, } - result = await self._post( + return await self._post( "createNewStickerSet", data, read_timeout=read_timeout, @@ -5892,8 +5845,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def add_sticker_to_set( self, @@ -6051,7 +6002,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. "mask_position": mask_position, } - result = await self._post( + return await self._post( "addStickerToSet", data, read_timeout=read_timeout, @@ -6061,8 +6012,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def set_sticker_position_in_set( self, @@ -6089,7 +6038,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"sticker": sticker, "position": position} - result = await self._post( + return await self._post( "setStickerPositionInSet", data, read_timeout=read_timeout, @@ -6098,7 +6047,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def delete_sticker_from_set( @@ -6124,7 +6072,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"sticker": sticker} - result = await self._post( + return await self._post( "deleteStickerFromSet", data, read_timeout=read_timeout, @@ -6133,7 +6081,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def delete_sticker_set( @@ -6162,7 +6109,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"name": name} - result = await self._post( + return await self._post( "deleteStickerSet", data, read_timeout=read_timeout, @@ -6171,7 +6118,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_sticker_set_thumbnail( @@ -6317,7 +6263,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. "thumbnail": self._parse_file_input(thumbnail) if thumbnail else None, } - result = await self._post( + return await self._post( "setStickerSetThumbnail", data, read_timeout=read_timeout, @@ -6327,8 +6273,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def set_sticker_set_title( self, @@ -6360,7 +6304,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"name": name, "title": title} - result = await self._post( + return await self._post( "setStickerSetTitle", data, read_timeout=read_timeout, @@ -6369,7 +6313,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_sticker_emoji_list( @@ -6403,7 +6346,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. :class:`telegram.error.TelegramError` """ data: JSONDict = {"sticker": sticker, "emoji_list": emoji_list} - result = await self._post( + return await self._post( "setStickerEmojiList", data, read_timeout=read_timeout, @@ -6412,7 +6355,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_sticker_keywords( @@ -6446,7 +6388,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. :class:`telegram.error.TelegramError` """ data: JSONDict = {"sticker": sticker, "keywords": keywords} - result = await self._post( + return await self._post( "setStickerKeywords", data, read_timeout=read_timeout, @@ -6455,7 +6397,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_sticker_mask_position( @@ -6488,7 +6429,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. :class:`telegram.error.TelegramError` """ data: JSONDict = {"sticker": sticker, "mask_position": mask_position} - result = await self._post( + return await self._post( "setStickerMaskPosition", data, read_timeout=read_timeout, @@ -6497,7 +6438,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def set_custom_emoji_sticker_set_thumbnail( @@ -6531,7 +6471,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"name": name, "custom_emoji_id": custom_emoji_id} - result = await self._post( + return await self._post( "setCustomEmojiStickerSetThumbnail", data, read_timeout=read_timeout, @@ -6541,8 +6481,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def set_passport_data_errors( self, @@ -6580,7 +6518,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"user_id": user_id, "errors": errors} - result = await self._post( + return await self._post( "setPassportDataErrors", data, read_timeout=read_timeout, @@ -6589,7 +6527,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result @_log async def send_poll( @@ -6926,7 +6863,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"rights": rights, "for_channels": for_channels} - result = await self._post( + return await self._post( "setMyDefaultAdministratorRights", data, read_timeout=read_timeout, @@ -6936,8 +6873,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def get_my_commands( self, @@ -7047,7 +6982,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. cmds = [c if isinstance(c, BotCommand) else BotCommand(c[0], c[1]) for c in commands] data: JSONDict = {"commands": cmds, "scope": scope, "language_code": language_code} - result = await self._post( + return await self._post( "setMyCommands", data, read_timeout=read_timeout, @@ -7057,8 +6992,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def delete_my_commands( self, @@ -7097,7 +7030,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. """ data: JSONDict = {"scope": scope, "language_code": language_code} - result = await self._post( + return await self._post( "deleteMyCommands", data, read_timeout=read_timeout, @@ -7107,8 +7040,6 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. api_kwargs=api_kwargs, ) - return result - @_log async def log_out( self, diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index 7ac73d559..610d9ff54 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -41,7 +41,7 @@ class ChatJoinRequest(TelegramObject): Note: * Since Bot API 5.5, bots are allowed to contact users who sent a join request to a chat where the bot is an administrator with the - :attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right – even + :attr:`~telegram.ChatMemberAdministrator.can_invite_users` administrator right - even if the user never interacted with the bot before. * Telegram does not guarantee that :attr:`from_user.id ` coincides with the ``chat_id`` of the user. Please use :attr:`user_chat_id` to contact the user in diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 734ba7d51..b05414108 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -93,8 +93,7 @@ class MenuButton(TelegramObject): if cls is MenuButton and data.get("type") in _class_mapping: return _class_mapping[data.pop("type")].de_json(data, bot=bot) - out = super().de_json(data=data, bot=bot) - return out + return super().de_json(data=data, bot=bot) COMMANDS: ClassVar[str] = constants.MenuButtonType.COMMANDS """:const:`telegram.constants.MenuButtonType.COMMANDS`""" diff --git a/telegram/_message.py b/telegram/_message.py index a9aa44714..7d1a01420 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -829,11 +829,8 @@ class Message(TelegramObject): a private chat or normal group, returns a t.me link of the message. """ if self.chat.type not in [Chat.PRIVATE, Chat.GROUP]: - if self.chat.username: - to_link = self.chat.username - else: - # Get rid of leading -100 for supergroups - to_link = f"c/{str(self.chat.id)[4:]}" + # the else block gets rid of leading -100 for supergroups: + to_link = self.chat.username if self.chat.username else f"c/{str(self.chat.id)[4:]}" return f"https://t.me/{to_link}/{self.message_id}" return None @@ -3268,38 +3265,40 @@ class Message(TelegramObject): parsed_entities.extend(list(nested_entities.keys())) orig_text = text - text = escape(text) + escaped_text = escape(text) if nested_entities: - text = Message._parse_html( + escaped_text = Message._parse_html( orig_text, nested_entities, urled=urled, offset=entity.offset ) if entity.type == MessageEntity.TEXT_LINK: - insert = f'{text}' + insert = f'{escaped_text}' elif entity.type == MessageEntity.TEXT_MENTION and entity.user: - insert = f'{text}' + insert = f'{escaped_text}' elif entity.type == MessageEntity.URL and urled: - insert = f'{text}' + insert = f'{escaped_text}' elif entity.type == MessageEntity.BOLD: - insert = f"{text}" + insert = f"{escaped_text}" elif entity.type == MessageEntity.ITALIC: - insert = f"{text}" + insert = f"{escaped_text}" elif entity.type == MessageEntity.CODE: - insert = f"{text}" + insert = f"{escaped_text}" elif entity.type == MessageEntity.PRE: if entity.language: - insert = f'
{text}
' + insert = ( + f'
{escaped_text}
' + ) else: - insert = f"
{text}
" + insert = f"
{escaped_text}
" elif entity.type == MessageEntity.UNDERLINE: - insert = f"{text}" + insert = f"{escaped_text}" elif entity.type == MessageEntity.STRIKETHROUGH: - insert = f"{text}" + insert = f"{escaped_text}" elif entity.type == MessageEntity.SPOILER: - insert = f'{text}' + insert = f'{escaped_text}' else: - insert = text + insert = escaped_text if offset == 0: if sys.maxunicode == 0xFFFF: @@ -3315,16 +3314,15 @@ class Message(TelegramObject): ) + insert ) + elif sys.maxunicode == 0xFFFF: + html_text += message_text[last_offset : entity.offset - offset] + insert else: - if sys.maxunicode == 0xFFFF: - html_text += message_text[last_offset : entity.offset - offset] + insert - else: - html_text += ( - message_text[ # type: ignore - last_offset * 2 : (entity.offset - offset) * 2 - ].decode("utf-16-le") - + insert - ) + html_text += ( + message_text[ # type: ignore + last_offset * 2 : (entity.offset - offset) * 2 + ].decode("utf-16-le") + + insert + ) last_offset = entity.offset - offset + entity.length @@ -3335,11 +3333,10 @@ class Message(TelegramObject): html_text += escape( message_text[last_offset * 2 :].decode("utf-16-le") # type: ignore ) + elif sys.maxunicode == 0xFFFF: + html_text += message_text[last_offset:] else: - if sys.maxunicode == 0xFFFF: - html_text += message_text[last_offset:] - else: - html_text += message_text[last_offset * 2 :].decode("utf-16-le") # type: ignore + html_text += message_text[last_offset * 2 :].decode("utf-16-le") # type: ignore return html_text @@ -3452,8 +3449,7 @@ class Message(TelegramObject): } parsed_entities.extend(list(nested_entities.keys())) - orig_text = text - text = escape_markdown(text, version=version) + escaped_text = escape_markdown(text, version=version) if nested_entities: if version < 2: @@ -3461,8 +3457,8 @@ class Message(TelegramObject): "Nested entities are not supported for Markdown version 1" ) - text = Message._parse_markdown( - orig_text, + escaped_text = Message._parse_markdown( + text, nested_entities, urled=urled, offset=entity.offset, @@ -3477,56 +3473,50 @@ class Message(TelegramObject): url = escape_markdown( entity.url, version=version, entity_type=MessageEntity.TEXT_LINK ) - insert = f"[{text}]({url})" + insert = f"[{escaped_text}]({url})" elif entity.type == MessageEntity.TEXT_MENTION and entity.user: - insert = f"[{text}](tg://user?id={entity.user.id})" + insert = f"[{escaped_text}](tg://user?id={entity.user.id})" elif entity.type == MessageEntity.URL and urled: - if version == 1: - link = orig_text - else: - link = text - insert = f"[{link}]({orig_text})" + link = text if version == 1 else escaped_text + insert = f"[{link}]({text})" elif entity.type == MessageEntity.BOLD: - insert = f"*{text}*" + insert = f"*{escaped_text}*" elif entity.type == MessageEntity.ITALIC: - insert = f"_{text}_" + insert = f"_{escaped_text}_" elif entity.type == MessageEntity.CODE: # Monospace needs special escaping. Also can't have entities nested within - insert = f"`{escape_markdown(orig_text, version, MessageEntity.CODE)}`" + insert = f"`{escape_markdown(text, version, MessageEntity.CODE)}`" elif entity.type == MessageEntity.PRE: # Monospace needs special escaping. Also can't have entities nested within - code = escape_markdown( - orig_text, version=version, entity_type=MessageEntity.PRE - ) + code = escape_markdown(text, version=version, entity_type=MessageEntity.PRE) if entity.language: prefix = f"```{entity.language}\n" + elif code.startswith("\\"): + prefix = "```" else: - if code.startswith("\\"): - prefix = "```" - else: - prefix = "```\n" + prefix = "```\n" insert = f"{prefix}{code}```" elif entity.type == MessageEntity.UNDERLINE: if version == 1: raise ValueError( "Underline entities are not supported for Markdown version 1" ) - insert = f"__{text}__" + insert = f"__{escaped_text}__" elif entity.type == MessageEntity.STRIKETHROUGH: if version == 1: raise ValueError( "Strikethrough entities are not supported for Markdown version 1" ) - insert = f"~{text}~" + insert = f"~{escaped_text}~" elif entity.type == MessageEntity.SPOILER: if version == 1: raise ValueError( "Spoiler entities are not supported for Markdown version 1" ) - insert = f"||{text}||" + insert = f"||{escaped_text}||" else: - insert = text + insert = escaped_text if offset == 0: if sys.maxunicode == 0xFFFF: @@ -3546,18 +3536,15 @@ class Message(TelegramObject): ) + insert ) + elif sys.maxunicode == 0xFFFF: + markdown_text += message_text[last_offset : entity.offset - offset] + insert else: - if sys.maxunicode == 0xFFFF: - markdown_text += ( - message_text[last_offset : entity.offset - offset] + insert - ) - else: - markdown_text += ( - message_text[ # type: ignore - last_offset * 2 : (entity.offset - offset) * 2 - ].decode("utf-16-le") - + insert - ) + markdown_text += ( + message_text[ # type: ignore + last_offset * 2 : (entity.offset - offset) * 2 + ].decode("utf-16-le") + + insert + ) last_offset = entity.offset - offset + entity.length @@ -3569,13 +3556,10 @@ class Message(TelegramObject): message_text[last_offset * 2 :].decode("utf-16-le"), # type: ignore version=version, ) + elif sys.maxunicode == 0xFFFF: + markdown_text += message_text[last_offset:] else: - if sys.maxunicode == 0xFFFF: - markdown_text += message_text[last_offset:] - else: - markdown_text += message_text[last_offset * 2 :].decode( # type: ignore - "utf-16-le" - ) + markdown_text += message_text[last_offset * 2 :].decode("utf-16-le") # type: ignore return markdown_text diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index f615c1ea8..c7b4de7dd 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -180,7 +180,7 @@ class PassportElementErrorFiles(PassportElementError): with self._unfrozen(): self.file_hashes: str = file_hashes - self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) + self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes)) class PassportElementErrorFrontSide(PassportElementError): @@ -362,7 +362,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): with self._unfrozen(): self.file_hashes: str = file_hashes - self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) + self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes)) class PassportElementErrorUnspecified(PassportElementError): diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index 95572abc4..6b2af30db 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -24,7 +24,7 @@ from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: - from telegram import LabeledPrice # noqa + from telegram import LabeledPrice class ShippingOption(TelegramObject): diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 753e216ab..7c5dd2372 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -281,7 +281,7 @@ class TelegramObject: # Make sure that we have a `_bot` attribute. This is necessary, since __getstate__ omits # this as Bots are not pickable. - setattr(self, "_bot", None) + self._bot = None # get api_kwargs first because we may need to add entries to it (see try-except below) api_kwargs = cast(Dict[str, object], state.pop("api_kwargs", {})) @@ -299,7 +299,7 @@ class TelegramObject: # and then set the rest as MappingProxyType attribute. Converting to MappingProxyType # is necessary, since __getstate__ converts it to a dict as MPT is not pickable. self._apply_api_kwargs(api_kwargs) - setattr(self, "api_kwargs", MappingProxyType(api_kwargs)) + self.api_kwargs = MappingProxyType(api_kwargs) # Apply freezing if necessary # we .get(…) the setting for backwards compatibility with objects that were pickled @@ -328,7 +328,7 @@ class TelegramObject: result = cls.__new__(cls) # create a new instance memodict[id(self)] = result # save the id of the object in the dict - setattr(result, "_frozen", False) # unfreeze the new object for setting the attributes + result._frozen = False # unfreeze the new object for setting the attributes # now we set the attributes in the deepcopied object for k in self._get_attrs_names(include_private=True): diff --git a/telegram/_update.py b/telegram/_update.py index 143986fa6..771b68732 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -34,7 +34,7 @@ from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict if TYPE_CHECKING: - from telegram import Bot, Chat, User # noqa + from telegram import Bot, Chat, User class Update(TelegramObject): @@ -46,7 +46,7 @@ class Update(TelegramObject): Note: At most one of the optional parameters can be present in any given update. - .. seealso:: :wiki:`Your First Bot ` + .. seealso:: :wiki:`Your First Bot ` Args: update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a diff --git a/telegram/error.py b/telegram/error.py index 717ac05d3..1f9fdec63 100644 --- a/telegram/error.py +++ b/telegram/error.py @@ -48,11 +48,7 @@ def _lstrip_str(in_s: str, lstr: str) -> str: :obj:`str`: The stripped string. """ - if in_s.startswith(lstr): - res = in_s[len(lstr) :] - else: - res = in_s - return res + return in_s[len(lstr) :] if in_s.startswith(lstr) else in_s class TelegramError(Exception): diff --git a/telegram/ext/_aioratelimiter.py b/telegram/ext/_aioratelimiter.py index 691ab3825..c948075ac 100644 --- a/telegram/ext/_aioratelimiter.py +++ b/telegram/ext/_aioratelimiter.py @@ -203,7 +203,7 @@ class AIORateLimiter(BaseRateLimiter[int]): return await callback(*args, **kwargs) # mypy doesn't understand that the last run of the for loop raises an exception - async def process_request( # type: ignore[return] + async def process_request( self, callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], args: Any, @@ -232,10 +232,8 @@ class AIORateLimiter(BaseRateLimiter[int]): chat = True # In case user passes integer chat id as string - try: + with contextlib.suppress(ValueError, TypeError): chat_id = int(chat_id) - except (ValueError, TypeError): - pass if (isinstance(chat_id, int) and chat_id < 0) or isinstance(chat_id, str): # string chat_id only works for channels and supergroups @@ -262,3 +260,4 @@ class AIORateLimiter(BaseRateLimiter[int]): finally: # Allow other requests to be processed self._retry_after_event.set() + return None # type: ignore[return-value] diff --git a/telegram/ext/_application.py b/telegram/ext/_application.py index ddb9c0d4e..716d972a1 100644 --- a/telegram/ext/_application.py +++ b/telegram/ext/_application.py @@ -18,6 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the Application class.""" import asyncio +import contextlib import inspect import itertools import logging @@ -74,6 +75,7 @@ DEFAULT_GROUP: int = 0 _AppType = TypeVar("_AppType", bound="Application") # pylint: disable=invalid-name _STOP_SIGNAL = object() +_DEFAULT_0 = DefaultValue(0) _logger = logging.getLogger(__name__) @@ -137,7 +139,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica Examples: :any:`Echo Bot ` - .. seealso:: :wiki:`Your First Bot `, + .. seealso:: :wiki:`Your First Bot `, :wiki:`Architecture Overview ` .. versionchanged:: 20.0 @@ -997,10 +999,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica self.__create_task_tasks.discard(task) # Discard from our set since we are done with it # We just retrieve the eventual exception so that asyncio doesn't complain in case # it's not retrieved somewhere else - try: + with contextlib.suppress(asyncio.CancelledError, asyncio.InvalidStateError): task.exception() - except (asyncio.CancelledError, asyncio.InvalidStateError): - pass async def __create_task_callback( self, @@ -1200,7 +1200,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica Union[List[BaseHandler[Any, CCT]], Tuple[BaseHandler[Any, CCT]]], Dict[int, Union[List[BaseHandler[Any, CCT]], Tuple[BaseHandler[Any, CCT]]]], ], - group: Union[int, DefaultValue[int]] = DefaultValue(0), + group: Union[int, DefaultValue[int]] = _DEFAULT_0, ) -> None: """Registers multiple handlers at once. The order of the handlers in the passed sequence(s) matters. See :meth:`add_handler` for details. diff --git a/telegram/ext/_applicationbuilder.py b/telegram/ext/_applicationbuilder.py index 7c8876d60..909e55a72 100644 --- a/telegram/ext/_applicationbuilder.py +++ b/telegram/ext/_applicationbuilder.py @@ -114,7 +114,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]): * Unless a custom :class:`telegram.Bot` instance is set via :meth:`bot`, :meth:`build` will use :class:`telegram.ext.ExtBot` for the bot. - .. seealso:: :wiki:`Your First Bot `, + .. seealso:: :wiki:`Your First Bot `, :wiki:`Builder Pattern ` .. _`builder pattern`: https://en.wikipedia.org/wiki/Builder_pattern diff --git a/telegram/ext/_callbackcontext.py b/telegram/ext/_callbackcontext.py index b4b4f1b56..52894d551 100644 --- a/telegram/ext/_callbackcontext.py +++ b/telegram/ext/_callbackcontext.py @@ -41,7 +41,7 @@ from telegram.ext._utils.types import BD, BT, CD, UD if TYPE_CHECKING: from asyncio import Future, Queue - from telegram.ext import Application, Job, JobQueue # noqa: F401 + from telegram.ext import Application, Job, JobQueue from telegram.ext._utils.types import CCT _STORING_DATA_WIKI = ( diff --git a/telegram/ext/_conversationhandler.py b/telegram/ext/_conversationhandler.py index ef2f4a474..884b38728 100644 --- a/telegram/ext/_conversationhandler.py +++ b/telegram/ext/_conversationhandler.py @@ -654,7 +654,7 @@ class ConversationHandler(BaseHandler[Update, CCT]): "timeout.", exc_info=exc, ) - return + return None return self._schedule_job( new_state=effective_new_state, application=application, @@ -813,13 +813,12 @@ class ConversationHandler(BaseHandler[Update, CCT]): # 3. Default values of the bot if handler.block is not DEFAULT_TRUE: block = handler.block + elif self._block is not DEFAULT_TRUE: + block = self._block + elif isinstance(application.bot, ExtBot) and application.bot.defaults is not None: + block = application.bot.defaults.block else: - if self._block is not DEFAULT_TRUE: - block = self._block - elif isinstance(application.bot, ExtBot) and application.bot.defaults is not None: - block = application.bot.defaults.block - else: - block = DefaultValue.get_value(handler.block) + block = DefaultValue.get_value(handler.block) try: # Now create task or await the callback if block: @@ -847,20 +846,17 @@ class ConversationHandler(BaseHandler[Update, CCT]): "Ignoring `conversation_timeout` because the Applications JobQueue is " "not running.", ) - else: - # Add the new timeout job - # checking if the new state is self.END is done in _schedule_job - if isinstance(new_state, asyncio.Task): - application.create_task( - self._schedule_job_delayed( - new_state, application, update, context, conversation_key - ), - update=update, - ) - else: - self._schedule_job( + # Add the new timeout job + # checking if the new state is self.END is done in _schedule_job + elif isinstance(new_state, asyncio.Task): + application.create_task( + self._schedule_job_delayed( new_state, application, update, context, conversation_key - ) + ), + update=update, + ) + else: + self._schedule_job(new_state, application, update, context, conversation_key) if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent: self._update_state(self.END, conversation_key, handler) @@ -874,7 +870,7 @@ class ConversationHandler(BaseHandler[Update, CCT]): if raise_dp_handler_stop: # Don't pass the new state here. If we're in a nested conversation, the parent is # expecting None as return value. - raise ApplicationHandlerStop() + raise ApplicationHandlerStop # Signals a possible parent conversation to stay in the current state return None diff --git a/telegram/ext/_dictpersistence.py b/telegram/ext/_dictpersistence.py index 1111d7865..a328c6212 100644 --- a/telegram/ext/_dictpersistence.py +++ b/telegram/ext/_dictpersistence.py @@ -468,12 +468,12 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, tmp: Dict[int, Dict[object, object]] = {} decoded_data = json.loads(data) for user, user_data in decoded_data.items(): - user = int(user) - tmp[user] = {} + int_user_id = int(user) + tmp[int_user_id] = {} for key, value in user_data.items(): try: - key = int(key) + _id = int(key) except ValueError: - pass - tmp[user][key] = value + _id = key + tmp[int_user_id][_id] = value return tmp diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 56abd63ab..fb87e49a8 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -385,10 +385,10 @@ class ExtBot(Bot, Generic[RLARGS]): # 3) elif isinstance(val, InputMedia) and val.parse_mode is DEFAULT_NONE: # Copy object as not to edit it in-place - val = copy(val) - with val._unfrozen(): - val.parse_mode = self.defaults.parse_mode if self.defaults else None - data[key] = val + copied_val = copy(val) + with copied_val._unfrozen(): + copied_val.parse_mode = self.defaults.parse_mode if self.defaults else None + data[key] = copied_val elif key == "media" and isinstance(val, Sequence): # Copy objects as not to edit them in-place copy_list = [copy(media) for media in val] diff --git a/telegram/ext/_jobqueue.py b/telegram/ext/_jobqueue.py index 4a84e716e..7e45374d1 100644 --- a/telegram/ext/_jobqueue.py +++ b/telegram/ext/_jobqueue.py @@ -41,6 +41,9 @@ if TYPE_CHECKING: from telegram.ext import Application +_ALL_DAYS = tuple(range(7)) + + class JobQueue(Generic[CCT]): """This class allows you to periodically perform tasks with the bot. It is a convenience wrapper for the APScheduler library. @@ -436,7 +439,7 @@ class JobQueue(Generic[CCT]): self, callback: JobCallback[CCT], time: datetime.time, - days: Tuple[int, ...] = tuple(range(7)), + days: Tuple[int, ...] = _ALL_DAYS, data: object = None, name: str = None, chat_id: int = None, diff --git a/telegram/ext/_messagehandler.py b/telegram/ext/_messagehandler.py index 0deb2efe0..eff8b3c77 100644 --- a/telegram/ext/_messagehandler.py +++ b/telegram/ext/_messagehandler.py @@ -47,7 +47,7 @@ class MessageHandler(BaseHandler[Update, CCT]): operators (& for and, | for or, ~ for not). Passing :obj:`None` is a shortcut to passing :class:`telegram.ext.filters.ALL`. - .. seealso:: :wiki:`Advanced Filters ` + .. seealso:: :wiki:`Advanced Filters ` callback (:term:`coroutine function`): The callback function for this handler. Will be called when :meth:`check_update` has determined that an update should be processed by this handler. Callback signature:: diff --git a/telegram/ext/_prefixhandler.py b/telegram/ext/_prefixhandler.py index 5026457c6..35e0cbffe 100644 --- a/telegram/ext/_prefixhandler.py +++ b/telegram/ext/_prefixhandler.py @@ -132,15 +132,9 @@ class PrefixHandler(BaseHandler[Update, CCT]): ): super().__init__(callback=callback, block=block) - if isinstance(prefix, str): - prefixes = {prefix.lower()} - else: - prefixes = {x.lower() for x in prefix} + prefixes = {prefix.lower()} if isinstance(prefix, str) else {x.lower() for x in prefix} - if isinstance(command, str): - commands = {command.lower()} - else: - commands = {x.lower() for x in command} + commands = {command.lower()} if isinstance(command, str) else {x.lower() for x in command} self.commands: FrozenSet[str] = frozenset( p + c for p, c in itertools.product(prefixes, commands) diff --git a/telegram/ext/_updater.py b/telegram/ext/_updater.py index 7f0f376af..73ca65c3f 100644 --- a/telegram/ext/_updater.py +++ b/telegram/ext/_updater.py @@ -18,6 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the class Updater, which tries to make creating Telegram bots intuitive.""" import asyncio +import contextlib import logging import ssl from pathlib import Path @@ -746,11 +747,9 @@ class Updater(AsyncContextManager["Updater"]): self._logger.debug("Waiting background polling task to finish up.") self.__polling_task.cancel() - try: + with contextlib.suppress(asyncio.CancelledError): await self.__polling_task - except asyncio.CancelledError: - # This only happens in rare edge-cases, e.g. when `stop()` is called directly + # It only fails in rare edge-cases, e.g. when `stop()` is called directly # after start_polling(), but lets better be safe than sorry ... - pass self.__polling_task = None diff --git a/telegram/ext/_utils/webhookhandler.py b/telegram/ext/_utils/webhookhandler.py index ee17a6462..364364633 100644 --- a/telegram/ext/_utils/webhookhandler.py +++ b/telegram/ext/_utils/webhookhandler.py @@ -93,7 +93,7 @@ class WebhookAppClass(tornado.web.Application): "update_queue": update_queue, "secret_token": secret_token, } - handlers = [(rf"{webhook_path}/?", TelegramHandler, self.shared_objects)] # noqa + handlers = [(rf"{webhook_path}/?", TelegramHandler, self.shared_objects)] tornado.web.Application.__init__(self, handlers) # type: ignore def log_request(self, handler: tornado.web.RequestHandler) -> None: diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index c8e6d23f7..ba6db52ee 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -244,7 +244,7 @@ class MessageFilter(BaseFilter): Please see :class:`BaseFilter` for details on how to create custom filters. - .. seealso:: :wiki:`Advanced Filters ` + .. seealso:: :wiki:`Advanced Filters ` """ @@ -379,7 +379,7 @@ class _MergedFilter(UpdateFilter): def _merge(base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> FilterDataDict: base = base_output if isinstance(base_output, dict) else {} comp = comp_output if isinstance(comp_output, dict) else {} - for k in comp.keys(): + for k in comp: # Make sure comp values are lists comp_value = comp[k] if isinstance(comp[k], list) else [] try: @@ -387,7 +387,7 @@ class _MergedFilter(UpdateFilter): if isinstance(base[k], list): base[k] += comp_value else: - base[k] = [base[k]] + comp_value + base[k] = [base[k], *comp_value] except KeyError: base[k] = comp_value return base diff --git a/telegram/helpers.py b/telegram/helpers.py index bf43de766..632cd3647 100644 --- a/telegram/helpers.py +++ b/telegram/helpers.py @@ -162,6 +162,11 @@ def create_deep_linked_url(bot_username: str, payload: str = None, group: bool = Returns: :obj:`str`: An URL to start the bot with specific parameters. + + Raises: + :exc:`ValueError`: If the length of the :paramref:`payload` exceeds 64 characters, + contains invalid characters, or if the :paramref:`bot_username` is less than 4 + characters. """ if bot_username is None or len(bot_username) <= 3: raise ValueError("You must provide a valid bot_username.") @@ -179,9 +184,6 @@ def create_deep_linked_url(bot_username: str, payload: str = None, group: bool = "URLs: A-Z, a-z, 0-9, _ and -" ) - if group: - key = "startgroup" - else: - key = "start" + key = "startgroup" if group else "start" return f"{base_url}?{key}={payload}" diff --git a/telegram/request/_baserequest.py b/telegram/request/_baserequest.py index 97445cbe9..6c45bbf70 100644 --- a/telegram/request/_baserequest.py +++ b/telegram/request/_baserequest.py @@ -296,10 +296,7 @@ class BaseRequest( response_data = self.parse_json_payload(payload) description = response_data.get("description") - if description: - message = description - else: - message = "Unknown HTTPError" + message = description if description else "Unknown HTTPError" # In some special cases, we can raise more informative exceptions: # see https://core.telegram.org/bots/api#responseparameters and diff --git a/telegram/request/_httpxrequest.py b/telegram/request/_httpxrequest.py index ae1444c86..837162b41 100644 --- a/telegram/request/_httpxrequest.py +++ b/telegram/request/_httpxrequest.py @@ -122,7 +122,7 @@ class HTTPXRequest(BaseRequest): # See https://github.com/python-telegram-bot/python-telegram-bot/pull/3542 # for why we need to use `dict()` here. - self._client_kwargs = dict( # pylint: disable=use-dict-literal + self._client_kwargs = dict( # pylint: disable=use-dict-literal # noqa: C408 timeout=timeout, proxies=proxy_url, limits=limits, diff --git a/tests/_files/test_animation.py b/tests/_files/test_animation.py index b7fe2e2d6..85c5d9c92 100644 --- a/tests/_files/test_animation.py +++ b/tests/_files/test_animation.py @@ -36,7 +36,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def animation_file(): with data_file("game.gif").open("rb") as f: yield f @@ -75,8 +75,8 @@ class TestAnimationWithoutRequest(TestAnimationBase): assert isinstance(animation, Animation) assert isinstance(animation.file_id, str) assert isinstance(animation.file_unique_id, str) - assert animation.file_id != "" - assert animation.file_unique_id != "" + assert animation.file_id + assert animation.file_unique_id def test_expected_values(self, animation): assert animation.mime_type == self.mime_type @@ -231,8 +231,8 @@ class TestAnimationWithRequest(TestAnimationBase): assert isinstance(message.animation, Animation) assert isinstance(message.animation.file_id, str) assert isinstance(message.animation.file_unique_id, str) - assert message.animation.file_id != "" - assert message.animation.file_unique_id != "" + assert message.animation.file_id + assert message.animation.file_unique_id assert message.animation.file_name == animation.file_name assert message.animation.mime_type == animation.mime_type assert message.animation.file_size == animation.file_size @@ -266,8 +266,8 @@ class TestAnimationWithRequest(TestAnimationBase): assert isinstance(message.animation, Animation) assert isinstance(message.animation.file_id, str) assert isinstance(message.animation.file_unique_id, str) - assert message.animation.file_id != "" - assert message.animation.file_unique_id != "" + assert message.animation.file_id + assert message.animation.file_unique_id assert message.animation.duration == animation.duration assert message.animation.file_name.startswith( @@ -321,7 +321,7 @@ class TestAnimationWithRequest(TestAnimationBase): assert message.caption_markdown == escape_markdown(test_markdown_string) @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -368,9 +368,7 @@ class TestAnimationWithRequest(TestAnimationBase): assert message.animation == animation async def test_error_send_empty_file(self, bot, chat_id): - animation_file = open(os.devnull, "rb") - - with pytest.raises(TelegramError): + with Path(os.devnull).open("rb") as animation_file, pytest.raises(TelegramError): await bot.send_animation(chat_id=chat_id, animation=animation_file) async def test_error_send_empty_file_id(self, bot, chat_id): diff --git a/tests/_files/test_audio.py b/tests/_files/test_audio.py index c8fe7cfc9..b8a4c44a1 100644 --- a/tests/_files/test_audio.py +++ b/tests/_files/test_audio.py @@ -36,7 +36,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def audio_file(): with data_file("telegram.mp3").open("rb") as f: yield f @@ -77,8 +77,8 @@ class TestAudioWithoutRequest(TestAudioBase): assert isinstance(audio, Audio) assert isinstance(audio.file_id, str) assert isinstance(audio.file_unique_id, str) - assert audio.file_id != "" - assert audio.file_unique_id != "" + assert audio.file_id + assert audio.file_unique_id def test_expected_values(self, audio): assert audio.duration == self.duration @@ -331,9 +331,7 @@ class TestAudioWithRequest(TestAudioBase): assert message.audio == audio async def test_error_send_empty_file(self, bot, chat_id): - audio_file = open(os.devnull, "rb") - - with pytest.raises(TelegramError): + with Path(os.devnull).open("rb") as audio_file, pytest.raises(TelegramError): await bot.send_audio(chat_id=chat_id, audio=audio_file) async def test_error_send_empty_file_id(self, bot, chat_id): diff --git a/tests/_files/test_chatphoto.py b/tests/_files/test_chatphoto.py index d759e1460..d15ac6181 100644 --- a/tests/_files/test_chatphoto.py +++ b/tests/_files/test_chatphoto.py @@ -36,7 +36,7 @@ from tests.auxil.networking import expect_bad_request from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def chatphoto_file(): with data_file("telegram.jpg").open("rb") as f: yield f @@ -174,7 +174,8 @@ class TestChatPhotoWithRequest: await file.download_to_drive(jpg_file) assert jpg_file.is_file() - assert "small" in asserts and "big" in asserts + assert "small" in asserts + assert "big" in asserts async def test_send_all_args(self, bot, super_group_id, chatphoto_file): async def func(): @@ -185,9 +186,7 @@ class TestChatPhotoWithRequest: ) async def test_error_send_empty_file(self, bot, super_group_id): - chatphoto_file = open(os.devnull, "rb") - - with pytest.raises(TelegramError): + with Path(os.devnull).open("rb") as chatphoto_file, pytest.raises(TelegramError): await bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file) async def test_error_send_empty_file_id(self, bot, super_group_id): diff --git a/tests/_files/test_contact.py b/tests/_files/test_contact.py index 696eea595..d7906a9ee 100644 --- a/tests/_files/test_contact.py +++ b/tests/_files/test_contact.py @@ -129,7 +129,7 @@ class TestContactWithoutRequest(TestContactBase): class TestContactWithRequest(TestContactBase): @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), diff --git a/tests/_files/test_document.py b/tests/_files/test_document.py index 429d7b61f..a447aeb81 100644 --- a/tests/_files/test_document.py +++ b/tests/_files/test_document.py @@ -36,7 +36,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def document_file(): with data_file("telegram.png").open("rb") as f: yield f @@ -71,8 +71,8 @@ class TestDocumentWithoutRequest(TestDocumentBase): assert isinstance(document, Document) assert isinstance(document.file_id, str) assert isinstance(document.file_unique_id, str) - assert document.file_id != "" - assert document.file_unique_id != "" + assert document.file_id + assert document.file_unique_id def test_expected_values(self, document): assert document.file_size == self.file_size @@ -206,9 +206,8 @@ class TestDocumentWithoutRequest(TestDocumentBase): class TestDocumentWithRequest(TestDocumentBase): async def test_error_send_empty_file(self, bot, chat_id): - with open(os.devnull, "rb") as f: - with pytest.raises(TelegramError): - await bot.send_document(chat_id=chat_id, document=f) + with Path(os.devnull).open("rb") as f, pytest.raises(TelegramError): + await bot.send_document(chat_id=chat_id, document=f) async def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): @@ -247,9 +246,9 @@ class TestDocumentWithRequest(TestDocumentBase): assert isinstance(message.document, Document) assert isinstance(message.document.file_id, str) - assert message.document.file_id != "" + assert message.document.file_id assert isinstance(message.document.file_unique_id, str) - assert message.document.file_unique_id != "" + assert message.document.file_unique_id assert isinstance(message.document.thumbnail, PhotoSize) assert message.document.file_name == "telegram_custom.png" assert message.document.mime_type == document.mime_type @@ -266,9 +265,9 @@ class TestDocumentWithRequest(TestDocumentBase): assert isinstance(document, Document) assert isinstance(document.file_id, str) - assert document.file_id != "" + assert document.file_id assert isinstance(message.document.file_unique_id, str) - assert message.document.file_unique_id != "" + assert message.document.file_unique_id assert isinstance(document.thumbnail, PhotoSize) assert document.file_name == "telegram.gif" assert document.mime_type == "image/gif" @@ -328,7 +327,7 @@ class TestDocumentWithRequest(TestDocumentBase): assert message.caption_markdown == escape_markdown(test_markdown_string) @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), diff --git a/tests/_files/test_inputfile.py b/tests/_files/test_inputfile.py index f657b2d2c..c772192df 100644 --- a/tests/_files/test_inputfile.py +++ b/tests/_files/test_inputfile.py @@ -16,6 +16,7 @@ # # 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 contextlib import subprocess import sys from io import BytesIO @@ -49,12 +50,10 @@ class TestInputFileWithoutRequest: assert in_file.mimetype == "application/octet-stream" assert in_file.filename == "application.octet-stream" - try: + with contextlib.suppress(ProcessLookupError): proc.kill() - except ProcessLookupError: # This exception may be thrown if the process has finished before we had the chance # to kill it. - pass @pytest.mark.parametrize("attach", [True, False]) def test_attach(self, attach): diff --git a/tests/_files/test_inputmedia.py b/tests/_files/test_inputmedia.py index 82ba6279b..ef99b2d14 100644 --- a/tests/_files/test_inputmedia.py +++ b/tests/_files/test_inputmedia.py @@ -54,7 +54,7 @@ from .test_audio import audio, audio_file # noqa: F401 from .test_document import document, document_file # noqa: F401 # noinspection PyUnresolvedReferences -from .test_photo import _photo, photo, photo_file, thumb # noqa: F401 +from .test_photo import photo, photo_file, photolist, thumb # noqa: F401 # noinspection PyUnresolvedReferences from .test_video import video, video_file # noqa: F401 @@ -211,7 +211,7 @@ class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase): assert input_media_video.thumbnail == data_file("telegram.jpg").as_uri() def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="You passed different entities as 'thumb' and "): InputMediaVideo( data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg"), @@ -351,7 +351,7 @@ class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase): assert input_media_animation.thumbnail == data_file("telegram.jpg").as_uri() def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="You passed different entities as 'thumb' and "): InputMediaAnimation( data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg"), @@ -436,7 +436,7 @@ class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase): assert input_media_audio.thumbnail == data_file("telegram.jpg").as_uri() def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="You passed different entities as 'thumb' and "): InputMediaAudio( data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg"), @@ -518,7 +518,7 @@ class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase): assert input_media_document.thumbnail == data_file("telegram.jpg").as_uri() def test_with_local_files_throws_exception_with_different_thumb_and_thumbnail(self): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="You passed different entities as 'thumb' and "): InputMediaDocument( data_file("telegram.mp4"), thumbnail=data_file("telegram.jpg"), @@ -526,7 +526,7 @@ class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase): ) -@pytest.fixture(scope="module") # noqa: F811 +@pytest.fixture(scope="module") def media_group(photo, thumb): # noqa: F811 return [ InputMediaPhoto(photo, caption="*photo* 1", parse_mode="Markdown"), @@ -537,12 +537,12 @@ def media_group(photo, thumb): # noqa: F811 ] -@pytest.fixture(scope="module") # noqa: F811 +@pytest.fixture(scope="module") def media_group_no_caption_args(photo, thumb): # noqa: F811 return [InputMediaPhoto(photo), InputMediaPhoto(thumb), InputMediaPhoto(photo)] -@pytest.fixture(scope="module") # noqa: F811 +@pytest.fixture(scope="module") def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811 return [ InputMediaPhoto(photo, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)]), @@ -550,7 +550,7 @@ def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811 ] -@pytest.fixture(scope="module") # noqa: F811 +@pytest.fixture(scope="module") def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811 return [ InputMediaPhoto(photo, parse_mode="Markdown"), @@ -720,7 +720,7 @@ class TestSendMediaGroupWithRequest: assert all(i.message_thread_id == real_topic.message_thread_id for i in messages) @pytest.mark.parametrize( - "caption, parse_mode, caption_entities", + ("caption", "parse_mode", "caption_entities"), [ # same combinations of caption options as in media_group fixture ("*photo* 1", "Markdown", None), @@ -852,7 +852,7 @@ class TestSendMediaGroupWithRequest: assert isinstance(new_message, Message) @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -984,6 +984,7 @@ class TestSendMediaGroupWithRequest: return InputMediaPhoto(photo, **kwargs) if med_type == "video": return InputMediaVideo(video, **kwargs) + return None message = await default_bot.send_photo(chat_id, photo) diff --git a/tests/_files/test_inputsticker.py b/tests/_files/test_inputsticker.py index d1e2d463b..780a7fbba 100644 --- a/tests/_files/test_inputsticker.py +++ b/tests/_files/test_inputsticker.py @@ -51,7 +51,8 @@ class TestInputStickerNoRequest(TestInputStickerBase): assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" def test_expected_values(self, input_sticker): - assert input_sticker.sticker == self.sticker and isinstance(input_sticker.sticker, str) + assert input_sticker.sticker == self.sticker + assert isinstance(input_sticker.sticker, str) assert input_sticker.emoji_list == self.emoji_list assert input_sticker.mask_position == self.mask_position assert input_sticker.keywords == self.keywords @@ -60,7 +61,8 @@ class TestInputStickerNoRequest(TestInputStickerBase): assert isinstance(input_sticker.keywords, tuple) assert isinstance(input_sticker.emoji_list, tuple) a = InputSticker("sticker", ["emoji"]) - assert isinstance(a.emoji_list, tuple) and a.keywords == () + assert isinstance(a.emoji_list, tuple) + assert a.keywords == () def test_to_dict(self, input_sticker): input_sticker_dict = input_sticker.to_dict() diff --git a/tests/_files/test_location.py b/tests/_files/test_location.py index 0fcef8e0a..aef25c970 100644 --- a/tests/_files/test_location.py +++ b/tests/_files/test_location.py @@ -136,8 +136,7 @@ class TestLocationWithoutRequest(TestLocationBase): # TODO: Needs improvement with in inline sent live location. async def test_stop_live_inline_message(self, monkeypatch, bot): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - id_ = request_data.json_parameters["inline_message_id"] == "1234" - return id_ + return request_data.json_parameters["inline_message_id"] == "1234" monkeypatch.setattr(bot.request, "post", make_assertion) assert await bot.stop_message_live_location(inline_message_id=1234) @@ -163,7 +162,7 @@ class TestLocationWithoutRequest(TestLocationBase): class TestLocationWithRequest: @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -205,7 +204,7 @@ class TestLocationWithRequest: assert protected.has_protected_content assert not unprotected.has_protected_content - @pytest.mark.xfail + @pytest.mark.xfail() async def test_send_live_location(self, bot, chat_id): message = await bot.send_location( chat_id=chat_id, @@ -218,8 +217,8 @@ class TestLocationWithRequest: protect_content=True, ) assert message.location - assert 52.223880 == pytest.approx(message.location.latitude, rel=1e-5) - assert 5.166146 == pytest.approx(message.location.longitude, rel=1e-5) + assert pytest.approx(message.location.latitude, rel=1e-5) == 52.223880 + assert pytest.approx(message.location.longitude, rel=1e-5) == 5.166146 assert message.location.live_period == 80 assert message.location.horizontal_accuracy == 50 assert message.location.heading == 90 @@ -236,8 +235,8 @@ class TestLocationWithRequest: proximity_alert_radius=500, ) - assert 52.223098 == pytest.approx(message2.location.latitude, rel=1e-5) - assert 5.164306 == pytest.approx(message2.location.longitude, rel=1e-5) + assert pytest.approx(message2.location.latitude, rel=1e-5) == 52.223098 + assert pytest.approx(message2.location.longitude, rel=1e-5) == 5.164306 assert message2.location.horizontal_accuracy == 30 assert message2.location.heading == 10 assert message2.location.proximity_alert_radius == 500 diff --git a/tests/_files/test_photo.py b/tests/_files/test_photo.py index 099dfbe63..939b7d7ab 100644 --- a/tests/_files/test_photo.py +++ b/tests/_files/test_photo.py @@ -36,14 +36,14 @@ from tests.auxil.networking import expect_bad_request from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def photo_file(): with data_file("telegram.jpg").open("rb") as f: yield f @pytest.fixture(scope="module") -async def _photo(bot, chat_id): +async def photolist(bot, chat_id): async def func(): with data_file("telegram.jpg").open("rb") as f: return (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo @@ -54,13 +54,13 @@ async def _photo(bot, chat_id): @pytest.fixture(scope="module") -def thumb(_photo): - return _photo[0] +def thumb(photolist): + return photolist[0] @pytest.fixture(scope="module") -def photo(_photo): - return _photo[-1] +def photo(photolist): + return photolist[-1] class TestPhotoBase: @@ -84,14 +84,14 @@ class TestPhotoWithoutRequest(TestPhotoBase): assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != "" - assert photo.file_unique_id != "" + assert photo.file_id + assert photo.file_unique_id assert isinstance(thumb, PhotoSize) assert isinstance(thumb.file_id, str) assert isinstance(thumb.file_unique_id, str) - assert thumb.file_id != "" - assert thumb.file_unique_id != "" + assert thumb.file_id + assert thumb.file_unique_id def test_expected_values(self, photo, thumb): assert photo.width == self.width @@ -223,14 +223,14 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(message.photo[-2], PhotoSize) assert isinstance(message.photo[-2].file_id, str) assert isinstance(message.photo[-2].file_unique_id, str) - assert message.photo[-2].file_id != "" - assert message.photo[-2].file_unique_id != "" + assert message.photo[-2].file_id + assert message.photo[-2].file_unique_id assert isinstance(message.photo[-1], PhotoSize) assert isinstance(message.photo[-1].file_id, str) assert isinstance(message.photo[-1].file_unique_id, str) - assert message.photo[-1].file_id != "" - assert message.photo[-1].file_unique_id != "" + assert message.photo[-1].file_id + assert message.photo[-1].file_unique_id assert message.caption == self.caption.replace("*", "") assert message.has_protected_content @@ -243,14 +243,14 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(message.photo[-2], PhotoSize) assert isinstance(message.photo[-2].file_id, str) assert isinstance(message.photo[-2].file_unique_id, str) - assert message.photo[-2].file_id != "" - assert message.photo[-2].file_unique_id != "" + assert message.photo[-2].file_id + assert message.photo[-2].file_unique_id assert isinstance(message.photo[-1], PhotoSize) assert isinstance(message.photo[-1].file_id, str) assert isinstance(message.photo[-1].file_unique_id, str) - assert message.photo[-1].file_id != "" - assert message.photo[-1].file_unique_id != "" + assert message.photo[-1].file_id + assert message.photo[-1].file_unique_id assert message.caption == self.caption.replace("*", "") assert len(message.caption_entities) == 1 @@ -262,14 +262,14 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(message.photo[-2], PhotoSize) assert isinstance(message.photo[-2].file_id, str) assert isinstance(message.photo[-2].file_unique_id, str) - assert message.photo[-2].file_id != "" - assert message.photo[-2].file_unique_id != "" + assert message.photo[-2].file_id + assert message.photo[-2].file_unique_id assert isinstance(message.photo[-1], PhotoSize) assert isinstance(message.photo[-1].file_id, str) assert isinstance(message.photo[-1].file_unique_id, str) - assert message.photo[-1].file_id != "" - assert message.photo[-1].file_unique_id != "" + assert message.photo[-1].file_id + assert message.photo[-1].file_unique_id assert message.caption == self.caption.replace("", "").replace("", "") assert len(message.caption_entities) == 1 @@ -328,7 +328,7 @@ class TestPhotoWithRequest(TestPhotoBase): assert not unprotected.has_protected_content @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -381,14 +381,14 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(message.photo[-2], PhotoSize) assert isinstance(message.photo[-2].file_id, str) assert isinstance(message.photo[-2].file_unique_id, str) - assert message.photo[-2].file_id != "" - assert message.photo[-2].file_unique_id != "" + assert message.photo[-2].file_id + assert message.photo[-2].file_unique_id assert isinstance(message.photo[-1], PhotoSize) assert isinstance(message.photo[-1].file_id, str) assert isinstance(message.photo[-1].file_unique_id, str) - assert message.photo[-1].file_id != "" - assert message.photo[-1].file_unique_id != "" + assert message.photo[-1].file_id + assert message.photo[-1].file_unique_id async def test_send_url_png_file(self, bot, chat_id): message = await bot.send_photo( @@ -400,8 +400,8 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != "" - assert photo.file_unique_id != "" + assert photo.file_id + assert photo.file_unique_id async def test_send_file_unicode_filename(self, bot, chat_id): """ @@ -415,8 +415,8 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != "" - assert photo.file_unique_id != "" + assert photo.file_id + assert photo.file_unique_id async def test_send_bytesio_jpg_file(self, bot, chat_id): filepath = data_file("telegram_no_standard_header.jpg") @@ -438,8 +438,8 @@ class TestPhotoWithRequest(TestPhotoBase): photo = message.photo[-1] assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != "" - assert photo.file_unique_id != "" + assert photo.file_id + assert photo.file_unique_id assert isinstance(photo, PhotoSize) assert photo.width == 1280 assert photo.height == 720 @@ -451,18 +451,18 @@ class TestPhotoWithRequest(TestPhotoBase): assert isinstance(message.photo[-2], PhotoSize) assert isinstance(message.photo[-2].file_id, str) assert isinstance(message.photo[-2].file_unique_id, str) - assert message.photo[-2].file_id != "" - assert message.photo[-2].file_unique_id != "" + assert message.photo[-2].file_id + assert message.photo[-2].file_unique_id assert isinstance(message.photo[-1], PhotoSize) assert isinstance(message.photo[-1].file_id, str) assert isinstance(message.photo[-1].file_unique_id, str) - assert message.photo[-1].file_id != "" - assert message.photo[-1].file_unique_id != "" + assert message.photo[-1].file_id + assert message.photo[-1].file_unique_id async def test_error_send_empty_file(self, bot, chat_id): - with pytest.raises(TelegramError): - await bot.send_photo(chat_id=chat_id, photo=open(os.devnull, "rb")) + with Path(os.devnull).open("rb") as file, pytest.raises(TelegramError): + await bot.send_photo(chat_id=chat_id, photo=file) async def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): diff --git a/tests/_files/test_sticker.py b/tests/_files/test_sticker.py index dddf42d92..433563a43 100644 --- a/tests/_files/test_sticker.py +++ b/tests/_files/test_sticker.py @@ -48,7 +48,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def sticker_file(): with data_file("telegram.webp").open("rb") as file: yield file @@ -64,7 +64,7 @@ async def sticker(bot, chat_id): return sticker -@pytest.fixture(scope="function") +@pytest.fixture() def animated_sticker_file(): with data_file("telegram_animated_sticker.tgs").open("rb") as f: yield f @@ -76,7 +76,7 @@ async def animated_sticker(bot, chat_id): return (await bot.send_sticker(chat_id, sticker=f, read_timeout=50)).sticker -@pytest.fixture(scope="function") +@pytest.fixture() def video_sticker_file(): with data_file("telegram_video_sticker.webm").open("rb") as f: yield f @@ -126,13 +126,13 @@ class TestStickerWithoutRequest(TestStickerBase): assert isinstance(sticker, Sticker) assert isinstance(sticker.file_id, str) assert isinstance(sticker.file_unique_id, str) - assert sticker.file_id != "" - assert sticker.file_unique_id != "" + assert sticker.file_id + assert sticker.file_unique_id assert isinstance(sticker.thumbnail, PhotoSize) assert isinstance(sticker.thumbnail.file_id, str) assert isinstance(sticker.thumbnail.file_unique_id, str) - assert sticker.thumbnail.file_id != "" - assert sticker.thumbnail.file_unique_id != "" + assert sticker.thumbnail.file_id + assert sticker.thumbnail.file_unique_id assert isinstance(sticker.needs_repainting, bool) def test_expected_values(self, sticker): @@ -312,8 +312,8 @@ class TestStickerWithRequest(TestStickerBase): assert isinstance(message.sticker, Sticker) assert isinstance(message.sticker.file_id, str) assert isinstance(message.sticker.file_unique_id, str) - assert message.sticker.file_id != "" - assert message.sticker.file_unique_id != "" + assert message.sticker.file_id + assert message.sticker.file_unique_id assert message.sticker.width == sticker.width assert message.sticker.height == sticker.height assert message.sticker.is_animated == sticker.is_animated @@ -327,8 +327,8 @@ class TestStickerWithRequest(TestStickerBase): assert isinstance(message.sticker.thumbnail, PhotoSize) assert isinstance(message.sticker.thumbnail.file_id, str) assert isinstance(message.sticker.thumbnail.file_unique_id, str) - assert message.sticker.thumbnail.file_id != "" - assert message.sticker.thumbnail.file_unique_id != "" + assert message.sticker.thumbnail.file_id + assert message.sticker.thumbnail.file_unique_id assert message.sticker.thumbnail.width == sticker.thumbnail.width assert message.sticker.thumbnail.height == sticker.thumbnail.height assert message.sticker.thumbnail.file_size == sticker.thumbnail.file_size @@ -372,8 +372,8 @@ class TestStickerWithRequest(TestStickerBase): assert isinstance(message.sticker, Sticker) assert isinstance(message.sticker.file_id, str) assert isinstance(message.sticker.file_unique_id, str) - assert message.sticker.file_id != "" - assert message.sticker.file_unique_id != "" + assert message.sticker.file_id + assert message.sticker.file_unique_id assert message.sticker.width == sticker.width assert message.sticker.height == sticker.height assert message.sticker.is_animated == sticker.is_animated @@ -384,14 +384,14 @@ class TestStickerWithRequest(TestStickerBase): assert isinstance(message.sticker.thumbnail, PhotoSize) assert isinstance(message.sticker.thumbnail.file_id, str) assert isinstance(message.sticker.thumbnail.file_unique_id, str) - assert message.sticker.thumbnail.file_id != "" - assert message.sticker.thumbnail.file_unique_id != "" + assert message.sticker.thumbnail.file_id + assert message.sticker.thumbnail.file_unique_id assert message.sticker.thumbnail.width == sticker.thumbnail.width assert message.sticker.thumbnail.height == sticker.thumbnail.height assert message.sticker.thumbnail.file_size == sticker.thumbnail.file_size @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -442,7 +442,7 @@ class TestStickerWithRequest(TestStickerBase): premium_sticker = premium_sticker_set.stickers[20] assert premium_sticker.premium_animation.file_unique_id == "AQADOBwAAifPOElr" assert isinstance(premium_sticker.premium_animation.file_id, str) - assert premium_sticker.premium_animation.file_id != "" + assert premium_sticker.premium_animation.file_id premium_sticker_dict = { "file_unique_id": "AQADOBwAAifPOElr", "file_id": premium_sticker.premium_animation.file_id, @@ -478,15 +478,15 @@ class TestStickerWithRequest(TestStickerBase): assert emoji_sticker_list[0].file_unique_id == "AgAD6gwAAoY06FM" async def test_error_send_empty_file(self, bot, chat_id): - with pytest.raises(TelegramError): - await bot.send_sticker(chat_id, open(os.devnull, "rb")) + with Path(os.devnull).open("rb") as file, pytest.raises(TelegramError): + await bot.send_sticker(chat_id, file) async def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): await bot.send_sticker(chat_id, "") -@pytest.fixture(scope="function") +@pytest.fixture() async def sticker_set(bot): ss = await bot.get_sticker_set(f"test_by_{bot.username}") if len(ss.stickers) > 100: @@ -496,11 +496,11 @@ async def sticker_set(bot): except BadRequest as e: if e.message == "Stickerset_not_modified": return ss - raise Exception("stickerset is growing too large.") + raise Exception("stickerset is growing too large.") from None return ss -@pytest.fixture(scope="function") +@pytest.fixture() async def animated_sticker_set(bot): ss = await bot.get_sticker_set(f"animated_test_by_{bot.username}") if len(ss.stickers) > 100: @@ -510,11 +510,11 @@ async def animated_sticker_set(bot): except BadRequest as e: if e.message == "Stickerset_not_modified": return ss - raise Exception("stickerset is growing too large.") + raise Exception("stickerset is growing too large.") from None return ss -@pytest.fixture(scope="function") +@pytest.fixture() async def video_sticker_set(bot): ss = await bot.get_sticker_set(f"video_test_by_{bot.username}") if len(ss.stickers) > 100: @@ -524,11 +524,11 @@ async def video_sticker_set(bot): except BadRequest as e: if e.message == "Stickerset_not_modified": return ss - raise Exception("stickerset is growing too large.") + raise Exception("stickerset is growing too large.") from None return ss -@pytest.fixture(scope="function") +@pytest.fixture() def sticker_set_thumb_file(): with data_file("sticker_set_thumb.png").open("rb") as file: yield file diff --git a/tests/_files/test_venue.py b/tests/_files/test_venue.py index f20f1251f..9631d562d 100644 --- a/tests/_files/test_venue.py +++ b/tests/_files/test_venue.py @@ -144,7 +144,7 @@ class TestVenueWithoutRequest(TestVenueBase): class TestVenueWithRequest(TestVenueBase): @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), diff --git a/tests/_files/test_video.py b/tests/_files/test_video.py index 8d2be6486..5d10d81d8 100644 --- a/tests/_files/test_video.py +++ b/tests/_files/test_video.py @@ -36,7 +36,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def video_file(): with data_file("telegram.mp4").open("rb") as f: yield f @@ -76,14 +76,14 @@ class TestVideoWithoutRequest(TestVideoBase): assert isinstance(video, Video) assert isinstance(video.file_id, str) assert isinstance(video.file_unique_id, str) - assert video.file_id != "" - assert video.file_unique_id != "" + assert video.file_id + assert video.file_unique_id assert isinstance(video.thumbnail, PhotoSize) assert isinstance(video.thumbnail.file_id, str) assert isinstance(video.thumbnail.file_unique_id, str) - assert video.thumbnail.file_id != "" - assert video.thumbnail.file_unique_id != "" + assert video.thumbnail.file_id + assert video.thumbnail.file_unique_id def test_expected_values(self, video): assert video.width == self.width @@ -244,8 +244,8 @@ class TestVideoWithRequest(TestVideoBase): assert isinstance(message.video, Video) assert isinstance(message.video.file_id, str) assert isinstance(message.video.file_unique_id, str) - assert message.video.file_id != "" - assert message.video.file_unique_id != "" + assert message.video.file_id + assert message.video.file_unique_id assert message.video.width == video.width assert message.video.height == video.height assert message.video.duration == video.duration @@ -282,8 +282,8 @@ class TestVideoWithRequest(TestVideoBase): assert isinstance(message.video, Video) assert isinstance(message.video.file_id, str) assert isinstance(message.video.file_unique_id, str) - assert message.video.file_id != "" - assert message.video.file_unique_id != "" + assert message.video.file_id + assert message.video.file_unique_id assert message.video.width == video.width assert message.video.height == video.height assert message.video.duration == video.duration @@ -292,8 +292,8 @@ class TestVideoWithRequest(TestVideoBase): assert isinstance(message.video.thumbnail, PhotoSize) assert isinstance(message.video.thumbnail.file_id, str) assert isinstance(message.video.thumbnail.file_unique_id, str) - assert message.video.thumbnail.file_id != "" - assert message.video.thumbnail.file_unique_id != "" + assert message.video.thumbnail.file_id + assert message.video.thumbnail.file_unique_id assert message.video.thumbnail.width == 51 # This seems odd that it's not self.thumb_width assert message.video.thumbnail.height == 90 # Ditto assert message.video.thumbnail.file_size == 645 # same @@ -358,7 +358,7 @@ class TestVideoWithRequest(TestVideoBase): assert not unprotected.has_protected_content @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -391,8 +391,8 @@ class TestVideoWithRequest(TestVideoBase): ) async def test_error_send_empty_file(self, bot, chat_id): - with pytest.raises(TelegramError): - await bot.send_video(chat_id, open(os.devnull, "rb")) + with Path(os.devnull).open("rb") as file, pytest.raises(TelegramError): + await bot.send_video(chat_id, file) async def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): diff --git a/tests/_files/test_videonote.py b/tests/_files/test_videonote.py index 1c5bf007d..5ce780fe1 100644 --- a/tests/_files/test_videonote.py +++ b/tests/_files/test_videonote.py @@ -35,7 +35,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def video_note_file(): with data_file("telegram2.mp4").open("rb") as f: yield f @@ -70,14 +70,14 @@ class TestVideoNoteWithoutRequest(TestVideoNoteBase): assert isinstance(video_note, VideoNote) assert isinstance(video_note.file_id, str) assert isinstance(video_note.file_unique_id, str) - assert video_note.file_id != "" - assert video_note.file_unique_id != "" + assert video_note.file_id + assert video_note.file_unique_id assert isinstance(video_note.thumbnail, PhotoSize) assert isinstance(video_note.thumbnail.file_id, str) assert isinstance(video_note.thumbnail.file_unique_id, str) - assert video_note.thumbnail.file_id != "" - assert video_note.thumbnail.file_unique_id != "" + assert video_note.thumbnail.file_id + assert video_note.thumbnail.file_unique_id def test_expected_values(self, video_note): assert video_note.length == self.length @@ -221,8 +221,8 @@ class TestVideoNoteWithRequest(TestVideoNoteBase): assert isinstance(message.video_note, VideoNote) assert isinstance(message.video_note.file_id, str) assert isinstance(message.video_note.file_unique_id, str) - assert message.video_note.file_id != "" - assert message.video_note.file_unique_id != "" + assert message.video_note.file_id + assert message.video_note.file_unique_id assert message.video_note.length == video_note.length assert message.video_note.duration == video_note.duration assert message.video_note.file_size == video_note.file_size @@ -252,7 +252,7 @@ class TestVideoNoteWithRequest(TestVideoNoteBase): assert message.video_note == video_note @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -295,8 +295,8 @@ class TestVideoNoteWithRequest(TestVideoNoteBase): assert not unprotected.has_protected_content async def test_error_send_empty_file(self, bot, chat_id): - with pytest.raises(TelegramError): - await bot.send_video_note(chat_id, open(os.devnull, "rb")) + with Path(os.devnull).open("rb") as file, pytest.raises(TelegramError): + await bot.send_video_note(chat_id, file) async def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): diff --git a/tests/_files/test_voice.py b/tests/_files/test_voice.py index 93c9e5a80..69ed43d02 100644 --- a/tests/_files/test_voice.py +++ b/tests/_files/test_voice.py @@ -35,7 +35,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def voice_file(): with data_file("telegram.ogg").open("rb") as f: yield f @@ -68,8 +68,8 @@ class TestVoiceWithoutRequest(TestVoiceBase): assert isinstance(voice, Voice) assert isinstance(voice.file_id, str) assert isinstance(voice.file_unique_id, str) - assert voice.file_id != "" - assert voice.file_unique_id != "" + assert voice.file_id + assert voice.file_unique_id def test_expected_values(self, voice): assert voice.duration == self.duration @@ -191,8 +191,8 @@ class TestVoiceWithRequest(TestVoiceBase): assert isinstance(message.voice, Voice) assert isinstance(message.voice.file_id, str) assert isinstance(message.voice.file_unique_id, str) - assert message.voice.file_id != "" - assert message.voice.file_unique_id != "" + assert message.voice.file_id + assert message.voice.file_unique_id assert message.voice.duration == voice.duration assert message.voice.mime_type == voice.mime_type assert message.voice.file_size == voice.file_size @@ -220,8 +220,8 @@ class TestVoiceWithRequest(TestVoiceBase): assert isinstance(message.voice, Voice) assert isinstance(message.voice.file_id, str) assert isinstance(message.voice.file_unique_id, str) - assert message.voice.file_id != "" - assert message.voice.file_unique_id != "" + assert message.voice.file_id + assert message.voice.file_unique_id assert message.voice.duration == voice.duration assert message.voice.mime_type == voice.mime_type assert message.voice.file_size == voice.file_size @@ -285,7 +285,7 @@ class TestVoiceWithRequest(TestVoiceBase): assert not unprotected.has_protected_content @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -318,8 +318,8 @@ class TestVoiceWithRequest(TestVoiceBase): ) async def test_error_send_empty_file(self, bot, chat_id): - with pytest.raises(TelegramError): - await bot.sendVoice(chat_id, open(os.devnull, "rb")) + with Path(os.devnull).open("rb") as file, pytest.raises(TelegramError): + await bot.sendVoice(chat_id, file) async def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): diff --git a/tests/_inline/test_inlinekeyboardbutton.py b/tests/_inline/test_inlinekeyboardbutton.py index aa8034347..29675504b 100644 --- a/tests/_inline/test_inlinekeyboardbutton.py +++ b/tests/_inline/test_inlinekeyboardbutton.py @@ -93,7 +93,7 @@ class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase): assert inline_keyboard_button_dict["pay"] == inline_keyboard_button.pay assert ( inline_keyboard_button_dict["login_url"] == inline_keyboard_button.login_url.to_dict() - ) # NOQA: E127 + ) assert inline_keyboard_button_dict["web_app"] == inline_keyboard_button.web_app.to_dict() def test_de_json(self, bot): diff --git a/tests/_inline/test_inlinekeyboardmarkup.py b/tests/_inline/test_inlinekeyboardmarkup.py index 24c7e02a0..3e67e4db2 100644 --- a/tests/_inline/test_inlinekeyboardmarkup.py +++ b/tests/_inline/test_inlinekeyboardmarkup.py @@ -179,17 +179,17 @@ class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase): ) def test_wrong_keyboard_inputs(self): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): InlineKeyboardMarkup( [[InlineKeyboardButton("b1", "1")], InlineKeyboardButton("b2", "2")] ) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): InlineKeyboardMarkup("strings_are_not_allowed") - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): InlineKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"]) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): InlineKeyboardMarkup(InlineKeyboardButton("b1", "1")) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): InlineKeyboardMarkup([[[InlineKeyboardButton("only_2d_array_is_allowed", "1")]]]) async def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch): diff --git a/tests/_inline/test_inlinequeryhandler.py b/tests/_inline/test_inlinequeryhandler.py index e8f517ba2..0e50666a4 100644 --- a/tests/_inline/test_inlinequeryhandler.py +++ b/tests/_inline/test_inlinequeryhandler.py @@ -68,7 +68,7 @@ def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope="function") +@pytest.fixture() def inline_query(bot): update = Update( 0, @@ -95,7 +95,7 @@ class TestInlineQueryHandler: assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): @@ -154,7 +154,7 @@ class TestInlineQueryHandler: @pytest.mark.parametrize("chat_types", [[Chat.SENDER], [Chat.SENDER, Chat.SUPERGROUP], []]) @pytest.mark.parametrize( - "chat_type,result", [(Chat.SENDER, True), (Chat.CHANNEL, False), (None, False)] + ("chat_type", "result"), [(Chat.SENDER, True), (Chat.CHANNEL, False), (None, False)] ) async def test_chat_types(self, app, inline_query, chat_types, chat_type, result): try: diff --git a/tests/_inline/test_inputinvoicemessagecontent.py b/tests/_inline/test_inputinvoicemessagecontent.py index 0ba74db1c..c399c49aa 100644 --- a/tests/_inline/test_inputinvoicemessagecontent.py +++ b/tests/_inline/test_inputinvoicemessagecontent.py @@ -120,7 +120,7 @@ class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageConten currency=self.currency, prices=self.prices, ) - assert input_invoice_message_content.suggested_tip_amounts == tuple() + assert input_invoice_message_content.suggested_tip_amounts == () def test_to_dict(self, input_invoice_message_content): input_invoice_message_content_dict = input_invoice_message_content.to_dict() diff --git a/tests/_passport/test_no_passport.py b/tests/_passport/test_no_passport.py index d32ed4f00..8fa4b0925 100644 --- a/tests/_passport/test_no_passport.py +++ b/tests/_passport/test_no_passport.py @@ -29,7 +29,7 @@ with the TEST_WITH_OPT_DEPS environment variable set to False in addition to the import pytest from telegram import _bot as bot -from telegram._passport import credentials as credentials +from telegram._passport import credentials from tests.auxil.envvars import TEST_WITH_OPT_DEPS diff --git a/tests/_payment/test_invoice.py b/tests/_payment/test_invoice.py index 0dc344ae2..68a2e447b 100644 --- a/tests/_payment/test_invoice.py +++ b/tests/_payment/test_invoice.py @@ -90,7 +90,7 @@ class TestInvoiceWithoutRequest(TestInvoiceBase): # parameters correctly because #2526 went unnoticed for 3 years … async def make_assertion(*args, **_): kwargs = args[1] - return all([kwargs[key] == key for key in kwargs]) + return all(kwargs[key] == key for key in kwargs) monkeypatch.setattr(bot, "_send_message", make_assertion) assert await bot.send_invoice( @@ -123,7 +123,7 @@ class TestInvoiceWithoutRequest(TestInvoiceBase): async def test_send_all_args_create_invoice_link(self, bot, monkeypatch): async def make_assertion(*args, **_): kwargs = args[1] - return all([kwargs[i] == i for i in kwargs]) + return all(kwargs[i] == i for i in kwargs) monkeypatch.setattr(bot, "_post", make_assertion) assert await bot.create_invoice_link( @@ -196,7 +196,7 @@ class TestInvoiceWithRequest(TestInvoiceBase): ) assert message.invoice.currency == self.currency - assert message.invoice.start_parameter == "" + assert not message.invoice.start_parameter assert message.invoice.description == self.description assert message.invoice.title == self.title assert message.invoice.total_amount == self.total_amount @@ -211,7 +211,7 @@ class TestInvoiceWithRequest(TestInvoiceBase): ) assert isinstance(link, str) - assert link != "" + assert link @pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True) async def test_send_invoice_default_protect_content( @@ -237,7 +237,7 @@ class TestInvoiceWithRequest(TestInvoiceBase): assert not unprotected.has_protected_content @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), diff --git a/tests/_utils/test_datetime.py b/tests/_utils/test_datetime.py index b36f3bb49..3776c3da9 100644 --- a/tests/_utils/test_datetime.py +++ b/tests/_utils/test_datetime.py @@ -96,7 +96,7 @@ class TestDatetime: def test_to_float_timestamp_absolute_no_reference(self): """A reference timestamp is only relevant for relative time specifications""" - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="while reference_timestamp is not None"): tg_dtm.to_float_timestamp(dtm.datetime(2019, 11, 11), reference_timestamp=123) # see note on parametrization at the top of this file diff --git a/tests/_utils/test_defaults.py b/tests/_utils/test_defaults.py index 81eb3102c..45e2849f7 100644 --- a/tests/_utils/test_defaults.py +++ b/tests/_utils/test_defaults.py @@ -45,7 +45,7 @@ class TestDefault: def test_data_assignment(self): defaults = Defaults() - for name, val in inspect.getmembers(Defaults, lambda x: isinstance(x, property)): + for name, _val in inspect.getmembers(Defaults, lambda x: isinstance(x, property)): with pytest.raises(AttributeError): setattr(defaults, name, True) diff --git a/tests/_utils/test_defaultvalue.py b/tests/_utils/test_defaultvalue.py index 53556a5e9..04d72614f 100644 --- a/tests/_utils/test_defaultvalue.py +++ b/tests/_utils/test_defaultvalue.py @@ -37,7 +37,7 @@ class TestDefaultValue: assert df_1 != df_2 @pytest.mark.parametrize( - "value,expected", + ("value", "expected"), [ ({}, False), ({1: 2}, True), @@ -67,8 +67,7 @@ class TestDefaultValue: def foo(arg=default_one): if arg is default_one: return 1 - else: - return 2 + return 2 assert foo() == 1 assert foo(None) == 2 diff --git a/tests/_utils/test_files.py b/tests/_utils/test_files.py index 31a150ba3..9ec70c433 100644 --- a/tests/_utils/test_files.py +++ b/tests/_utils/test_files.py @@ -16,6 +16,7 @@ # # 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 contextlib import subprocess import sys from pathlib import Path @@ -30,7 +31,7 @@ from tests.auxil.files import TEST_DATA_PATH, data_file class TestFiles: @pytest.mark.parametrize( - "string,expected", + ("string", "expected"), [ (str(data_file("game.gif")), True), (str(TEST_DATA_PATH), False), @@ -46,7 +47,7 @@ class TestFiles: assert telegram._utils.files.is_local_file(string) == expected @pytest.mark.parametrize( - "string,expected_local,expected_non_local", + ("string", "expected_local", "expected_non_local"), [ (data_file("game.gif"), data_file("game.gif").as_uri(), InputFile), (TEST_DATA_PATH, TEST_DATA_PATH, TEST_DATA_PATH), @@ -76,7 +77,7 @@ class TestFiles: telegram._utils.files.parse_file_input(string, local_mode=False), InputFile ) elif expected_non_local is ValueError: - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="but local mode is not enabled."): telegram._utils.files.parse_file_input(string, local_mode=False) else: assert ( @@ -153,9 +154,7 @@ class TestFiles: assert out[0] is None assert out[1] == png_file.read_bytes() - try: + with contextlib.suppress(ProcessLookupError): proc.kill() - except ProcessLookupError: # This exception may be thrown if the process has finished before we had the chance # to kill it. - pass diff --git a/tests/auxil/bot_method_checks.py b/tests/auxil/bot_method_checks.py index 11db77fcd..c6d148014 100644 --- a/tests/auxil/bot_method_checks.py +++ b/tests/auxil/bot_method_checks.py @@ -19,7 +19,6 @@ import datetime import functools import inspect -import os from typing import Any, Callable, Dict, Iterable, List import pytest @@ -38,9 +37,8 @@ from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue from telegram.constants import InputMediaType from telegram.ext import Defaults, ExtBot from telegram.request import RequestData -from tests.auxil.envvars import env_var_2_bool +from tests.auxil.envvars import TEST_WITH_OPT_DEPS -TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True)) if TEST_WITH_OPT_DEPS: import pytz @@ -138,15 +136,9 @@ async def check_shortcut_call( Returns: :obj:`bool` """ - if not skip_params: - skip_params = set() - else: - skip_params = set(skip_params) + skip_params = set() if not skip_params else set(skip_params) skip_params.add("rate_limit_args") - if not shortcut_kwargs: - shortcut_kwargs = set() - else: - shortcut_kwargs = set(shortcut_kwargs) + shortcut_kwargs = set() if not shortcut_kwargs else set(shortcut_kwargs) orig_bot_method = getattr(bot, bot_method_name) bot_signature = inspect.signature(orig_bot_method) @@ -278,7 +270,7 @@ async def check_defaults_handling( kwargs_need_default.remove("parse_mode") defaults_no_custom_defaults = Defaults() - kwargs = {kwarg: "custom_default" for kwarg in inspect.signature(Defaults).parameters.keys()} + kwargs = {kwarg: "custom_default" for kwarg in inspect.signature(Defaults).parameters} kwargs["tzinfo"] = pytz.timezone("America/New_York") defaults_custom_defaults = Defaults(**kwargs) @@ -351,15 +343,12 @@ async def check_defaults_handling( # Check datetime conversion until_date = data.pop("until_date", None) if until_date: - if df_value == "non-None-value": - if until_date != 946681200: - pytest.fail("Non-naive until_date was interpreted as Europe/Berlin.") - if df_value is DEFAULT_NONE: - if until_date != 946684800: - pytest.fail("Naive until_date was not interpreted as UTC") - if df_value == "custom_default": - if until_date != 946702800: - pytest.fail("Naive until_date was not interpreted as America/New_York") + if df_value == "non-None-value" and until_date != 946681200: + pytest.fail("Non-naive until_date was interpreted as Europe/Berlin.") + if df_value is DEFAULT_NONE and until_date != 946684800: + pytest.fail("Naive until_date was not interpreted as UTC") + if df_value == "custom_default" and until_date != 946702800: + pytest.fail("Naive until_date was not interpreted as America/New_York") if method.__name__ in ["get_file", "get_small_file", "get_big_file"]: # This is here mainly for PassportFile.get_file, which calls .set_credentials on the @@ -397,13 +386,13 @@ async def check_defaults_handling( kwargs_need_default, ) assertion_callback = functools.partial(make_assertion, df_value=default_value) - setattr(request, "post", assertion_callback) + request.post = assertion_callback assert await method(**kwargs) in expected_return_values # 2: test that we get the manually passed non-None value kwargs = build_kwargs(shortcut_signature, kwargs_need_default, dfv="non-None-value") assertion_callback = functools.partial(make_assertion, df_value="non-None-value") - setattr(request, "post", assertion_callback) + request.post = assertion_callback assert await method(**kwargs) in expected_return_values # 3: test that we get the manually passed None value @@ -413,12 +402,12 @@ async def check_defaults_handling( dfv=None, ) assertion_callback = functools.partial(make_assertion, df_value=None) - setattr(request, "post", assertion_callback) + request.post = assertion_callback assert await method(**kwargs) in expected_return_values except Exception as exc: raise exc finally: - setattr(request, "post", orig_post) + request.post = orig_post if not raw_bot: bot._defaults = None diff --git a/tests/auxil/envvars.py b/tests/auxil/envvars.py index b5eabb8d0..e74562043 100644 --- a/tests/auxil/envvars.py +++ b/tests/auxil/envvars.py @@ -27,5 +27,5 @@ def env_var_2_bool(env_var: object) -> bool: return env_var.lower().strip() == "true" -GITHUB_ACTION = os.getenv("GITHUB_ACTION", False) -TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True)) +GITHUB_ACTION = os.getenv("GITHUB_ACTION", "") +TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", "true")) diff --git a/tests/auxil/plugin_github_group.py b/tests/auxil/plugin_github_group.py index 8c363e234..7f4c7291d 100644 --- a/tests/auxil/plugin_github_group.py +++ b/tests/auxil/plugin_github_group.py @@ -33,15 +33,16 @@ def terminal_summary_wrapper(original, plugin_name): return pytest_terminal_summary -@pytest.mark.trylast +@pytest.mark.trylast() def pytest_configure(config): for hookimpl in config.pluginmanager.hook.pytest_terminal_summary._nonwrappers: if hookimpl.plugin_name in fold_plugins: hookimpl.function = terminal_summary_wrapper(hookimpl.function, hookimpl.plugin_name) -terminal = None -previous_name = None +class PytestPluginHelpers: + terminal = None + previous_name = None def _get_name(location): @@ -50,7 +51,7 @@ def _get_name(location): return location[0] -@pytest.mark.trylast +@pytest.mark.trylast() def pytest_itemcollected(item): item._nodeid = item._nodeid.split("::", 1)[1] @@ -58,19 +59,16 @@ def pytest_itemcollected(item): @pytest.hookimpl(hookwrapper=True, tryfirst=True) def pytest_runtest_protocol(item, nextitem): # This is naughty but pytests' own plugins does something similar too, so who cares - global terminal - if terminal is None: - terminal = _pytest.config.create_terminal_writer(item.config) - - global previous_name + if PytestPluginHelpers.terminal is None: + PytestPluginHelpers.terminal = _pytest.config.create_terminal_writer(item.config) name = _get_name(item.location) - if previous_name is None or previous_name != name: - previous_name = name - terminal.write(f"\n##[group] {name}") + if PytestPluginHelpers.previous_name is None or PytestPluginHelpers.previous_name != name: + PytestPluginHelpers.previous_name = name + PytestPluginHelpers.terminal.write(f"\n##[group] {name}") yield if nextitem is None or _get_name(nextitem.location) != name: - terminal.write("\n##[endgroup]") + PytestPluginHelpers.terminal.write("\n##[endgroup]") diff --git a/tests/auxil/pytest_classes.py b/tests/auxil/pytest_classes.py index dd4aec159..15ade7975 100644 --- a/tests/auxil/pytest_classes.py +++ b/tests/auxil/pytest_classes.py @@ -92,11 +92,10 @@ def make_bot(bot_info=None, **kwargs): token = kwargs.pop("token", (bot_info or {}).get("token")) private_key = kwargs.pop("private_key", PRIVATE_KEY) kwargs.pop("token", None) - _bot = PytestExtBot( + return PytestExtBot( token=token, private_key=private_key if TEST_WITH_OPT_DEPS else None, request=NonchalantHttpxRequest(8), get_updates_request=NonchalantHttpxRequest(1), **kwargs, ) - return _bot diff --git a/tests/auxil/slots.py b/tests/auxil/slots.py index 8c8e40b98..0d05dd0c9 100644 --- a/tests/auxil/slots.py +++ b/tests/auxil/slots.py @@ -28,10 +28,7 @@ def mro_slots(obj, only_parents: bool = False): """ cls = obj if inspect.isclass(obj) else obj.__class__ - if only_parents: - classes = cls.__mro__[1:] - else: - classes = cls.__mro__ + classes = cls.__mro__[1:] if only_parents else cls.__mro__ return [ attr diff --git a/tests/conftest.py b/tests/conftest.py index d444c78a8..d42629fc7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -100,8 +100,7 @@ def event_loop(request): # https://github.com/python/cpython/issues/83413, https://github.com/encode/httpx/issues/914 if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith("win"): asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - loop = asyncio.get_event_loop_policy().new_event_loop() - yield loop + return asyncio.get_event_loop_policy().new_event_loop() # loop.close() # instead of closing here, do that at the every end of the test session @@ -117,7 +116,7 @@ async def bot(bot_info): yield _bot -@pytest.fixture(scope="function") +@pytest.fixture() def one_time_bot(bot_info): """A function scoped bot since the session bot would shutdown when `async with app` finishes""" return make_bot(bot_info) @@ -199,7 +198,7 @@ def provider_token(bot_info): return bot_info["payment_provider_token"] -@pytest.fixture(scope="function") +@pytest.fixture() async def app(bot_info): # We build a new bot each time so that we use `app` in a context manager without problems application = ( @@ -211,7 +210,7 @@ async def app(bot_info): await application.shutdown() -@pytest.fixture(scope="function") +@pytest.fixture() async def updater(bot_info): # We build a new bot each time so that we use `updater` in a context manager without problems up = Updater(bot=make_bot(bot_info), update_queue=asyncio.Queue()) @@ -221,7 +220,7 @@ async def updater(bot_info): await up.shutdown() -@pytest.fixture(scope="function") +@pytest.fixture() def thumb_file(): with data_file("thumb.jpg").open("rb") as f: yield f @@ -266,7 +265,7 @@ def _get_false_update_fixture_decorator_params(): return {"params": params, "ids": ids} -@pytest.fixture(scope="function", **_get_false_update_fixture_decorator_params()) +@pytest.fixture(**_get_false_update_fixture_decorator_params()) def false_update(request): return Update(update_id=1, **request.param) @@ -275,9 +274,8 @@ def false_update(request): def tzinfo(request): if TEST_WITH_OPT_DEPS: return pytz.timezone(request.param) - else: - hours_offset = {"Europe/Berlin": 2, "Asia/Singapore": 8, "UTC": 0}[request.param] - return BasicTimezone(offset=datetime.timedelta(hours=hours_offset), name=request.param) + hours_offset = {"Europe/Berlin": 2, "Asia/Singapore": 8, "UTC": 0}[request.param] + return BasicTimezone(offset=datetime.timedelta(hours=hours_offset), name=request.param) @pytest.fixture(scope="session") diff --git a/tests/docs/admonition_inserter.py b/tests/docs/admonition_inserter.py index 5710ec835..c135cbfd1 100644 --- a/tests/docs/admonition_inserter.py +++ b/tests/docs/admonition_inserter.py @@ -81,8 +81,8 @@ class TestAdmonitionInserter: ) @pytest.mark.parametrize( - "admonition_type, cls, link", - ( + ("admonition_type", "cls", "link"), + [ ( "available_in", telegram.ChatMember, @@ -171,7 +171,7 @@ class TestAdmonitionInserter: telegram.ext.PicklePersistence, # subclass ":meth:`telegram.ext.ApplicationBuilder.persistence`", ), - ), + ], ) def test_check_presence(self, admonition_inserter, admonition_type, cls, link): """Checks if a given link is present in the admonition of a given type for a given @@ -199,8 +199,8 @@ class TestAdmonitionInserter: ) @pytest.mark.parametrize( - "admonition_type, cls, link", - ( + ("admonition_type", "cls", "link"), + [ ( "returned_in", telegram.ext.CallbackContext, @@ -208,7 +208,7 @@ class TestAdmonitionInserter: # In this case classes inside square brackets must not be parsed ":meth:`telegram.ext.ApplicationBuilder.build`", ), - ), + ], ) def test_check_absence(self, admonition_inserter, admonition_type, cls, link): """Checks if a given link is **absent** in the admonition of a given type for a given diff --git a/tests/ext/_utils/test_trackingdict.py b/tests/ext/_utils/test_trackingdict.py index 49c7cc57d..5ed327c71 100644 --- a/tests/ext/_utils/test_trackingdict.py +++ b/tests/ext/_utils/test_trackingdict.py @@ -23,14 +23,14 @@ from telegram.ext._utils.trackingdict import TrackingDict from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def td() -> TrackingDict: td = TrackingDict() td.update_no_track({1: 1}) return td -@pytest.fixture(scope="function") +@pytest.fixture() def data() -> dict: return {1: 1} @@ -62,9 +62,9 @@ class TestTrackingDict: assert td != td_2 assert td_2 != td assert td != 1 - assert 1 != td + assert td != 1 + assert td != 5 assert td != 5 - assert 5 != td def test_getitem(self, td): assert td[1] == 1 diff --git a/tests/ext/test_application.py b/tests/ext/test_application.py index 8dc00dba5..b68696e98 100644 --- a/tests/ext/test_application.py +++ b/tests/ext/test_application.py @@ -75,7 +75,7 @@ class TestApplication: count = 0 @pytest.fixture(autouse=True, name="reset") - def reset_fixture(self): + def _reset_fixture(self): self.reset() def reset(self): @@ -121,8 +121,8 @@ class TestApplication: async def test_slot_behaviour(self, one_time_bot): async with ApplicationBuilder().bot(one_time_bot).build() as app: for at in app.__slots__: - at = f"_Application{at}" if at.startswith("__") and not at.endswith("__") else at - assert getattr(app, at, "err") != "err", f"got extra slot '{at}'" + attr = f"_Application{at}" if at.startswith("__") and not at.endswith("__") else at + assert getattr(app, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(app)) == len(set(mro_slots(app))), "duplicate slot" def test_manual_init_warning(self, recwarn, updater): @@ -146,7 +146,7 @@ class TestApplication: assert recwarn[0].filename == __file__, "stacklevel is incorrect!" @pytest.mark.parametrize( - "concurrent_updates, expected", [(0, 0), (4, 4), (False, 0), (True, 256)] + ("concurrent_updates", "expected"), [(0, 0), (4, 4), (False, 0), (True, 256)] ) @pytest.mark.filterwarnings("ignore: `Application` instances should") def test_init(self, one_time_bot, concurrent_updates, expected): @@ -239,7 +239,7 @@ class TestApplication: assert isinstance(application.chat_data[1], float) assert isinstance(application.bot_data, complex) - @pytest.mark.parametrize("updater", (True, False)) + @pytest.mark.parametrize("updater", [True, False]) async def test_initialize(self, one_time_bot, monkeypatch, updater): """Initialization of persistence is tested test_basepersistence""" self.test_flag = set() @@ -266,7 +266,7 @@ class TestApplication: assert self.test_flag == {"bot"} await app.shutdown() - @pytest.mark.parametrize("updater", (True, False)) + @pytest.mark.parametrize("updater", [True, False]) async def test_shutdown(self, one_time_bot, monkeypatch, updater): """Shutdown of persistence is tested in test_basepersistence""" self.test_flag = set() @@ -408,7 +408,7 @@ class TestApplication: builder_1.token(app.bot.token) builder_2.token(app.bot.token) - @pytest.mark.parametrize("job_queue", (True, False)) + @pytest.mark.parametrize("job_queue", [True, False]) @pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning") async def test_start_stop_processing_updates(self, one_time_bot, job_queue): # TODO: repeat a similar test for create_task, persistence processing and job queue @@ -488,9 +488,8 @@ class TestApplication: if update.message.text == "test": if context is not self.received: pytest.fail("Expected same context object, got different") - else: - if context is self.received: - pytest.fail("First handler was wrongly called") + elif context is self.received: + pytest.fail("First handler was wrongly called") async with app: app.add_handler(MessageHandler(filters.Regex("test"), one), group=1) @@ -510,7 +509,7 @@ class TestApplication: with pytest.raises(TypeError, match="group is not int"): app.add_handler(handler, "one") - @pytest.mark.parametrize("group_empty", (True, False)) + @pytest.mark.parametrize("group_empty", [True, False]) async def test_add_remove_handler(self, app, group_empty): handler = MessageHandler(filters.ALL, self.callback_increase_count) app.add_handler(handler) @@ -578,11 +577,9 @@ class TestApplication: await asyncio.sleep(0.05) # sleep is required otherwise there is random behaviour # Test if handler was added to correct group with correct order- - assert ( - self.count == 2 - and len(app.handlers[1]) == 3 - and app.handlers[1][0] is msg_handler_set_count - ) + assert self.count == 2 + assert len(app.handlers[1]) == 3 + assert app.handlers[1][0] is msg_handler_set_count # Now lets test add_handlers when `handlers` is a dict- voice_filter_handler_to_check = MessageHandler( @@ -606,18 +603,17 @@ class TestApplication: await app.update_queue.put(voice_update) await asyncio.sleep(0.05) - assert ( - self.count == 4 - and len(app.handlers[1]) == 5 - and app.handlers[1][-1] is voice_filter_handler_to_check - ) + assert self.count == 4 + assert len(app.handlers[1]) == 5 + assert app.handlers[1][-1] is voice_filter_handler_to_check await app.update_queue.put( make_message_update(message=Message(5, None, None, caption="cap")) ) await asyncio.sleep(0.05) - assert self.count == 2 and len(app.handlers[-1]) == 1 + assert self.count == 2 + assert len(app.handlers[-1]) == 1 # Now lets test the errors which can be produced- with pytest.raises(ValueError, match="The `group` argument"): @@ -781,7 +777,7 @@ class TestApplication: await app.process_update(update) assert passed == ["start1", "error", err, "start3"] - @pytest.mark.parametrize("block", (True, False)) + @pytest.mark.parametrize("block", [True, False]) async def test_error_handler(self, app, block): app.add_error_handler(self.error_handler_context) app.add_handler(TypeHandler(object, self.callback_raise_error("TestError"), block=block)) @@ -903,7 +899,7 @@ class TestApplication: assert self.received == (CustomContext, float, complex, int) @pytest.mark.parametrize( - "check,expected", + ("check", "expected"), [(True, True), (None, False), (False, False), ({}, True), ("", True), ("check", True)], ) async def test_check_update_handling(self, app, check, expected): @@ -994,7 +990,7 @@ class TestApplication: ) await app.stop() - @pytest.mark.parametrize("handler_block", (True, False)) + @pytest.mark.parametrize("handler_block", [True, False]) async def test_non_blocking_error_handler(self, app, handler_block): event = asyncio.Event() @@ -1021,12 +1017,12 @@ class TestApplication: assert self.received == "done" assert task.done() - @pytest.mark.parametrize("handler_block", (True, False)) + @pytest.mark.parametrize("handler_block", [True, False]) async def test_non_blocking_error_handler_applicationhandlerstop( self, app, recwarn, handler_block ): async def callback(update, context): - raise RuntimeError() + raise RuntimeError async def error_handler(update, context): raise ApplicationHandlerStop @@ -1050,7 +1046,7 @@ class TestApplication: Path(recwarn[0].filename) == PROJECT_ROOT_PATH / "telegram" / "ext" / "_application.py" ), "incorrect stacklevel!" - @pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)]) + @pytest.mark.parametrize(("block", "expected_output"), [(False, 0), (True, 5)]) async def test_default_block_error_handler(self, bot_info, block, expected_output): async def error_handler(*args, **kwargs): await asyncio.sleep(0.1) @@ -1067,7 +1063,7 @@ class TestApplication: await asyncio.sleep(0.1) assert self.count == 5 - @pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)]) + @pytest.mark.parametrize(("block", "expected_output"), [(False, 0), (True, 5)]) async def test_default_block_handler(self, bot_info, block, expected_output): bot = make_bot(bot_info, defaults=Defaults(block=block)) app = Application.builder().bot(bot).build() @@ -1079,8 +1075,8 @@ class TestApplication: await asyncio.sleep(0.15) assert self.count == 5 - @pytest.mark.parametrize("handler_block", (True, False)) - @pytest.mark.parametrize("error_handler_block", (True, False)) + @pytest.mark.parametrize("handler_block", [True, False]) + @pytest.mark.parametrize("error_handler_block", [True, False]) async def test_nonblocking_handler_raises_and_non_blocking_error_handler_raises( self, app, caplog, handler_block, error_handler_block ): @@ -1155,7 +1151,7 @@ class TestApplication: assert app.chat_data[effective_new_chat_id]["key"] == "test" @pytest.mark.parametrize( - "c_id,expected", + ("c_id", "expected"), [(321, {222: "remove_me"}), (111, {321: {"not_empty": "no"}, 222: "remove_me"})], ids=["test chat_id removal", "test no key in data (no error)"], ) @@ -1165,7 +1161,7 @@ class TestApplication: assert app.chat_data == expected @pytest.mark.parametrize( - "u_id,expected", + ("u_id", "expected"), [(321, {222: "remove_me"}), (111, {321: {"not_empty": "no"}, 222: "remove_me"})], ids=["test user_id removal", "test no key in data (no error)"], ) @@ -1188,7 +1184,7 @@ class TestApplication: assert self.count == 42 assert out == 43 - @pytest.mark.parametrize("running", (True, False)) + @pytest.mark.parametrize("running", [True, False]) async def test_create_task_awaiting_warning(self, app, running, recwarn): async def callback(): await asyncio.sleep(0.1) @@ -1213,7 +1209,7 @@ class TestApplication: assert not task.done() await task - @pytest.mark.parametrize("update", (None, object())) + @pytest.mark.parametrize("update", [None, object()]) async def test_create_task_error_handling(self, app, update): exception = RuntimeError("TestError") @@ -1334,7 +1330,7 @@ class TestApplication: await app.stop() - @pytest.mark.parametrize("concurrent_updates", (15, 50, 100)) + @pytest.mark.parametrize("concurrent_updates", [15, 50, 100]) async def test_concurrent_updates(self, one_time_bot, concurrent_updates): # We don't test with `True` since the large number of parallel coroutines quickly leads # to test instabilities @@ -2042,7 +2038,7 @@ class TestApplication: Updater, "shutdown", call_after(Updater.shutdown, after_shutdown("updater")) ) app = ApplicationBuilder().bot(one_time_bot).build() - with pytest.raises(RuntimeError, match="Test Exception"): + with pytest.raises(RuntimeError, match="Test Exception"): # noqa: PT012 if "polling" in method: app.run_polling(close_loop=False) else: @@ -2064,7 +2060,7 @@ class TestApplication: monkeypatch.setattr(Application, "initialize", raise_method) app = ApplicationBuilder().bot(one_time_bot).build() - with pytest.raises(RuntimeError, match="Prevent Actually Running"): + with pytest.raises(RuntimeError, match="Prevent Actually Running"): # noqa: PT012 if "polling" in method: app.run_polling(close_loop=False, stop_signals=(signal.SIGINT,)) else: @@ -2080,7 +2076,7 @@ class TestApplication: assert found recwarn.clear() - with pytest.raises(RuntimeError, match="Prevent Actually Running"): + with pytest.raises(RuntimeError, match="Prevent Actually Running"): # noqa: PT012 if "polling" in method: app.run_polling(close_loop=False, stop_signals=None) else: diff --git a/tests/ext/test_applicationbuilder.py b/tests/ext/test_applicationbuilder.py index 117a68481..b17d30451 100644 --- a/tests/ext/test_applicationbuilder.py +++ b/tests/ext/test_applicationbuilder.py @@ -42,7 +42,7 @@ from tests.auxil.files import data_file from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def builder(): return ApplicationBuilder() @@ -142,7 +142,7 @@ class TestApplicationBuilder: assert app.post_stop is None @pytest.mark.parametrize( - "method, description", _BOT_CHECKS, ids=[entry[0] for entry in _BOT_CHECKS] + ("method", "description"), _BOT_CHECKS, ids=[entry[0] for entry in _BOT_CHECKS] ) def test_mutually_exclusive_for_bot(self, builder, method, description): # First test that e.g. `bot` can't be set if `request` was already set @@ -160,7 +160,7 @@ class TestApplicationBuilder: @pytest.mark.parametrize( "method", - ( + [ "connection_pool_size", "connect_timeout", "pool_timeout", @@ -170,7 +170,7 @@ class TestApplicationBuilder: "bot", "updater", "http_version", - ), + ], ) def test_mutually_exclusive_for_request(self, builder, method): builder.request(1) @@ -187,7 +187,7 @@ class TestApplicationBuilder: @pytest.mark.parametrize( "method", - ( + [ "get_updates_connection_pool_size", "get_updates_connect_timeout", "get_updates_pool_timeout", @@ -197,7 +197,7 @@ class TestApplicationBuilder: "get_updates_http_version", "bot", "updater", - ), + ], ) def test_mutually_exclusive_for_get_updates_request(self, builder, method): builder.get_updates_request(1) @@ -415,7 +415,7 @@ class TestApplicationBuilder: assert app.bot is updater.bot assert app.update_queue is updater.update_queue - @pytest.mark.parametrize("input_type", ("bytes", "str", "Path")) + @pytest.mark.parametrize("input_type", ["bytes", "str", "Path"]) def test_all_private_key_input_types(self, builder, bot, input_type): private_key = data_file("private.key") password = data_file("private_key.password") diff --git a/tests/ext/test_basepersistence.py b/tests/ext/test_basepersistence.py index ce2b63ce0..acd8bcff2 100644 --- a/tests/ext/test_basepersistence.py +++ b/tests/ext/test_basepersistence.py @@ -260,7 +260,7 @@ def build_conversation_handler(name: str, persistent: bool = True) -> BaseHandle return TrackingConversationHandler(name=name, persistent=persistent) -@pytest.fixture(scope="function") +@pytest.fixture() def papp(request, bot_info) -> Application: papp_input = request.param store_data = {} @@ -359,10 +359,10 @@ class TestBasePersistence: slots = mro_slots(inst, only_parents=True) assert len(slots) == len(set(slots)), "duplicate slot" - @pytest.mark.parametrize("bot_data", (True, False)) - @pytest.mark.parametrize("chat_data", (True, False)) - @pytest.mark.parametrize("user_data", (True, False)) - @pytest.mark.parametrize("callback_data", (True, False)) + @pytest.mark.parametrize("bot_data", [True, False]) + @pytest.mark.parametrize("chat_data", [True, False]) + @pytest.mark.parametrize("user_data", [True, False]) + @pytest.mark.parametrize("callback_data", [True, False]) def test_init_store_data_update_interval(self, bot_data, chat_data, user_data, callback_data): store_data = PersistenceInput( bot_data=bot_data, @@ -503,7 +503,7 @@ class TestBasePersistence: [PappInput(fill_data=True)], indirect=True, ) - @pytest.mark.parametrize("callback_data", ("invalid", (1, 2, 3))) + @pytest.mark.parametrize("callback_data", ["invalid", (1, 2, 3)]) async def test_initialization_invalid_callback_data( self, papp: Application, callback_data, monkeypatch ): @@ -897,7 +897,7 @@ class TestBasePersistence: assert not papp.persistence.conversations @default_papp - @pytest.mark.parametrize("delay_type", ("job", "handler", "task")) + @pytest.mark.parametrize("delay_type", ["job", "handler", "task"]) async def test_update_persistence_loop_async_logic( self, papp: Application, delay_type: str, chat_id ): @@ -1069,7 +1069,7 @@ class TestBasePersistence: @default_papp @pytest.mark.parametrize( - "delay_type", ("job", "blocking_handler", "nonblocking_handler", "task") + "delay_type", ["job", "blocking_handler", "nonblocking_handler", "task"] ) async def test_update_persistence_after_exception( self, papp: Application, delay_type: str, chat_id diff --git a/tests/ext/test_callbackdatacache.py b/tests/ext/test_callbackdatacache.py index 35104e732..862820812 100644 --- a/tests/ext/test_callbackdatacache.py +++ b/tests/ext/test_callbackdatacache.py @@ -31,7 +31,7 @@ from tests.auxil.envvars import TEST_WITH_OPT_DEPS from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def callback_data_cache(bot): return CallbackDataCache(bot) @@ -80,12 +80,12 @@ class TestKeyboardData: class TestCallbackDataCache: def test_slot_behaviour(self, callback_data_cache): for attr in callback_data_cache.__slots__: - attr = ( + at = ( f"_CallbackDataCache{attr}" if attr.startswith("__") and not attr.endswith("__") else attr ) - assert getattr(callback_data_cache, attr, "err") != "err", f"got extra slot '{attr}'" + assert getattr(callback_data_cache, at, "err") != "err", f"got extra slot '{at}'" assert len(mro_slots(callback_data_cache)) == len( set(mro_slots(callback_data_cache)) ), "duplicate slot" @@ -327,7 +327,7 @@ class TestCallbackDataCache: callback_data_cache.drop_data(callback_query) assert callback_data_cache.persistence_data == ([], {}) - @pytest.mark.parametrize("method", ("callback_data", "callback_queries")) + @pytest.mark.parametrize("method", ["callback_data", "callback_queries"]) def test_clear_all(self, callback_data_cache, method): changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") @@ -401,4 +401,4 @@ class TestCallbackDataCache: callback_data = [ list(data[2].values())[0] for data in callback_data_cache.persistence_data[0] ] - assert callback_data == list(str(i) for i in range(50, 100)) + assert callback_data == [str(i) for i in range(50, 100)] diff --git a/tests/ext/test_callbackqueryhandler.py b/tests/ext/test_callbackqueryhandler.py index 60530d644..f150f778f 100644 --- a/tests/ext/test_callbackqueryhandler.py +++ b/tests/ext/test_callbackqueryhandler.py @@ -65,7 +65,7 @@ def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope="function") +@pytest.fixture() def callback_query(bot): update = Update(0, callback_query=CallbackQuery(2, User(1, "", False), None, data="test data")) update._unfreeze() @@ -83,7 +83,7 @@ class TestCallbackQueryHandler: assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False def callback_basic(self, update, context): diff --git a/tests/ext/test_chatjoinrequesthandler.py b/tests/ext/test_chatjoinrequesthandler.py index baed6df45..bc359a6c1 100644 --- a/tests/ext/test_chatjoinrequesthandler.py +++ b/tests/ext/test_chatjoinrequesthandler.py @@ -96,7 +96,7 @@ def chat_join_request(time, bot): return cjr -@pytest.fixture(scope="function") +@pytest.fixture() def chat_join_request_update(bot, chat_join_request): return Update(0, chat_join_request=chat_join_request) @@ -111,7 +111,7 @@ class TestChatJoinRequestHandler: assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_chatmemberhandler.py b/tests/ext/test_chatmemberhandler.py index d10348fc8..45f16eb35 100644 --- a/tests/ext/test_chatmemberhandler.py +++ b/tests/ext/test_chatmemberhandler.py @@ -81,7 +81,7 @@ def chat_member_updated(): ) -@pytest.fixture(scope="function") +@pytest.fixture() def chat_member(bot, chat_member_updated): update = Update(0, my_chat_member=chat_member_updated) update._unfreeze() @@ -98,7 +98,7 @@ class TestChatMemberHandler: assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_choseninlineresulthandler.py b/tests/ext/test_choseninlineresulthandler.py index 3ccae1532..83c986671 100644 --- a/tests/ext/test_choseninlineresulthandler.py +++ b/tests/ext/test_choseninlineresulthandler.py @@ -82,7 +82,7 @@ class TestChosenInlineResultHandler: test_flag = False @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False def test_slot_behaviour(self): diff --git a/tests/ext/test_commandhandler.py b/tests/ext/test_commandhandler.py index 5dc7ae82f..c925a494f 100644 --- a/tests/ext/test_commandhandler.py +++ b/tests/ext/test_commandhandler.py @@ -51,7 +51,7 @@ class BaseTest: SRE_TYPE = type(re.match("", "")) @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def response(self, application, update): diff --git a/tests/ext/test_conversationhandler.py b/tests/ext/test_conversationhandler.py index ce15b3c72..913a6a6a7 100644 --- a/tests/ext/test_conversationhandler.py +++ b/tests/ext/test_conversationhandler.py @@ -103,7 +103,7 @@ class TestConversationHandler: # Test related @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.raise_app_handler_stop = False self.test_flag = False self.current_state = {} @@ -1106,7 +1106,7 @@ class TestConversationHandler: async def raise_error(*a, **kw): if test_type == "none": - return None + return raise error handler = ConversationHandler( @@ -1316,7 +1316,7 @@ class TestConversationHandler: ) def timeout(*args, **kwargs): - raise ApplicationHandlerStop() + raise ApplicationHandlerStop self.states.update({ConversationHandler.TIMEOUT: [TypeHandler(Update, timeout)]}) app.add_handler(handler) @@ -2130,10 +2130,7 @@ class TestConversationHandler: handler = CommandHandler("start", callback=callback) fallback = MessageHandler(filters.ALL, callback, block=handler_block) - if default_block is not None: - defaults = Defaults(block=default_block) - else: - defaults = None + defaults = Defaults(block=default_block) if default_block is not None else None if ch_block is not None: conv_handler = ConversationHandler( diff --git a/tests/ext/test_dictpersistence.py b/tests/ext/test_dictpersistence.py index ed739b2b0..ae9d71dfb 100644 --- a/tests/ext/test_dictpersistence.py +++ b/tests/ext/test_dictpersistence.py @@ -25,33 +25,33 @@ from tests.auxil.slots import mro_slots @pytest.fixture(autouse=True) -def reset_callback_data_cache(cdc_bot): +def _reset_callback_data_cache(cdc_bot): yield cdc_bot.callback_data_cache.clear_callback_data() cdc_bot.callback_data_cache.clear_callback_queries() -@pytest.fixture(scope="function") +@pytest.fixture() def bot_data(): return {"test1": "test2", "test3": {"test4": "test5"}} -@pytest.fixture(scope="function") +@pytest.fixture() def chat_data(): return {-12345: {"test1": "test2", "test3": {"test4": "test5"}}, -67890: {3: "test4"}} -@pytest.fixture(scope="function") +@pytest.fixture() def user_data(): return {12345: {"test1": "test2", "test3": {"test4": "test5"}}, 67890: {3: "test4"}} -@pytest.fixture(scope="function") +@pytest.fixture() def callback_data(): return [("test1", 1000, {"button1": "test0", "button2": "test1"})], {"test1": "test2"} -@pytest.fixture(scope="function") +@pytest.fixture() def conversations(): return { "name1": {(123, 123): 3, (456, 654): 4}, @@ -60,27 +60,27 @@ def conversations(): } -@pytest.fixture(scope="function") +@pytest.fixture() def user_data_json(user_data): return json.dumps(user_data) -@pytest.fixture(scope="function") +@pytest.fixture() def chat_data_json(chat_data): return json.dumps(chat_data) -@pytest.fixture(scope="function") +@pytest.fixture() def bot_data_json(bot_data): return json.dumps(bot_data) -@pytest.fixture(scope="function") +@pytest.fixture() def callback_data_json(callback_data): return json.dumps(callback_data) -@pytest.fixture(scope="function") +@pytest.fixture() def conversations_json(conversations): return """{"name1": {"[123, 123]": 3, "[456, 654]": 4}, "name2": {"[123, 321]": 1, "[890, 890]": 2}, "name3": @@ -303,7 +303,7 @@ class TestDictPersistence: conversation1 = await dict_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not dict_persistence.conversations["name1"] == conversation1 + assert dict_persistence.conversations["name1"] != conversation1 await dict_persistence.update_conversation("name1", (123, 123), 5) assert dict_persistence.conversations["name1"] == conversation1 conversations["name1"][(123, 123)] = 5 diff --git a/tests/ext/test_filters.py b/tests/ext/test_filters.py index bc08d52d6..80737f9ee 100644 --- a/tests/ext/test_filters.py +++ b/tests/ext/test_filters.py @@ -38,7 +38,7 @@ from telegram.ext import filters from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function") +@pytest.fixture() def update(): update = Update( 0, @@ -64,7 +64,7 @@ def update(): return update -@pytest.fixture(scope="function", params=MessageEntity.ALL_TYPES) +@pytest.fixture(params=MessageEntity.ALL_TYPES) def message_entity(request): return MessageEntity(request.param, 0, 0, url="", user=User(1, "first_name", False)) @@ -87,7 +87,7 @@ class TestFilters: """ def filter_class(obj): - return True if inspect.isclass(obj) and "filters" in repr(obj) else False + return bool(inspect.isclass(obj) and "filters" in repr(obj)) # The total no. of filters is about 72 as of 31/10/21. # Gather all the filters to test using DFS- @@ -98,7 +98,7 @@ class TestFilters: cls = stack[-1][-1] # get last element and its class for inner_cls in inspect.getmembers( cls, # Get inner filters - lambda a: inspect.isclass(a) and not issubclass(a, cls.__class__), + lambda a: inspect.isclass(a) and not issubclass(a, cls.__class__), # noqa: B023 ): if inner_cls[1] not in visited: stack.append(inner_cls) @@ -1096,7 +1096,7 @@ class TestFilters: assert not filters.Entity(message_entity.type).check_update(update) @pytest.mark.parametrize( - "chat_type, results", + ("chat_type", "results"), [ (Chat.PRIVATE, (True, False, False, False, False)), (Chat.GROUP, (False, True, False, True, False)), @@ -1252,7 +1252,8 @@ class TestFilters: f.add_usernames("@barfoo") assert str(f).startswith("filters.User(") # we don't know th exact order - assert "barfoo" in str(f) and "foobar" in str(f) + assert "barfoo" in str(f) + assert "foobar" in str(f) with pytest.raises(RuntimeError, match="Cannot set name"): f.name = "foo" @@ -1411,7 +1412,8 @@ class TestFilters: f.add_usernames("@barfoo") assert str(f).startswith("filters.Chat(") # we don't know th exact order - assert "barfoo" in str(f) and "foobar" in str(f) + assert "barfoo" in str(f) + assert "foobar" in str(f) with pytest.raises(RuntimeError, match="Cannot set name"): f.name = "foo" @@ -1661,7 +1663,8 @@ class TestFilters: f.add_usernames("@barfoo") assert str(f).startswith("filters.ForwardedFrom(") # we don't know the exact order - assert "barfoo" in str(f) and "foobar" in str(f) + assert "barfoo" in str(f) + assert "foobar" in str(f) with pytest.raises(RuntimeError, match="Cannot set name"): f.name = "foo" @@ -1808,7 +1811,8 @@ class TestFilters: f.add_usernames("@barfoo") assert str(f).startswith("filters.SenderChat(") # we don't know th exact order - assert "barfoo" in str(f) and "foobar" in str(f) + assert "barfoo" in str(f) + assert "foobar" in str(f) with pytest.raises(RuntimeError, match="Cannot set name"): f.name = "foo" @@ -1879,7 +1883,8 @@ class TestFilters: @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI) def test_filters_dice(self, update, emoji): update.message.dice = Dice(4, emoji) - assert filters.Dice.ALL.check_update(update) and filters.Dice().check_update(update) + assert filters.Dice.ALL.check_update(update) + assert filters.Dice().check_update(update) to_camel = emoji.name.title().replace("_", "") assert repr(filters.Dice.ALL) == "filters.Dice.ALL" @@ -2217,13 +2222,16 @@ class TestFilters: self.data = data def filter(self, _): - return {"test": [self.data]} + return {"test": [self.data], "test2": {"test3": [self.data]}} result = (filters.COMMAND & DataFilter("blah")).check_update(update) assert result["test"] == ["blah"] + assert not result["test2"] result = (DataFilter("blah1") & DataFilter("blah2")).check_update(update) assert result["test"] == ["blah1", "blah2"] + assert isinstance(result["test2"], list) + assert result["test2"][0]["test3"] == ["blah1"] update.message.text = "test" update.message.entities = [] @@ -2390,7 +2398,8 @@ class TestFilters: f.add_usernames("@barfoo") assert str(f).startswith("filters.ViaBot(") # we don't know th exact order - assert "barfoo" in str(f) and "foobar" in str(f) + assert "barfoo" in str(f) + assert "foobar" in str(f) with pytest.raises(RuntimeError, match="Cannot set name"): f.name = "foo" diff --git a/tests/ext/test_jobqueue.py b/tests/ext/test_jobqueue.py index 27fd05498..82ee293ba 100644 --- a/tests/ext/test_jobqueue.py +++ b/tests/ext/test_jobqueue.py @@ -20,14 +20,13 @@ import asyncio import calendar import datetime as dtm import logging -import os import platform import time import pytest from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Job, JobQueue -from tests.auxil.envvars import TEST_WITH_OPT_DEPS +from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS from tests.auxil.pytest_classes import make_bot from tests.auxil.slots import mro_slots @@ -45,7 +44,7 @@ class CustomContext(CallbackContext): pass -@pytest.fixture(scope="function") +@pytest.fixture() async def job_queue(app): jq = JobQueue() jq.set_application(app) @@ -71,7 +70,7 @@ class TestNoJobQueue: not TEST_WITH_OPT_DEPS, reason="Only relevant if the optional dependency is installed" ) @pytest.mark.skipif( - os.getenv("GITHUB_ACTIONS", False) and platform.system() in ["Windows", "Darwin"], + bool(GITHUB_ACTION and platform.system() in ["Windows", "Darwin"]), reason="On Windows & MacOS precise timings are not accurate.", ) @pytest.mark.flaky(10, 1) # Timings aren't quite perfect @@ -86,7 +85,7 @@ class TestJobQueue: ) @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.result = 0 self.job_time = 0 self.received_error = None @@ -362,7 +361,7 @@ class TestJobQueue: assert str(recwarn[0].message) == self.expected_warning assert recwarn[0].filename == __file__, "wrong stacklevel" - @pytest.mark.parametrize("weekday", (0, 1, 2, 3, 4, 5, 6)) + @pytest.mark.parametrize("weekday", [0, 1, 2, 3, 4, 5, 6]) async def test_run_daily_days_of_week(self, job_queue, recwarn, weekday): delta, now = 1, dtm.datetime.now(UTC) time_of_day = (now + dtm.timedelta(seconds=delta)).time() @@ -592,7 +591,7 @@ class TestJobQueue: ): job.error - @pytest.mark.parametrize("wait", (True, False)) + @pytest.mark.parametrize("wait", [True, False]) async def test_wait_on_shut_down(self, job_queue, wait): ready_event = asyncio.Event() diff --git a/tests/ext/test_messagehandler.py b/tests/ext/test_messagehandler.py index bd5ea513c..e521c6ac8 100644 --- a/tests/ext/test_messagehandler.py +++ b/tests/ext/test_messagehandler.py @@ -83,7 +83,7 @@ class TestMessageHandler: assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_picklepersistence.py b/tests/ext/test_picklepersistence.py index bcdf21764..2164e12c7 100644 --- a/tests/ext/test_picklepersistence.py +++ b/tests/ext/test_picklepersistence.py @@ -32,7 +32,7 @@ from tests.auxil.slots import mro_slots @pytest.fixture(autouse=True) -def change_directory(tmp_path: Path): +def _change_directory(tmp_path: Path): orig_dir = Path.cwd() # Switch to a temporary directory, so we don't have to worry about cleaning up files os.chdir(tmp_path) @@ -42,33 +42,33 @@ def change_directory(tmp_path: Path): @pytest.fixture(autouse=True) -def reset_callback_data_cache(cdc_bot): +def _reset_callback_data_cache(cdc_bot): yield cdc_bot.callback_data_cache.clear_callback_data() cdc_bot.callback_data_cache.clear_callback_queries() -@pytest.fixture(scope="function") +@pytest.fixture() def bot_data(): return {"test1": "test2", "test3": {"test4": "test5"}} -@pytest.fixture(scope="function") +@pytest.fixture() def chat_data(): return {-12345: {"test1": "test2", "test3": {"test4": "test5"}}, -67890: {3: "test4"}} -@pytest.fixture(scope="function") +@pytest.fixture() def user_data(): return {12345: {"test1": "test2", "test3": {"test4": "test5"}}, 67890: {3: "test4"}} -@pytest.fixture(scope="function") +@pytest.fixture() def callback_data(): return [("test1", 1000, {"button1": "test0", "button2": "test1"})], {"test1": "test2"} -@pytest.fixture(scope="function") +@pytest.fixture() def conversations(): return { "name1": {(123, 123): 3, (456, 654): 4}, @@ -77,7 +77,7 @@ def conversations(): } -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_persistence(): return PicklePersistence( filepath="pickletest", @@ -86,7 +86,7 @@ def pickle_persistence(): ) -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_persistence_only_bot(): return PicklePersistence( filepath="pickletest", @@ -96,7 +96,7 @@ def pickle_persistence_only_bot(): ) -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_persistence_only_chat(): return PicklePersistence( filepath="pickletest", @@ -106,7 +106,7 @@ def pickle_persistence_only_chat(): ) -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_persistence_only_user(): return PicklePersistence( filepath="pickletest", @@ -116,7 +116,7 @@ def pickle_persistence_only_user(): ) -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_persistence_only_callback(): return PicklePersistence( filepath="pickletest", @@ -126,7 +126,7 @@ def pickle_persistence_only_callback(): ) -@pytest.fixture(scope="function") +@pytest.fixture() def bad_pickle_files(): for name in [ "pickletest_user_data", @@ -137,10 +137,10 @@ def bad_pickle_files(): "pickletest", ]: Path(name).write_text("(())") - yield True + return True -@pytest.fixture(scope="function") +@pytest.fixture() def invalid_pickle_files(): for name in [ "pickletest_user_data", @@ -154,10 +154,10 @@ def invalid_pickle_files(): # see https://stackoverflow.com/a/44422239/10606962 with gzip.open(name, "wb") as file: pickle.dump([1, 2, 3], file) - yield True + return True -@pytest.fixture(scope="function") +@pytest.fixture() def good_pickle_files(user_data, chat_data, bot_data, callback_data, conversations): data = { "user_data": user_data, @@ -178,10 +178,10 @@ def good_pickle_files(user_data, chat_data, bot_data, callback_data, conversatio pickle.dump(conversations, f) with Path("pickletest").open("wb") as f: pickle.dump(data, f) - yield True + return True -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_files_wo_bot_data(user_data, chat_data, callback_data, conversations): data = { "user_data": user_data, @@ -199,10 +199,10 @@ def pickle_files_wo_bot_data(user_data, chat_data, callback_data, conversations) pickle.dump(conversations, f) with Path("pickletest").open("wb") as f: pickle.dump(data, f) - yield True + return True -@pytest.fixture(scope="function") +@pytest.fixture() def pickle_files_wo_callback_data(user_data, chat_data, bot_data, conversations): data = { "user_data": user_data, @@ -220,10 +220,10 @@ def pickle_files_wo_callback_data(user_data, chat_data, bot_data, conversations) pickle.dump(conversations, f) with Path("pickletest").open("wb") as f: pickle.dump(data, f) - yield True + return True -@pytest.fixture(scope="function") +@pytest.fixture() def update(bot): user = User(id=321, first_name="test_user", is_bot=False) chat = Chat(id=123, type="group") @@ -261,7 +261,7 @@ class TestPicklePersistence: assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - @pytest.mark.parametrize("on_flush", (True, False)) + @pytest.mark.parametrize("on_flush", [True, False]) async def test_on_flush(self, pickle_persistence, on_flush): pickle_persistence.on_flush = on_flush pickle_persistence.single_file = True @@ -610,7 +610,7 @@ class TestPicklePersistence: conversation1 = await pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations["name1"] == conversation1 + assert pickle_persistence.conversations["name1"] != conversation1 await pickle_persistence.update_conversation("name1", (123, 123), 5) assert pickle_persistence.conversations["name1"] == conversation1 assert await pickle_persistence.get_conversations("name1") == conversation1 @@ -668,7 +668,7 @@ class TestPicklePersistence: conversation1 = await pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations["name1"] == conversation1 + assert pickle_persistence.conversations["name1"] != conversation1 await pickle_persistence.update_conversation("name1", (123, 123), 5) assert pickle_persistence.conversations["name1"] == conversation1 assert await pickle_persistence.get_conversations("name1") == conversation1 @@ -693,8 +693,8 @@ class TestPicklePersistence: ] ) await pickle_persistence.flush() - with pytest.raises(FileNotFoundError, match="pickletest"): - open("pickletest", "rb") + with pytest.raises(FileNotFoundError, match="pickletest"), Path("pickletest").open("rb"): + pass async def test_save_on_flush_multi_files(self, pickle_persistence, good_pickle_files): # Should run without error @@ -755,14 +755,14 @@ class TestPicklePersistence: conversation1 = await pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations["name1"] == conversation1 + assert pickle_persistence.conversations["name1"] != conversation1 await pickle_persistence.update_conversation("name1", (123, 123), 5) assert pickle_persistence.conversations["name1"] == conversation1 with Path("pickletest_conversations").open("rb") as f: conversations_test = dict(pickle.load(f)) - assert not conversations_test["name1"] == conversation1 + assert conversations_test["name1"] != conversation1 await pickle_persistence.flush() with Path("pickletest_user_data").open("rb") as f: @@ -828,12 +828,12 @@ class TestPicklePersistence: conversation1 = await pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations["name1"] == conversation1 + assert pickle_persistence.conversations["name1"] != conversation1 await pickle_persistence.update_conversation("name1", (123, 123), 5) assert pickle_persistence.conversations["name1"] == conversation1 with Path("pickletest").open("rb") as f: conversations_test = dict(pickle.load(f))["conversations"] - assert not conversations_test["name1"] == conversation1 + assert conversations_test["name1"] != conversation1 await pickle_persistence.flush() with Path("pickletest").open("rb") as f: @@ -870,9 +870,10 @@ class TestPicklePersistence: "A load persistent id instruction was encountered,\nbut no persistent_load " "function was specified." ) - with pytest.raises(pickle.UnpicklingError, match=err_msg): - with open("pickletest_chat_data", "rb") as f: - pickle.load(f) + with Path("pickletest_chat_data").open("rb") as f, pytest.raises( + pickle.UnpicklingError, match=err_msg + ): + pickle.load(f) # Test that our custom unpickler works as intended -- inserts the current bot # We have to create a new instance otherwise unpickling is skipped diff --git a/tests/ext/test_pollanswerhandler.py b/tests/ext/test_pollanswerhandler.py index 58411324d..724e22f40 100644 --- a/tests/ext/test_pollanswerhandler.py +++ b/tests/ext/test_pollanswerhandler.py @@ -67,7 +67,7 @@ def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope="function") +@pytest.fixture() def poll_answer(bot): return Update(0, poll_answer=PollAnswer(1, User(2, "test user", False), [0, 1])) @@ -82,7 +82,7 @@ class TestPollAnswerHandler: assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_precheckoutqueryhandler.py b/tests/ext/test_precheckoutqueryhandler.py index d717a45fa..c22e058ff 100644 --- a/tests/ext/test_precheckoutqueryhandler.py +++ b/tests/ext/test_precheckoutqueryhandler.py @@ -87,7 +87,7 @@ class TestPreCheckoutQueryHandler: assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_ratelimiter.py b/tests/ext/test_ratelimiter.py index 7db1a4b00..e2c949ce1 100644 --- a/tests/ext/test_ratelimiter.py +++ b/tests/ext/test_ratelimiter.py @@ -23,7 +23,6 @@ notable """ import asyncio import json -import os import platform import time from datetime import datetime @@ -36,7 +35,7 @@ from telegram.constants import ParseMode from telegram.error import RetryAfter from telegram.ext import AIORateLimiter, BaseRateLimiter, Defaults, ExtBot from telegram.request import BaseRequest, RequestData -from tests.auxil.envvars import TEST_WITH_OPT_DEPS +from tests.auxil.envvars import GITHUB_ACTION, TEST_WITH_OPT_DEPS @pytest.mark.skipif( @@ -120,7 +119,7 @@ class TestBaseRateLimiter: assert self.rl_received[0] == ("getMe", {}, None) assert self.rl_received[1] == ( "setMyCommands", - dict(commands=[BotCommand("test", "test")], language_code="en", api="kwargs"), + {"commands": [BotCommand("test", "test")], "language_code": "en", "api": "kwargs"}, (43, "test-1"), ) assert len(self.request_received) == 4 @@ -143,7 +142,7 @@ class TestBaseRateLimiter: not TEST_WITH_OPT_DEPS, reason="Only relevant if the optional dependency is installed" ) @pytest.mark.skipif( - os.getenv("GITHUB_ACTIONS", False) and platform.system() == "Darwin", + bool(GITHUB_ACTION and platform.system() == "Darwin"), reason="The timings are apparently rather inaccurate on MacOS.", ) @pytest.mark.flaky(10, 1) # Timings aren't quite perfect @@ -187,9 +186,10 @@ class TestAIORateLimiter: } ).encode(), ) + return None @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.count = 0 TestAIORateLimiter.count = 0 self.call_times = [] diff --git a/tests/ext/test_shippingqueryhandler.py b/tests/ext/test_shippingqueryhandler.py index 88efc521d..2d8b3bb80 100644 --- a/tests/ext/test_shippingqueryhandler.py +++ b/tests/ext/test_shippingqueryhandler.py @@ -91,7 +91,7 @@ class TestShippingQueryHandler: assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_stringcommandhandler.py b/tests/ext/test_stringcommandhandler.py index 422e597b5..6d8f9d77b 100644 --- a/tests/ext/test_stringcommandhandler.py +++ b/tests/ext/test_stringcommandhandler.py @@ -79,7 +79,7 @@ class TestStringCommandHandler: assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_stringregexhandler.py b/tests/ext/test_stringregexhandler.py index 42d398192..208a63cf4 100644 --- a/tests/ext/test_stringregexhandler.py +++ b/tests/ext/test_stringregexhandler.py @@ -80,7 +80,7 @@ class TestStringRegexHandler: assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): @@ -98,7 +98,7 @@ class TestStringRegexHandler: if context.matches[0].groupdict(): self.test_flag = context.matches[0].groupdict() == {"begin": "t", "end": " message"} - @pytest.mark.parametrize("compile", (True, False)) + @pytest.mark.parametrize("compile", [True, False]) async def test_basic(self, app, compile): pattern = "(?P.*)est(?P.*)" if compile: diff --git a/tests/ext/test_typehandler.py b/tests/ext/test_typehandler.py index 0514739ed..8b3e64071 100644 --- a/tests/ext/test_typehandler.py +++ b/tests/ext/test_typehandler.py @@ -36,7 +36,7 @@ class TestTypeHandler: assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/ext/test_updater.py b/tests/ext/test_updater.py index 0a63d9f4a..4eb779527 100644 --- a/tests/ext/test_updater.py +++ b/tests/ext/test_updater.py @@ -46,8 +46,8 @@ if TEST_WITH_OPT_DEPS: ) class TestNoWebhooks: async def test_no_webhooks(self, bot): - with pytest.raises(RuntimeError, match=r"python-telegram-bot\[webhooks\]"): - async with Updater(bot=bot, update_queue=asyncio.Queue()) as updater: + async with Updater(bot=bot, update_queue=asyncio.Queue()) as updater: + with pytest.raises(RuntimeError, match=r"python-telegram-bot\[webhooks\]"): await updater.start_webhook() @@ -65,7 +65,7 @@ class TestUpdater: test_flag = False @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.message_count = 0 self.received = None self.attempts = 0 @@ -84,8 +84,8 @@ class TestUpdater: async def test_slot_behaviour(self, updater): async with updater: for at in updater.__slots__: - at = f"_Updater{at}" if at.startswith("__") and not at.endswith("__") else at - assert getattr(updater, at, "err") != "err", f"got extra slot '{at}'" + attr = f"_Updater{at}" if at.startswith("__") and not at.endswith("__") else at + assert getattr(updater, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(updater)) == len(set(mro_slots(updater))), "duplicate slot" def test_init(self, bot): @@ -207,7 +207,7 @@ class TestUpdater: assert self.test_flag == "stop" - @pytest.mark.parametrize("drop_pending_updates", (True, False)) + @pytest.mark.parametrize("drop_pending_updates", [True, False]) async def test_polling_basic(self, monkeypatch, updater, drop_pending_updates): updates = asyncio.Queue() await updates.put(Update(update_id=1)) @@ -277,15 +277,15 @@ class TestUpdater: update_queue = asyncio.Queue() await update_queue.put(Update(update_id=1)) - expected = dict( - timeout=10, - read_timeout=2, - write_timeout=DEFAULT_NONE, - connect_timeout=DEFAULT_NONE, - pool_timeout=DEFAULT_NONE, - allowed_updates=None, - api_kwargs=None, - ) + expected = { + "timeout": 10, + "read_timeout": 2, + "write_timeout": DEFAULT_NONE, + "connect_timeout": DEFAULT_NONE, + "pool_timeout": DEFAULT_NONE, + "allowed_updates": None, + "api_kwargs": None, + } async def get_updates(*args, **kwargs): for key, value in expected.items(): @@ -310,15 +310,15 @@ class TestUpdater: await update_queue.join() await updater.stop() - expected = dict( - timeout=42, - read_timeout=43, - write_timeout=44, - connect_timeout=45, - pool_timeout=46, - allowed_updates=["message"], - api_kwargs=None, - ) + expected = { + "timeout": 42, + "read_timeout": 43, + "write_timeout": 44, + "connect_timeout": 45, + "pool_timeout": 46, + "allowed_updates": ["message"], + "api_kwargs": None, + } await update_queue.put(Update(update_id=2)) await updater.start_polling( @@ -332,8 +332,8 @@ class TestUpdater: await update_queue.join() await updater.stop() - @pytest.mark.parametrize("exception_class", (InvalidToken, TelegramError)) - @pytest.mark.parametrize("retries", (3, 0)) + @pytest.mark.parametrize("exception_class", [InvalidToken, TelegramError]) + @pytest.mark.parametrize("retries", [3, 0]) async def test_start_polling_bootstrap_retries( self, updater, monkeypatch, exception_class, retries ): @@ -354,7 +354,7 @@ class TestUpdater: await updater.start_polling(bootstrap_retries=retries) @pytest.mark.parametrize( - "error,callback_should_be_called", + ("error", "callback_should_be_called"), argvalues=[ (TelegramError("TestMessage"), True), (RetryAfter(1), False), @@ -414,13 +414,12 @@ class TestUpdater: await get_updates_event.wait() if callback_should_be_called: - if callback_should_be_called: - if custom_error_callback: - assert self.received == error - else: - assert len(caplog.records) > 0 - records = (record.getMessage() for record in caplog.records) - assert "Error while getting Updates: TestMessage" in records + if custom_error_callback: + assert self.received == error + else: + assert len(caplog.records) > 0 + records = (record.getMessage() for record in caplog.records) + assert "Error while getting Updates: TestMessage" in records await updater.stop() async def test_start_polling_unexpected_shutdown(self, updater, monkeypatch, caplog): @@ -517,7 +516,7 @@ class TestUpdater: assert not updater.running @pytest.mark.parametrize("ext_bot", [True, False]) - @pytest.mark.parametrize("drop_pending_updates", (True, False)) + @pytest.mark.parametrize("drop_pending_updates", [True, False]) @pytest.mark.parametrize("secret_token", ["SecretToken", None]) async def test_webhook_basic( self, monkeypatch, updater, drop_pending_updates, ext_bot, secret_token @@ -627,9 +626,9 @@ class TestUpdater: await updater.stop() async def test_start_webhook_parameters_passing(self, updater, monkeypatch): - expected_delete_webhook = dict( - drop_pending_updates=None, - ) + expected_delete_webhook = { + "drop_pending_updates": None, + } expected_set_webhook = dict( certificate=None, @@ -668,10 +667,10 @@ class TestUpdater: async with updater: await updater.start_webhook() await updater.stop() - expected_delete_webhook = dict( - drop_pending_updates=True, - api_kwargs=None, - ) + expected_delete_webhook = { + "drop_pending_updates": True, + "api_kwargs": None, + } expected_set_webhook = dict( certificate=data_file("sslcert.pem").read_bytes(), @@ -819,8 +818,8 @@ class TestUpdater: assert self.test_flag == [True, True] await updater.stop() - @pytest.mark.parametrize("exception_class", (InvalidToken, TelegramError)) - @pytest.mark.parametrize("retries", (3, 0)) + @pytest.mark.parametrize("exception_class", [InvalidToken, TelegramError]) + @pytest.mark.parametrize("retries", [3, 0]) async def test_start_webhook_bootstrap_retries( self, updater, monkeypatch, exception_class, retries ): diff --git a/tests/request/test_request.py b/tests/request/test_request.py index 4f64d13d6..e2b3b066d 100644 --- a/tests/request/test_request.py +++ b/tests/request/test_request.py @@ -65,7 +65,7 @@ def mocker_factory( return make_assertion -@pytest.fixture(scope="function") +@pytest.fixture() async def httpx_request(): async with HTTPXRequest() as rq: yield rq @@ -102,7 +102,7 @@ class TestRequestWithoutRequest: test_flag = None @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = None async def test_init_import_errors(self, monkeypatch): @@ -120,9 +120,8 @@ class TestRequestWithoutRequest: def test_slot_behaviour(self): inst = HTTPXRequest() for attr in inst.__slots__: - if attr.startswith("__"): - attr = f"_{inst.__class__.__name__}{attr}" - assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" + at = f"_{inst.__class__.__name__}{attr}" if attr.startswith("__") else attr + assert getattr(inst, at, "err") != "err", f"got extra slot '{at}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" async def test_context_manager(self, monkeypatch): @@ -257,7 +256,7 @@ class TestRequestWithoutRequest: await httpx_request.post(None, None, None) @pytest.mark.parametrize( - "code, exception_class", + ("code", "exception_class"), [ (HTTPStatus.FORBIDDEN, Forbidden), (HTTPStatus.NOT_FOUND, InvalidToken), @@ -283,7 +282,7 @@ class TestRequestWithoutRequest: await httpx_request.post("", None, None) @pytest.mark.parametrize( - ["exception", "catch_class", "match"], + ("exception", "catch_class", "match"), [ (TelegramError("TelegramError"), TelegramError, "TelegramError"), ( @@ -344,7 +343,7 @@ class TestHTTPXRequestWithoutRequest: test_flag = None @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = None def test_init(self, monkeypatch): @@ -564,7 +563,7 @@ class TestHTTPXRequestWithoutRequest: assert content == b"content" @pytest.mark.parametrize( - ["raised_class", "expected_class", "expected_message"], + ("raised_class", "expected_class", "expected_message"), [ (httpx.TimeoutException, TimedOut, "Timed out"), (httpx.ReadError, NetworkError, "httpx.ReadError: message"), @@ -594,8 +593,8 @@ class TestHTTPXRequestWithoutRequest: monkeypatch.setattr(httpx.AsyncClient, "request", request) - with pytest.raises(TimedOut, match="Pool timeout"): - async with HTTPXRequest(pool_timeout=0.02) as httpx_request: + async with HTTPXRequest(pool_timeout=0.02) as httpx_request: + with pytest.raises(TimedOut, match="Pool timeout"): await asyncio.gather( httpx_request.do_request(method="GET", url="URL"), httpx_request.do_request(method="GET", url="URL"), diff --git a/tests/request/test_requestparameter.py b/tests/request/test_requestparameter.py index 935651f16..2f78e4835 100644 --- a/tests/request/test_requestparameter.py +++ b/tests/request/test_requestparameter.py @@ -47,7 +47,7 @@ class TestRequestParameterWithoutRequest: assert request_parameter.input_files is None @pytest.mark.parametrize( - "value, expected", + ("value", "expected"), [ (1, "1"), ("one", "one"), diff --git a/tests/test_bot.py b/tests/test_bot.py index cc558cc69..50398fdfd 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -93,7 +93,7 @@ def to_camel_case(snake_str): return components[0] + "".join(x.title() for x in components[1:]) -@pytest.fixture(scope="function") +@pytest.fixture() async def message(bot, chat_id): # mostly used in tests for edit_message out = await bot.send_message( chat_id, "Text", disable_web_page_preview=True, disable_notification=True @@ -200,8 +200,8 @@ class TestBotWithoutRequest: test_flag = None - @pytest.fixture(scope="function", autouse=True) - def reset(self): + @pytest.fixture(autouse=True) + def _reset(self): self.test_flag = None @pytest.mark.parametrize("bot_class", [Bot, ExtBot]) @@ -447,12 +447,9 @@ class TestBotWithoutRequest: Finally, there are some tests for Defaults.{parse_mode, quote, allow_sending_without_reply} at the appropriate places, as those are the only things we can actually check. """ - if bot_method_name.lower().replace("_", "") == "getme": - # Mocking get_me within check_defaults_handling messes with the cached values like - # Bot.{bot, username, id, …}` unless we return the expected User object. - return_value = bot.bot - else: - return_value = None + # Mocking get_me within check_defaults_handling messes with the cached values like + # Bot.{bot, username, id, …}` unless we return the expected User object. + return_value = bot.bot if bot_method_name.lower().replace("_", "") == "getme" else None # Check that ExtBot does the right thing bot_method = getattr(bot, bot_method_name) @@ -526,8 +523,7 @@ class TestBotWithoutRequest: "id": "1", }, } - web_app_msg = SentWebAppMessage("321").to_dict() - return web_app_msg + return SentWebAppMessage("321").to_dict() # We test different result types more thoroughly for answer_inline_query, so we just # use the one type here @@ -558,7 +554,7 @@ class TestBotWithoutRequest: indirect=True, ) @pytest.mark.parametrize( - "ilq_result,expected_params", + ("ilq_result", "expected_params"), [ ( InlineQueryResultArticle("1", "title", InputTextMessageContent("text")), @@ -632,8 +628,7 @@ class TestBotWithoutRequest: async def make_assertion(url, request_data: RequestData, *args, **kwargs): nonlocal params params = request_data.parameters == expected_params - web_app_msg = SentWebAppMessage("321").to_dict() - return web_app_msg + return SentWebAppMessage("321").to_dict() monkeypatch.setattr(bot.request, "post", make_assertion) @@ -908,7 +903,7 @@ class TestBotWithoutRequest: ) @pytest.mark.parametrize( - "current_offset,num_results,id_offset,expected_next_offset", + ("current_offset", "num_results", "id_offset", "expected_next_offset"), [ ("", InlineQueryLimit.RESULTS, 1, 1), (1, InlineQueryLimit.RESULTS, 51, 2), @@ -961,7 +956,7 @@ class TestBotWithoutRequest: results = data["results"] length_matches = len(results) == 30 ids_match = all(int(res["id"]) == 1 + i for i, res in enumerate(results)) - next_offset_matches = data["next_offset"] == "" + next_offset_matches = not data["next_offset"] return length_matches and ids_match and next_offset_matches monkeypatch.setattr(bot.request, "post", make_assertion) @@ -988,7 +983,7 @@ class TestBotWithoutRequest: data = request_data.parameters results = data["results"] length = results == [] - next_offset = data["next_offset"] == "" + next_offset = not data["next_offset"] return length and next_offset monkeypatch.setattr(bot.request, "post", make_assertion) @@ -1411,7 +1406,7 @@ class TestBotWithoutRequest: # here we test more extensively. @pytest.mark.parametrize( - "acd_in,maxsize", + ("acd_in", "maxsize"), [(True, 1024), (False, 1024), (0, 0), (None, None)], ) async def test_callback_data_maxsize(self, bot_info, acd_in, maxsize): @@ -1909,10 +1904,11 @@ class TestBotWithRequest: assert message_quiz.poll.is_closed assert message_quiz.poll.explanation == "Here is a link" assert message_quiz.poll.explanation_entities == tuple(explanation_entities) - assert poll_task.done() and quiz_task.done() + assert poll_task.done() + assert quiz_task.done() @pytest.mark.parametrize( - ["open_period", "close_date"], [(5, None), (None, True)], ids=["open_period", "close_date"] + ("open_period", "close_date"), [(5, None), (None, True)], ids=["open_period", "close_date"] ) async def test_send_open_period(self, bot, super_group_id, open_period, close_date): question = "Is this a test?" @@ -2034,7 +2030,7 @@ class TestBotWithRequest: assert message3.poll.explanation_entities == () @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -2085,7 +2081,7 @@ class TestBotWithRequest: assert protected_poll.has_protected_content assert not unprotect_poll.has_protected_content - @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI + [None]) + @pytest.mark.parametrize("emoji", [*Dice.ALL_EMOJI, None]) async def test_send_dice(self, bot, chat_id, emoji): message = await bot.send_dice(chat_id, emoji=emoji, protect_content=True) @@ -2097,7 +2093,7 @@ class TestBotWithRequest: assert message.dice.emoji == emoji @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -2367,7 +2363,7 @@ class TestBotWithRequest: await bot.delete_webhook() await asyncio.sleep(1) info = await bot.get_webhook_info() - assert info.url == "" + assert not info.url assert info.ip_address is None assert info.has_custom_certificate is False @@ -2419,14 +2415,14 @@ class TestBotWithRequest: assert message.game.description == ( "A no-op test game, for python-telegram-bot bot framework testing." ) - assert message.game.animation.file_id != "" + assert message.game.animation.file_id # We added some test bots later and for some reason the file size is not the same for them # so we accept three different sizes here. Shouldn't be too much of assert message.game.photo[0].file_size in [851, 4928, 850] assert message.has_protected_content @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), @@ -2462,7 +2458,7 @@ class TestBotWithRequest: ) @pytest.mark.parametrize( - "default_bot,val", + ("default_bot", "val"), [({"protect_content": True}, True), ({"protect_content": False}, None)], indirect=["default_bot"], ) @@ -2629,7 +2625,7 @@ class TestBotWithRequest: # Each link is unique apparently invite_link = await bot.export_chat_invite_link(channel_id) assert isinstance(invite_link, str) - assert invite_link != "" + assert invite_link async def test_edit_revoke_chat_invite_link_passing_link_objects(self, bot, channel_id): invite_link = await bot.create_chat_invite_link(chat_id=channel_id) @@ -2683,7 +2679,7 @@ class TestBotWithRequest: invite_link = await bot.create_chat_invite_link( channel_id, expire_date=expire_time, member_limit=10 ) - assert invite_link.invite_link != "" + assert invite_link.invite_link assert not invite_link.invite_link.endswith("...") assert abs(invite_link.expire_date - aware_time_in_future) < dtm.timedelta(seconds=1) assert invite_link.member_limit == 10 @@ -2734,7 +2730,7 @@ class TestBotWithRequest: invite_link = await tz_bot.create_chat_invite_link( channel_id, expire_date=time_in_future, member_limit=10 ) - assert invite_link.invite_link != "" + assert invite_link.invite_link assert not invite_link.invite_link.endswith("...") assert abs(invite_link.expire_date - aware_expire_date) < dtm.timedelta(seconds=1) assert invite_link.member_limit == 10 @@ -2827,8 +2823,9 @@ class TestBotWithRequest: assert len(messages) == 3 # Check if we sent 3 messages - assert all([await i for i in pinned_messages_tasks]) # Check if we pinned 3 messages - assert all([i.done() for i in pinned_messages_tasks]) # Check if all tasks are done + # Check if we pinned 3 messages + assert all([await i for i in pinned_messages_tasks]) # noqa: PIE802 + assert all(i.done() for i in pinned_messages_tasks) # Check if all tasks are done chat = await bot.get_chat(super_group_id) # get the chat to check the pinned message assert chat.pinned_message in messages @@ -2848,7 +2845,7 @@ class TestBotWithRequest: bot.unpin_chat_message(chat_id=super_group_id, read_timeout=10), # unpins most recent ) assert all(await tasks) - assert all([i.done() for i in tasks]) + assert all(i.done() for i in tasks) assert await bot.unpin_all_chat_messages(super_group_id, read_timeout=10) # get_sticker_set, upload_sticker_file, create_new_sticker_set, add_sticker_to_set, @@ -2902,7 +2899,7 @@ class TestBotWithRequest: assert not no_protect.has_protected_content @pytest.mark.parametrize( - "default_bot,custom", + ("default_bot", "custom"), [ ({"allow_sending_without_reply": True}, None), ({"allow_sending_without_reply": False}, None), diff --git a/tests/test_botcommandscope.py b/tests/test_botcommandscope.py index 78766c3b7..c9aee01bd 100644 --- a/tests/test_botcommandscope.py +++ b/tests/test_botcommandscope.py @@ -111,7 +111,7 @@ def scope_class_and_type(request): def bot_command_scope(scope_class_and_type, chat_id): # we use de_json here so that we don't have to worry about which class needs which arguments return scope_class_and_type[0].de_json( - dict(type=scope_class_and_type[1], chat_id=chat_id, user_id=42), bot=None + {"type": scope_class_and_type[1], "chat_id": chat_id, "user_id": 42}, bot=None ) diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index f4cadd08f..f1b598a77 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -30,7 +30,7 @@ from tests.auxil.bot_method_checks import ( from tests.auxil.slots import mro_slots -@pytest.fixture(scope="function", params=["message", "inline"]) +@pytest.fixture(params=["message", "inline"]) def callback_query(bot, request): cbq = CallbackQuery( TestCallbackQueryBase.id_, @@ -297,8 +297,7 @@ class TestCallbackQueryWithoutRequest(TestCallbackQueryBase): async def test_stop_message_live_location(self, monkeypatch, callback_query): async def make_assertion(*_, **kwargs): - ids = self.check_passed_ids(callback_query, kwargs) - return ids + return self.check_passed_ids(callback_query, kwargs) assert check_shortcut_signature( CallbackQuery.stop_message_live_location, diff --git a/tests/test_chat.py b/tests/test_chat.py index aed3475ff..d93be7c9f 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -486,8 +486,7 @@ class TestChatWithoutRequest(TestChatBase): async def test_delete_photo(self, monkeypatch, chat): async def make_assertion(*_, **kwargs): - chat_id = kwargs["chat_id"] == chat.id - return chat_id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature(Chat.delete_photo, Bot.delete_chat_photo, ["chat_id"], []) assert await check_shortcut_call(chat.delete_photo, chat.get_bot(), "delete_chat_photo") @@ -1182,8 +1181,8 @@ class TestChatWithoutRequest(TestChatBase): assert await chat.unhide_general_forum_topic() def test_mention_html(self): + chat = Chat(id=1, type="foo") with pytest.raises(TypeError, match="Can not create a mention to a private group chat"): - chat = Chat(id=1, type="foo") chat.mention_html() expected = '{}' @@ -1192,10 +1191,10 @@ class TestChatWithoutRequest(TestChatBase): ) assert chat.mention_html("the_name*\u2022") == expected.format(chat.id, "the_name*\u2022") assert chat.mention_html() == expected.format(chat.id, chat.full_name) + chat = Chat(id=1, type=Chat.PRIVATE, last_name="last\u2022name") with pytest.raises( TypeError, match="Can not create a mention to a private chat without first name" ): - chat = Chat(id=1, type=Chat.PRIVATE, last_name="last\u2022name") chat.mention_html() expected = '{}' @@ -1204,15 +1203,15 @@ class TestChatWithoutRequest(TestChatBase): chat.username, "the_name*\u2022" ) assert chat.mention_html() == expected.format(chat.username, chat.title) + chat = Chat(id=1, type="foo", username="user\u2022name") with pytest.raises( TypeError, match="Can not create a mention to a public chat without title" ): - chat = Chat(id=1, type="foo", username="user\u2022name") chat.mention_html() def test_mention_markdown(self): + chat = Chat(id=1, type="foo") with pytest.raises(TypeError, match="Can not create a mention to a private group chat"): - chat = Chat(id=1, type="foo") chat.mention_markdown() expected = "[{}](tg://user?id={})" @@ -1223,10 +1222,10 @@ class TestChatWithoutRequest(TestChatBase): "the_name*\u2022", chat.id ) assert chat.mention_markdown() == expected.format(chat.full_name, chat.id) + chat = Chat(id=1, type=Chat.PRIVATE, last_name="last\u2022name") with pytest.raises( TypeError, match="Can not create a mention to a private chat without first name" ): - chat = Chat(id=1, type=Chat.PRIVATE, last_name="last\u2022name") chat.mention_markdown() expected = "[{}](https://t.me/{})" @@ -1235,15 +1234,15 @@ class TestChatWithoutRequest(TestChatBase): "the_name*\u2022", chat.username ) assert chat.mention_markdown() == expected.format(chat.title, chat.username) + chat = Chat(id=1, type="foo", username="user\u2022name") with pytest.raises( TypeError, match="Can not create a mention to a public chat without title" ): - chat = Chat(id=1, type="foo", username="user\u2022name") chat.mention_markdown() def test_mention_markdown_v2(self): + chat = Chat(id=1, type="foo") with pytest.raises(TypeError, match="Can not create a mention to a private group chat"): - chat = Chat(id=1, type="foo") chat.mention_markdown_v2() expected = "[{}](tg://user?id={})" @@ -1254,10 +1253,10 @@ class TestChatWithoutRequest(TestChatBase): assert chat.mention_markdown_v2() == expected.format( escape_markdown(chat.full_name, version=2), chat.id ) + chat = Chat(id=1, type=Chat.PRIVATE, last_name="last_name") with pytest.raises( TypeError, match="Can not create a mention to a private chat without first name" ): - chat = Chat(id=1, type=Chat.PRIVATE, last_name="last_name") chat.mention_markdown_v2() expected = "[{}](https://t.me/{})" @@ -1268,8 +1267,8 @@ class TestChatWithoutRequest(TestChatBase): assert chat.mention_markdown_v2() == expected.format( escape_markdown(chat.title, version=2), chat.username ) + chat = Chat(id=1, type="foo", username="user\u2022name") with pytest.raises( TypeError, match="Can not create a mention to a public chat without title" ): - chat = Chat(id=1, type="foo", username="user\u2022name") chat.mention_markdown_v2() diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py index 405a998c0..0455a8e23 100644 --- a/tests/test_chatmember.py +++ b/tests/test_chatmember.py @@ -165,13 +165,13 @@ def iter_args(instance: ChatMember, de_json_inst: ChatMember, include_optional: inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name) if isinstance(json_at, datetime.datetime): # Convert datetime to int json_at = to_timestamp(json_at) - if param.default is not inspect.Parameter.empty and include_optional: - yield inst_at, json_at - elif param.default is inspect.Parameter.empty: + if ( + param.default is not inspect.Parameter.empty and include_optional + ) or param.default is inspect.Parameter.empty: yield inst_at, json_at -@pytest.fixture +@pytest.fixture() def chat_member_type(request): return request.param() diff --git a/tests/test_constants.py b/tests/test_constants.py index 69949a700..681de2b97 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -93,7 +93,7 @@ class TestConstantsWithoutRequest: assert StrEnumTest.FOO == "foo" assert StrEnumTest.FOO != StrEnumTest.BAR assert StrEnumTest.FOO != "bar" - assert StrEnumTest.FOO != object() + assert object() != StrEnumTest.FOO assert hash(StrEnumTest.FOO) == hash("foo") @@ -105,14 +105,15 @@ class TestConstantsWithoutRequest: assert IntEnumTest.FOO == 1 assert IntEnumTest.FOO != IntEnumTest.BAR assert IntEnumTest.FOO != 2 - assert IntEnumTest.FOO != object() + assert object() != IntEnumTest.FOO assert hash(IntEnumTest.FOO) == hash(1) def test_bot_api_version_and_info(self): - assert constants.BOT_API_VERSION == str(constants.BOT_API_VERSION_INFO) - assert constants.BOT_API_VERSION_INFO == tuple( - int(x) for x in constants.BOT_API_VERSION.split(".") + assert str(constants.BOT_API_VERSION_INFO) == constants.BOT_API_VERSION + assert ( + tuple(int(x) for x in constants.BOT_API_VERSION.split(".")) + == constants.BOT_API_VERSION_INFO ) def test_bot_api_version_info(self): @@ -139,7 +140,8 @@ class TestConstantsWithRequest: ) good_msg, bad_msg = await tasks assert good_msg.text == good_text - assert isinstance(bad_msg, BadRequest) and "Message is too long" in str(bad_msg) + assert isinstance(bad_msg, BadRequest) + assert "Message is too long" in str(bad_msg) async def test_max_caption_length(self, bot, chat_id): good_caption = "a" * constants.MessageLimit.CAPTION_LENGTH @@ -151,4 +153,5 @@ class TestConstantsWithRequest: ) good_msg, bad_msg = await tasks assert good_msg.caption == good_caption - assert isinstance(bad_msg, BadRequest) and "Message caption is too long" in str(bad_msg) + assert isinstance(bad_msg, BadRequest) + assert "Message caption is too long" in str(bad_msg) diff --git a/tests/test_error.py b/tests/test_error.py index 08fbb468b..757b17041 100644 --- a/tests/test_error.py +++ b/tests/test_error.py @@ -87,12 +87,9 @@ class TestErrors: raise TimedOut def test_chat_migrated(self): - with pytest.raises(ChatMigrated, match="Group migrated to supergroup. New chat id: 1234"): + with pytest.raises(ChatMigrated, match="New chat id: 1234") as e: raise ChatMigrated(1234) - try: - raise ChatMigrated(1234) - except ChatMigrated as e: - assert e.new_chat_id == 1234 + assert e.value.new_chat_id == 1234 def test_retry_after(self): with pytest.raises(RetryAfter, match="Flood control exceeded. Retry in 12 seconds"): @@ -103,7 +100,7 @@ class TestErrors: raise Conflict("Something something.") @pytest.mark.parametrize( - "exception, attributes", + ("exception", "attributes"), [ (TelegramError("test message"), ["message"]), (Forbidden("test message"), ["message"]), diff --git a/tests/test_forum.py b/tests/test_forum.py index ec355d79f..c8e3dec89 100644 --- a/tests/test_forum.py +++ b/tests/test_forum.py @@ -56,7 +56,7 @@ async def forum_topic_object(forum_group_id, emoji_id): ) -@pytest.fixture(scope="function") +@pytest.fixture() async def real_topic(bot, emoji_id, forum_group_id): result = await bot.create_forum_topic( chat_id=forum_group_id, @@ -247,7 +247,9 @@ class TestForumMethodsWithRequest: msg = await coro pin_msg_tasks.add(asyncio.create_task(msg.pin())) - assert all([await task for task in pin_msg_tasks]) is True, "Message(s) were not pinned" + assert ( + all([await task for task in pin_msg_tasks]) is True # noqa: PIE802 + ), "Message(s) were not pinned" # We need 2 or more pinned msgs for this to work, else we get Chat_not_modified error result = await bot.unpin_all_forum_topic_messages(forum_group_id, message_thread_id) @@ -420,7 +422,7 @@ class TestForumTopicEdited: # empty string json_dict = {"icon_custom_emoji_id": ""} action = ForumTopicEdited.de_json(json_dict, bot) - assert action.icon_custom_emoji_id == "" + assert not action.icon_custom_emoji_id def test_to_dict(self, topic_edited, emoji_id): action_dict = topic_edited.to_dict() diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d40bc93cb..8e8da7f36 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -26,7 +26,7 @@ from telegram.constants import MessageType class TestHelpers: @pytest.mark.parametrize( - "test_str,expected", + ("test_str", "expected"), [ ("*bold*", r"\*bold\*"), ("_italic_", r"\_italic\_"), @@ -39,7 +39,7 @@ class TestHelpers: assert expected == helpers.escape_markdown(test_str) @pytest.mark.parametrize( - "test_str, expected", + ("test_str", "expected"), [ (r"a_b*c[d]e", r"a\_b\*c\[d\]e"), (r"(fg) ", r"\(fg\) "), @@ -52,7 +52,7 @@ class TestHelpers: assert expected == helpers.escape_markdown(test_str, version=2) @pytest.mark.parametrize( - "test_str, expected", + ("test_str", "expected"), [ (r"mono/pre:", r"mono/pre:"), ("`abc`", r"\`abc\`"), @@ -101,27 +101,27 @@ class TestHelpers: payload = None assert expected == helpers.create_deep_linked_url(username, payload) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Only the following characters"): helpers.create_deep_linked_url(username, "text with spaces") - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="must not exceed 64"): helpers.create_deep_linked_url(username, "0" * 65) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="valid bot_username"): helpers.create_deep_linked_url(None, None) - with pytest.raises(ValueError): # too short username (4 is minimum) + with pytest.raises(ValueError, match="valid bot_username"): # too short username, 4 is min helpers.create_deep_linked_url("abc", None) @pytest.mark.parametrize("message_type", list(MessageType)) @pytest.mark.parametrize("entity_type", [Update, Message]) def test_effective_message_type(self, message_type, entity_type): def build_test_message(kwargs): - config = dict( - message_id=1, - from_user=None, - date=None, - chat=None, - ) + config = { + "message_id": 1, + "from_user": None, + "date": None, + "chat": None, + } config.update(**kwargs) return Message(**config) @@ -133,7 +133,7 @@ class TestHelpers: assert helpers.effective_message_type(empty_update) is None def test_effective_message_type_wrong_type(self): - entity = dict() + entity = {} with pytest.raises( TypeError, match=re.escape(f"neither Message nor Update (got: {type(entity)})") ): @@ -145,7 +145,7 @@ class TestHelpers: assert expected == helpers.mention_html(1, "the name") @pytest.mark.parametrize( - "test_str, expected", + ("test_str", "expected"), [ ("the name", "[the name](tg://user?id=1)"), ("under_score", "[under_score](tg://user?id=1)"), diff --git a/tests/test_menubutton.py b/tests/test_menubutton.py index 25b840cb2..418de635f 100644 --- a/tests/test_menubutton.py +++ b/tests/test_menubutton.py @@ -81,11 +81,11 @@ def scope_class_and_type(request): def menu_button(scope_class_and_type): # We use de_json here so that we don't have to worry about which class gets which arguments return scope_class_and_type[0].de_json( - dict( - type=scope_class_and_type[1], - text=TestMenuButtonselfBase.text, - web_app=TestMenuButtonselfBase.web_app.to_dict(), - ), + { + "type": scope_class_and_type[1], + "text": TestMenuButtonselfBase.text, + "web_app": TestMenuButtonselfBase.web_app.to_dict(), + }, bot=None, ) diff --git a/tests/test_message.py b/tests/test_message.py index 0242142e1..741aeb291 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -83,7 +83,6 @@ def message(bot): @pytest.fixture( - scope="function", params=[ {"forward_from": User(99, "forward_user", False), "forward_date": datetime.utcnow()}, { @@ -524,19 +523,19 @@ class TestMessageWithoutRequest(TestMessageBase): MessageEntity(MessageEntity.BOLD, offset=0, length=4), MessageEntity(MessageEntity.ITALIC, offset=0, length=4), ] - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Nested entities are not supported for"): assert message.text_markdown message.entities = [MessageEntity(MessageEntity.UNDERLINE, offset=0, length=4)] - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Underline entities are not supported for"): message.text_markdown message.entities = [MessageEntity(MessageEntity.STRIKETHROUGH, offset=0, length=4)] - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Strikethrough entities are not supported for"): message.text_markdown message.entities = [MessageEntity(MessageEntity.SPOILER, offset=0, length=4)] - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Spoiler entities are not supported for"): message.text_markdown message.entities = [] @@ -783,7 +782,7 @@ class TestMessageWithoutRequest(TestMessageBase): assert message.link == f"https://t.me/{message.chat.username}/{message.message_id}" @pytest.mark.parametrize( - "type_, id_", argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)] + ("type_", "id_"), argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)] ) def test_link_with_id(self, message, type_, id_): message.chat.username = None @@ -792,7 +791,7 @@ class TestMessageWithoutRequest(TestMessageBase): # The leading - for group ids/ -100 for supergroup ids isn't supposed to be in the link assert message.link == f"https://t.me/c/{3}/{message.message_id}" - @pytest.mark.parametrize("id_, username", argvalues=[(None, "username"), (-3, None)]) + @pytest.mark.parametrize(("id_", "username"), argvalues=[(None, "username"), (-3, None)]) def test_link_private_chats(self, message, id_, username): message.chat.type = Chat.PRIVATE message.chat.id = id_ @@ -1324,7 +1323,7 @@ class TestMessageWithoutRequest(TestMessageBase): quote=True, ) - @pytest.mark.parametrize("disable_notification,protected", [(False, True), (True, False)]) + @pytest.mark.parametrize(("disable_notification", "protected"), [(False, True), (True, False)]) async def test_forward(self, monkeypatch, message, disable_notification, protected): async def make_assertion(*_, **kwargs): chat_id = kwargs["chat_id"] == 123456 @@ -1346,7 +1345,7 @@ class TestMessageWithoutRequest(TestMessageBase): ) assert not await message.forward(635241) - @pytest.mark.parametrize("disable_notification,protected", [(True, False), (False, True)]) + @pytest.mark.parametrize(("disable_notification", "protected"), [(True, False), (False, True)]) async def test_copy(self, monkeypatch, message, disable_notification, protected): keyboard = [[1, 2]] @@ -1387,7 +1386,7 @@ class TestMessageWithoutRequest(TestMessageBase): ) assert not await message.copy(635241) - @pytest.mark.parametrize("disable_notification,protected", [(True, False), (False, True)]) + @pytest.mark.parametrize(("disable_notification", "protected"), [(True, False), (False, True)]) async def test_reply_copy(self, monkeypatch, message, disable_notification, protected): keyboard = [[1, 2]] diff --git a/tests/test_meta.py b/tests/test_meta.py index 29ea36c06..683c6c1e3 100644 --- a/tests/test_meta.py +++ b/tests/test_meta.py @@ -23,13 +23,13 @@ import pytest from tests.auxil.envvars import env_var_2_bool skip_disabled = pytest.mark.skipif( - not env_var_2_bool(os.getenv("TEST_BUILD", False)), reason="TEST_BUILD not enabled" + not env_var_2_bool(os.getenv("TEST_BUILD", "")), reason="TEST_BUILD not enabled" ) # To make the tests agnostic of the cwd @pytest.fixture(autouse=True) -def change_test_dir(request, monkeypatch): +def _change_test_dir(request, monkeypatch): monkeypatch.chdir(request.config.rootdir) diff --git a/tests/test_official.py b/tests/test_official.py index babc783ea..3e829f11b 100644 --- a/tests/test_official.py +++ b/tests/test_official.py @@ -167,9 +167,10 @@ IGNORED_PARAM_REQUIREMENTS.update(BACKWARDS_COMPAT_KWARGS) def find_next_sibling_until(tag, name, until): for sibling in tag.next_siblings: if sibling is until: - return + return None if sibling.name == name: return sibling + return None def parse_table(h4) -> List[List[str]]: @@ -280,10 +281,7 @@ def check_object(h4): def is_parameter_required_by_tg(field: str) -> bool: if field in {"Required", "Yes"}: return True - if field.split(".", 1)[0] == "Optional": # splits the sentence and extracts first word - return False - else: - return True + return field.split(".", 1)[0] != "Optional" # splits the sentence and extracts first word def check_required_param( @@ -304,7 +302,7 @@ def check_required_param( def check_defaults_type(ptb_param: inspect.Parameter) -> bool: - return True if DefaultValue.get_value(ptb_param.default) is None else False + return DefaultValue.get_value(ptb_param.default) is None to_run = env_var_2_bool(os.getenv("TEST_OFFICIAL")) diff --git a/tests/test_pollhandler.py b/tests/test_pollhandler.py index e2e28a8e2..c83a0d418 100644 --- a/tests/test_pollhandler.py +++ b/tests/test_pollhandler.py @@ -68,7 +68,7 @@ def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope="function") +@pytest.fixture() def poll(bot): return Update( 0, @@ -95,7 +95,7 @@ class TestPollHandler: assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" @pytest.fixture(autouse=True) - def reset(self): + def _reset(self): self.test_flag = False async def callback(self, update, context): diff --git a/tests/test_replykeyboardmarkup.py b/tests/test_replykeyboardmarkup.py index a202ed338..b0f329b98 100644 --- a/tests/test_replykeyboardmarkup.py +++ b/tests/test_replykeyboardmarkup.py @@ -107,15 +107,15 @@ class TestReplyKeyboardMarkupWithoutRequest(TestReplyKeyboardMarkupBase): assert hash(a) != hash(f) def test_wrong_keyboard_inputs(self): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): ReplyKeyboardMarkup([["button1"], 1]) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): ReplyKeyboardMarkup("strings_are_not_allowed") - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): ReplyKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"]) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): ReplyKeyboardMarkup(KeyboardButton("button1")) - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="should be a sequence of sequences"): ReplyKeyboardMarkup([[["button1"]]]) def test_from_button(self): diff --git a/tests/test_slots.py b/tests/test_slots.py index baa054c85..fdd8619bc 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -54,5 +54,4 @@ def test_class_has_slots_and_no_dict(): def get_slots(_class): - slots = [attr for cls in _class.__mro__ if hasattr(cls, "__slots__") for attr in cls.__slots__] - return slots + return [attr for cls in _class.__mro__ if hasattr(cls, "__slots__") for attr in cls.__slots__] diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 3e5eee8eb..50ec6c240 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -322,7 +322,8 @@ class TestTelegramObject: # * the (now) added attribute `is_forum` does not affect the unpickling pp = PicklePersistence(data_file("20a5_modified_chat.pickle")) chat = (await pp.get_chat_data())[1] - assert chat.id == 1 and chat.type == Chat.PRIVATE + assert chat.id == 1 + assert chat.type == Chat.PRIVATE assert chat.api_kwargs == { "all_members_are_administrators": True, "something": "Manually inserted", @@ -356,13 +357,19 @@ class TestTelegramObject: assert new_msg is not msg # The same bot should be present when deepcopying. - assert new_msg.get_bot() == bot and new_msg.get_bot() is bot + assert new_msg.get_bot() == bot + assert new_msg.get_bot() is bot - assert new_msg.date == date and new_msg.date is not date - assert new_msg.chat == chat and new_msg.chat is not chat - assert new_msg.from_user == user and new_msg.from_user is not user - assert new_msg.photo[0] == photo and new_msg.photo[0] is not photo - assert new_msg.api_kwargs == {"foo": "bar"} and new_msg.api_kwargs is not msg.api_kwargs + assert new_msg.date == date + assert new_msg.date is not date + assert new_msg.chat == chat + assert new_msg.chat is not chat + assert new_msg.from_user == user + assert new_msg.from_user is not user + assert new_msg.photo[0] == photo + assert new_msg.photo[0] is not photo + assert new_msg.api_kwargs == {"foo": "bar"} + assert new_msg.api_kwargs is not msg.api_kwargs # check that deepcopy preserves the freezing status with pytest.raises( @@ -380,7 +387,8 @@ class TestTelegramObject: d = deepcopy(s) assert d is not s assert d._private == s._private # Can't test for identity since two equal strings is True - assert d._bot == s._bot and d._bot is s._bot + assert d._bot == s._bot + assert d._bot is s._bot assert d.normal == s.normal def test_string_representation(self): diff --git a/tests/test_update.py b/tests/test_update.py index 5ae8418a9..3e5ed3b6f 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -95,7 +95,7 @@ all_types = ( "chat_join_request", ) -ids = all_types + ("callback_query_without_message",) +ids = (*all_types, "callback_query_without_message") @pytest.fixture(scope="module", params=params, ids=ids) diff --git a/tests/test_user.py b/tests/test_user.py index d345ff5ab..e21b5443d 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -45,7 +45,7 @@ def json_dict(): } -@pytest.fixture(scope="function") +@pytest.fixture() def user(bot): user = User( id=TestUserBase.id_, diff --git a/tests/test_version.py b/tests/test_version.py index c6c52f3d1..f521bdf38 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -37,7 +37,7 @@ class TestVersion: assert __version__ == str(__version_info__) @pytest.mark.parametrize( - "version,expected", + ("version", "expected"), [ (Version(1, 2, 3, "alpha", 4), "1.2.3a4"), (Version(2, 3, 4, "beta", 5), "2.3.4b5"), @@ -52,7 +52,7 @@ class TestVersion: def test_version_str(self, version, expected): assert str(version) == expected - @pytest.mark.parametrize("use_tuple", (True, False)) + @pytest.mark.parametrize("use_tuple", [True, False]) def test_version_info(self, use_tuple): version = Version(1, 2, 3, "beta", 4) assert isinstance(version, tuple) diff --git a/tests/test_videochat.py b/tests/test_videochat.py index ca7676dc8..046159bf4 100644 --- a/tests/test_videochat.py +++ b/tests/test_videochat.py @@ -116,7 +116,7 @@ class TestVideoChatParticipantsInvitedWithoutRequest: assert video_chat_participants.users[0].id == user1.id assert video_chat_participants.users[1].id == user2.id - @pytest.mark.parametrize("use_users", (True, False)) + @pytest.mark.parametrize("use_users", [True, False]) def test_to_dict(self, user1, user2, use_users): video_chat_participants = VideoChatParticipantsInvited([user1, user2] if use_users else ()) video_chat_dict = video_chat_participants.to_dict()