Empower ruff (#3594)

This commit is contained in:
Harshil 2023-03-25 23:48:04 +05:30 committed by GitHub
parent 4aedb33d37
commit 9997a9f47e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
111 changed files with 776 additions and 896 deletions

View file

@ -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

View file

@ -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:

View file

@ -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.

View file

@ -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>"""
query = update.inline_query.query
if query == "":
if not query: # empty query should not be handled
return
results = [

View file

@ -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

View file

@ -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

View file

@ -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"]

View file

@ -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 <examples.rawapibot>`
.. seealso:: :wiki:`Your First Bot <Extensions--Your-first-Bot>`,
.. seealso:: :wiki:`Your First Bot <Extensions---Your-first-Bot>`,
:wiki:`Builder Pattern <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,

View file

@ -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 <from_user>` coincides with the
``chat_id`` of the user. Please use :attr:`user_chat_id` to contact the user in

View file

@ -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`"""

View file

@ -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'<a href="{entity.url}">{text}</a>'
insert = f'<a href="{entity.url}">{escaped_text}</a>'
elif entity.type == MessageEntity.TEXT_MENTION and entity.user:
insert = f'<a href="tg://user?id={entity.user.id}">{text}</a>'
insert = f'<a href="tg://user?id={entity.user.id}">{escaped_text}</a>'
elif entity.type == MessageEntity.URL and urled:
insert = f'<a href="{text}">{text}</a>'
insert = f'<a href="{escaped_text}">{escaped_text}</a>'
elif entity.type == MessageEntity.BOLD:
insert = f"<b>{text}</b>"
insert = f"<b>{escaped_text}</b>"
elif entity.type == MessageEntity.ITALIC:
insert = f"<i>{text}</i>"
insert = f"<i>{escaped_text}</i>"
elif entity.type == MessageEntity.CODE:
insert = f"<code>{text}</code>"
insert = f"<code>{escaped_text}</code>"
elif entity.type == MessageEntity.PRE:
if entity.language:
insert = f'<pre><code class="{entity.language}">{text}</code></pre>'
insert = (
f'<pre><code class="{entity.language}">{escaped_text}</code></pre>'
)
else:
insert = f"<pre>{text}</pre>"
insert = f"<pre>{escaped_text}</pre>"
elif entity.type == MessageEntity.UNDERLINE:
insert = f"<u>{text}</u>"
insert = f"<u>{escaped_text}</u>"
elif entity.type == MessageEntity.STRIKETHROUGH:
insert = f"<s>{text}</s>"
insert = f"<s>{escaped_text}</s>"
elif entity.type == MessageEntity.SPOILER:
insert = f'<span class="tg-spoiler">{text}</span>'
insert = f'<span class="tg-spoiler">{escaped_text}</span>'
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

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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 <Extensions--Your-first-Bot>`
.. seealso:: :wiki:`Your First Bot <Extensions---Your-first-Bot>`
Args:
update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a

View file

@ -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):

View file

@ -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]

View file

@ -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 <examples.echobot>`
.. seealso:: :wiki:`Your First Bot <Extensions--Your-first-Bot>`,
.. seealso:: :wiki:`Your First Bot <Extensions---Your-first-Bot>`,
:wiki:`Architecture Overview <Architecture>`
.. 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.

View file

@ -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 <Extensions--Your-first-Bot>`,
.. seealso:: :wiki:`Your First Bot <Extensions---Your-first-Bot>`,
:wiki:`Builder Pattern <Builder-Pattern>`
.. _`builder pattern`: https://en.wikipedia.org/wiki/Builder_pattern

View file

@ -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 = (

View file

@ -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

View file

@ -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

View file

@ -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]

View file

@ -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,

View file

@ -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 <Extensions--Advanced-Filters>`
.. seealso:: :wiki:`Advanced Filters <Extensions---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::

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -244,7 +244,7 @@ class MessageFilter(BaseFilter):
Please see :class:`BaseFilter` for details on how to create custom filters.
.. seealso:: :wiki:`Advanced Filters <Extensions--Advanced-Filters>`
.. seealso:: :wiki:`Advanced Filters <Extensions---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

View file

@ -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}"

View file

@ -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

View file

@ -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,

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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),

View file

@ -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),

View file

@ -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):

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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("<b>", "").replace("</b>", "")
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):

View file

@ -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

View file

@ -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),

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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:

View file

@ -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()

View file

@ -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

View file

@ -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),

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"))

View file

@ -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]")

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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")

View file

@ -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

View file

@ -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)]

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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):

View file

@ -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(

View file

@ -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

View file

@ -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"

View file

@ -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()

View file

@ -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):

View file

@ -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

View file

@ -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):

View file

@ -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):

View file

@ -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 = []

View file

@ -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):

View file

@ -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):

View file

@ -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<begin>.*)est(?P<end>.*)"
if compile:

View file

@ -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):

View file

@ -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
):

View file

@ -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"),

View file

@ -47,7 +47,7 @@ class TestRequestParameterWithoutRequest:
assert request_parameter.input_files is None
@pytest.mark.parametrize(
"value, expected",
("value", "expected"),
[
(1, "1"),
("one", "one"),

View file

@ -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),

View file

@ -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
)

View file

@ -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,

View file

@ -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 = '<a href="tg://user?id={}">{}</a>'
@ -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 = '<a href="https://t.me/{}">{}</a>'
@ -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()

View file

@ -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()

View file

@ -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)

View file

@ -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"]),

View file

@ -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()

View file

@ -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)"),

View file

@ -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,
)

Some files were not shown because too many files have changed in this diff Show more