From 3c0fd677fa216a16aee24ae24dc055c114a28773 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 8 Sep 2022 22:40:11 +0200 Subject: [PATCH 01/90] Get a very rough start on TelegramObject(api_kwargs) --- telegram/_botcommand.py | 5 +- telegram/_botcommandscope.py | 20 ++++---- telegram/_callbackquery.py | 5 +- telegram/_chat.py | 3 +- telegram/_chatadministratorrights.py | 4 +- telegram/_chatinvitelink.py | 4 +- telegram/_chatjoinrequest.py | 5 +- telegram/_chatlocation.py | 2 +- telegram/_chatmember.py | 32 +++++++++--- telegram/_chatmemberupdated.py | 2 +- telegram/_chatpermissions.py | 4 +- telegram/_choseninlineresult.py | 6 ++- telegram/_dice.py | 4 +- telegram/_files/_basemedium.py | 2 +- telegram/_files/_basethumbedmedium.py | 9 ++-- telegram/_files/animation.py | 5 +- telegram/_files/audio.py | 5 +- telegram/_files/chatphoto.py | 3 +- telegram/_files/contact.py | 4 +- telegram/_files/document.py | 9 ++-- telegram/_files/file.py | 5 +- telegram/_files/location.py | 4 +- telegram/_files/photosize.py | 7 ++- telegram/_files/sticker.py | 18 ++++--- telegram/_files/venue.py | 2 +- telegram/_files/video.py | 7 ++- telegram/_files/videonote.py | 9 ++-- telegram/_files/voice.py | 7 ++- telegram/_forcereply.py | 4 +- telegram/_games/game.py | 4 +- telegram/_inline/inlinekeyboardbutton.py | 4 +- telegram/_inline/inlinekeyboardmarkup.py | 6 ++- telegram/_inline/inlinequery.py | 3 +- telegram/_inline/inlinequeryresult.py | 8 ++- telegram/_inline/inlinequeryresultarticle.py | 2 +- telegram/_inline/inlinequeryresultaudio.py | 4 +- .../_inline/inlinequeryresultcachedaudio.py | 6 +-- .../inlinequeryresultcacheddocument.py | 4 +- .../_inline/inlinequeryresultcachedgif.py | 2 +- .../inlinequeryresultcachedmpeg4gif.py | 4 +- .../_inline/inlinequeryresultcachedphoto.py | 6 +-- .../_inline/inlinequeryresultcachedsticker.py | 4 +- .../_inline/inlinequeryresultcachedvideo.py | 4 +- .../_inline/inlinequeryresultcachedvoice.py | 4 +- telegram/_inline/inlinequeryresultcontact.py | 2 +- telegram/_inline/inlinequeryresultdocument.py | 4 +- telegram/_inline/inlinequeryresultgame.py | 4 +- telegram/_inline/inlinequeryresultgif.py | 2 +- telegram/_inline/inlinequeryresultlocation.py | 4 +- telegram/_inline/inlinequeryresultmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultphoto.py | 4 +- telegram/_inline/inlinequeryresultvenue.py | 4 +- telegram/_inline/inlinequeryresultvideo.py | 4 +- telegram/_inline/inlinequeryresultvoice.py | 4 +- .../_inline/inputcontactmessagecontent.py | 6 ++- .../_inline/inputinvoicemessagecontent.py | 4 +- .../_inline/inputlocationmessagecontent.py | 4 +- telegram/_inline/inputtextmessagecontent.py | 2 +- telegram/_inline/inputvenuemessagecontent.py | 6 ++- telegram/_keyboardbutton.py | 2 +- telegram/_keyboardbuttonpolltype.py | 6 ++- telegram/_loginurl.py | 4 +- telegram/_menubutton.py | 12 +++-- telegram/_message.py | 8 +-- telegram/_messageautodeletetimerchanged.py | 4 +- telegram/_messageentity.py | 4 +- telegram/_messageid.py | 4 +- telegram/_passport/credentials.py | 30 +++++++---- telegram/_passport/data.py | 13 +++-- .../_passport/encryptedpassportelement.py | 9 ++-- telegram/_passport/passportdata.py | 7 ++- telegram/_passport/passportelementerrors.py | 43 ++++++++++++---- telegram/_passport/passportfile.py | 9 ++-- telegram/_payment/invoice.py | 4 +- telegram/_payment/labeledprice.py | 2 +- telegram/_payment/orderinfo.py | 2 +- telegram/_payment/precheckoutquery.py | 7 ++- telegram/_payment/shippingaddress.py | 4 +- telegram/_payment/shippingoption.py | 6 ++- telegram/_payment/shippingquery.py | 5 +- telegram/_payment/successfulpayment.py | 4 +- telegram/_poll.py | 10 ++-- telegram/_proximityalerttriggered.py | 6 ++- telegram/_replykeyboardmarkup.py | 4 +- telegram/_replykeyboardremove.py | 4 +- telegram/_sentwebappmessage.py | 4 +- telegram/_telegramobject.py | 50 +++++++++++-------- telegram/_update.py | 2 +- telegram/_user.py | 6 +-- telegram/_userprofilephotos.py | 6 ++- telegram/_videochat.py | 23 +++++++-- telegram/_webappdata.py | 4 +- telegram/_webappinfo.py | 4 +- telegram/_webhookinfo.py | 4 +- 94 files changed, 367 insertions(+), 266 deletions(-) diff --git a/telegram/_botcommand.py b/telegram/_botcommand.py index 29ded84d776..11f04bf7fc1 100644 --- a/telegram/_botcommand.py +++ b/telegram/_botcommand.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram Bot Command.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -42,7 +42,8 @@ class BotCommand(TelegramObject): __slots__ = ("description", "command") - def __init__(self, command: str, description: str, **_kwargs: Any): + def __init__(self, command: str, description: str, api_kwargs: Dict[str, object] = None): + super().__init__(api_kwargs=api_kwargs) self.command = command self.description = description diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 7c341e08df3..6a0f24cab24 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. # pylint: disable=redefined-builtin """This module contains objects representing Telegram bot command scopes.""" -from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Type, Union +from typing import TYPE_CHECKING, ClassVar, Dict, Optional, Type, Union from telegram import constants from telegram._telegramobject import TelegramObject @@ -75,7 +75,7 @@ class BotCommandScope(TelegramObject): CHAT_MEMBER: ClassVar[str] = constants.BotCommandScopeType.CHAT_MEMBER """:const:`telegram.constants.BotCommandScopeType.CHAT_MEMBER`""" - def __init__(self, type: str, **_kwargs: Any): + def __init__(self, type: str, api_kwargs: Dict[str, object] = None): self.type = type self._id_attrs = (self.type,) @@ -126,7 +126,7 @@ class BotCommandScopeDefault(BotCommandScope): __slots__ = () - def __init__(self, **_kwargs: Any): + def __init__(self, api_kwargs: Dict[str, object] = None): super().__init__(type=BotCommandScope.DEFAULT) @@ -141,7 +141,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): __slots__ = () - def __init__(self, **_kwargs: Any): + def __init__(self, api_kwargs: Dict[str, object] = None): super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS) @@ -156,7 +156,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope): __slots__ = () - def __init__(self, **_kwargs: Any): + def __init__(self, api_kwargs: Dict[str, object] = None): super().__init__(type=BotCommandScope.ALL_GROUP_CHATS) @@ -171,7 +171,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): __slots__ = () - def __init__(self, **_kwargs: Any): + def __init__(self, api_kwargs: Dict[str, object] = None): super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS) @@ -195,7 +195,7 @@ class BotCommandScopeChat(BotCommandScope): __slots__ = ("chat_id",) - def __init__(self, chat_id: Union[str, int], **_kwargs: Any): + def __init__(self, chat_id: Union[str, int], api_kwargs: Dict[str, object] = None): super().__init__(type=BotCommandScope.CHAT) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) @@ -224,7 +224,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): __slots__ = ("chat_id",) - def __init__(self, chat_id: Union[str, int], **_kwargs: Any): + def __init__(self, chat_id: Union[str, int], api_kwargs: Dict[str, object] = None): super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) @@ -255,7 +255,9 @@ class BotCommandScopeChatMember(BotCommandScope): __slots__ = ("chat_id", "user_id") - def __init__(self, chat_id: Union[str, int], user_id: int, **_kwargs: Any): + def __init__( + self, chat_id: Union[str, int], user_id: int, api_kwargs: Dict[str, object] = None + ): super().__init__(type=BotCommandScope.CHAT_MEMBER) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 111cc6f7e25..f611cfd3d03 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. # pylint: disable=redefined-builtin """This module contains an object that represents a Telegram CallbackQuery""" -from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional, Tuple, Union from telegram import constants from telegram._files.location import Location @@ -119,8 +119,7 @@ def __init__( data: str = None, inline_message_id: str = None, game_short_name: str = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.id = id # pylint: disable=invalid-name diff --git a/telegram/_chat.py b/telegram/_chat.py index 2d875106104..b94a30353a8 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -251,7 +251,6 @@ def __init__( username: str = None, first_name: str = None, last_name: str = None, - bot: "Bot" = None, photo: ChatPhoto = None, description: str = None, invite_link: str = None, @@ -269,7 +268,7 @@ def __init__( join_to_send_messages: bool = None, join_by_request: bool = None, has_restricted_voice_and_video_messages: bool = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.id = id # pylint: disable=invalid-name diff --git a/telegram/_chatadministratorrights.py b/telegram/_chatadministratorrights.py index 25da93de095..310accac5b7 100644 --- a/telegram/_chatadministratorrights.py +++ b/telegram/_chatadministratorrights.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the class which represents a Telegram ChatAdministratorRights.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -119,7 +119,7 @@ def __init__( can_post_messages: bool = None, can_edit_messages: bool = None, can_pin_messages: bool = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ) -> None: # Required self.is_anonymous = is_anonymous diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index 38a2571189c..52baa05db6b 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents an invite link for a chat.""" import datetime -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Dict, Optional from telegram._telegramobject import TelegramObject from telegram._user import User @@ -113,7 +113,7 @@ def __init__( member_limit: int = None, name: str = None, pending_join_request_count: int = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.invite_link = invite_link diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index 87a5bd21cf9..fc1929caecc 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatJoinRequest.""" import datetime -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Dict, Optional from telegram._chat import Chat from telegram._chatinvitelink import ChatInviteLink @@ -74,8 +74,7 @@ def __init__( date: datetime.datetime, bio: str = None, invite_link: ChatInviteLink = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.chat = chat diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index bdb8b328683..f9741c6d103 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -52,7 +52,7 @@ def __init__( self, location: Location, address: str, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.location = location self.address = address diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index 8bbe2bdbd81..e563dd63770 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -83,7 +83,12 @@ class ChatMember(TelegramObject): RESTRICTED: ClassVar[str] = constants.ChatMemberStatus.RESTRICTED """:const:`telegram.constants.ChatMemberStatus.RESTRICTED`""" - def __init__(self, user: User, status: str, **_kwargs: object): + def __init__( + self, + user: User, + status: str, + api_kwargs: Dict[str, object] = None, + ): # Required by all subclasses self.user = user self.status = status @@ -154,7 +159,7 @@ def __init__( user: User, is_anonymous: bool, custom_title: str = None, - **_kwargs: object, + api_kwargs: Dict[str, object] = None, ): super().__init__(status=ChatMember.OWNER, user=user) self.is_anonymous = is_anonymous @@ -276,7 +281,7 @@ def __init__( can_edit_messages: bool = None, can_pin_messages: bool = None, custom_title: str = None, - **_kwargs: object, + api_kwargs: Dict[str, object] = None, ): super().__init__(status=ChatMember.ADMINISTRATOR, user=user) self.can_be_edited = can_be_edited @@ -313,7 +318,11 @@ class ChatMemberMember(ChatMember): __slots__ = () - def __init__(self, user: User, **_kwargs: object): + def __init__( + self, + user: User, + api_kwargs: Dict[str, object] = None, + ): super().__init__(status=ChatMember.MEMBER, user=user) @@ -400,7 +409,7 @@ def __init__( can_send_other_messages: bool, can_add_web_page_previews: bool, until_date: datetime.datetime, - **_kwargs: object, + api_kwargs: Dict[str, object] = None, ): super().__init__(status=ChatMember.RESTRICTED, user=user) self.is_member = is_member @@ -433,7 +442,11 @@ class ChatMemberLeft(ChatMember): __slots__ = () - def __init__(self, user: User, **_kwargs: object): + def __init__( + self, + user: User, + api_kwargs: Dict[str, object] = None, + ): super().__init__(status=ChatMember.LEFT, user=user) @@ -460,6 +473,11 @@ class ChatMemberBanned(ChatMember): __slots__ = ("until_date",) - def __init__(self, user: User, until_date: datetime.datetime, **_kwargs: object): + def __init__( + self, + user: User, + until_date: datetime.datetime, + api_kwargs: Dict[str, object] = None, + ): super().__init__(status=ChatMember.BANNED, user=user) self.until_date = until_date diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index 26de5b00ed3..b422a4e6dea 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -83,7 +83,7 @@ def __init__( old_chat_member: ChatMember, new_chat_member: ChatMember, invite_link: ChatInviteLink = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.chat = chat diff --git a/telegram/_chatpermissions.py b/telegram/_chatpermissions.py index e12d577369d..35de9e01e92 100644 --- a/telegram/_chatpermissions.py +++ b/telegram/_chatpermissions.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatPermission.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -99,7 +99,7 @@ def __init__( can_change_info: bool = None, can_invite_users: bool = None, can_pin_messages: bool = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.can_send_messages = can_send_messages diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index 2199edac867..f9f2fdfc2f7 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChosenInlineResult.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Dict, Optional from telegram._files.location import Location from telegram._telegramobject import TelegramObject @@ -72,8 +72,10 @@ def __init__( query: str, location: Location = None, inline_message_id: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.result_id = result_id self.from_user = from_user diff --git a/telegram/_dice.py b/telegram/_dice.py index ac7622ec02d..6bc07f59dbf 100644 --- a/telegram/_dice.py +++ b/telegram/_dice.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram Dice.""" -from typing import Any, ClassVar, List +from typing import ClassVar, Dict, List from telegram import constants from telegram._telegramobject import TelegramObject @@ -67,7 +67,7 @@ class Dice(TelegramObject): __slots__ = ("emoji", "value") - def __init__(self, value: int, emoji: str, **_kwargs: Any): + def __init__(self, value: int, emoji: str, api_kwargs: Dict[str, object] = None): self.value = value self.emoji = emoji diff --git a/telegram/_files/_basemedium.py b/telegram/_files/_basemedium.py index e814fe4c9c7..0ae4b1c0aef 100644 --- a/telegram/_files/_basemedium.py +++ b/telegram/_files/_basemedium.py @@ -47,7 +47,7 @@ class _BaseMedium(TelegramObject): is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. file_size (:obj:`int`): Optional. File size. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index 9ae66bb8b8b..219174c4da7 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -54,7 +54,7 @@ class _BaseThumbedMedium(_BaseMedium): Can't be used to download or reuse the file. file_size (:obj:`int`): Optional. File size. thumb (:class:`telegram.PhotoSize`): Optional. Thumbnail as defined by sender. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -66,10 +66,13 @@ def __init__( file_unique_id: str, file_size: int = None, thumb: PhotoSize = None, - bot: "Bot" = None, + api_kwargs: Dict[str, object] = None, ): super().__init__( - file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, bot=bot + file_id=file_id, + file_unique_id=file_unique_id, + file_size=file_size, + api_kwargs=api_kwargs, ) self.thumb = thumb diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index 5ea5d851d3f..ab4526a5dcd 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -60,7 +60,7 @@ class Animation(_BaseThumbedMedium): file_name (:obj:`str`): Optional. Original animation filename as defined by sender. mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -77,8 +77,7 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index 83ec95e57db..fc2ea8a83e6 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -64,7 +64,7 @@ class Audio(_BaseThumbedMedium): file_size (:obj:`int`): Optional. File size in bytes. thumb (:class:`telegram.PhotoSize`): Optional. Thumbnail of the album cover to which the music file belongs. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -80,9 +80,8 @@ def __init__( mime_type: str = None, file_size: int = None, thumb: PhotoSize = None, - bot: "Bot" = None, file_name: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_files/chatphoto.py b/telegram/_files/chatphoto.py index 215b11af19e..b9afea46e12 100644 --- a/telegram/_files/chatphoto.py +++ b/telegram/_files/chatphoto.py @@ -78,8 +78,7 @@ def __init__( small_file_unique_id: str, big_file_id: str, big_file_unique_id: str, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.small_file_id = small_file_id self.small_file_unique_id = small_file_unique_id diff --git a/telegram/_files/contact.py b/telegram/_files/contact.py index a0ef463ef8a..06044f0a19b 100644 --- a/telegram/_files/contact.py +++ b/telegram/_files/contact.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Contact.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -55,7 +55,7 @@ def __init__( last_name: str = None, user_id: int = None, vcard: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.phone_number = str(phone_number) diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 850e0c90f14..0cfcae79246 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Document.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize @@ -55,7 +55,7 @@ class Document(_BaseThumbedMedium): file_name (:obj:`str`): Original filename. mime_type (:obj:`str`): Optional. MIME type of the file. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -69,15 +69,14 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, thumb=thumb, - bot=bot, + api_kwargs=api_kwargs, ) # Optional self.mime_type = mime_type diff --git a/telegram/_files/file.py b/telegram/_files/file.py index fe5661cfb4e..9b4983568a1 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -21,7 +21,7 @@ import urllib.parse as urllib_parse from base64 import b64decode from pathlib import Path -from typing import IO, TYPE_CHECKING, Any, Optional, Union +from typing import IO, TYPE_CHECKING, Dict, Optional, Union from telegram._passport.credentials import decrypt from telegram._telegramobject import TelegramObject @@ -81,10 +81,9 @@ def __init__( self, file_id: str, file_unique_id: str, - bot: "Bot" = None, file_size: int = None, file_path: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.file_id = str(file_id) diff --git a/telegram/_files/location.py b/telegram/_files/location.py index 8a504325b0a..6e3cdc0430a 100644 --- a/telegram/_files/location.py +++ b/telegram/_files/location.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Location.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -73,7 +73,7 @@ def __init__( live_period: int = None, heading: int = None, proximity_alert_radius: int = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.longitude = longitude diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 51618317b03..2f026e4323b 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram PhotoSize.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._files._basemedium import _BaseMedium @@ -52,7 +52,7 @@ class PhotoSize(_BaseMedium): width (:obj:`int`): Photo width. height (:obj:`int`): Photo height. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -65,8 +65,7 @@ def __init__( width: int, height: int, file_size: int = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, bot=bot diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 45ddea8bb46..944811dfbd8 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects that represent stickers.""" -from typing import TYPE_CHECKING, Any, ClassVar, List, Optional +from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional from telegram import constants from telegram._files._basethumbedmedium import _BaseThumbedMedium @@ -101,7 +101,7 @@ class Sticker(_BaseThumbedMedium): mask_position (:class:`telegram.MaskPosition`): Optional. For mask stickers, the position where the mask should be placed. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + premium_animation (:class:`telegram.File`): Optional. For premium regular stickers, premium animation for the sticker. @@ -139,10 +139,9 @@ def __init__( file_size: int = None, set_name: str = None, mask_position: "MaskPosition" = None, - bot: "Bot" = None, premium_animation: "File" = None, custom_emoji_id: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, @@ -251,7 +250,7 @@ def __init__( is_video: bool, sticker_type: str, thumb: PhotoSize = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.name = name self.title = title @@ -324,7 +323,14 @@ class MaskPosition(TelegramObject): CHIN: ClassVar[str] = constants.MaskPosition.CHIN """:const:`telegram.constants.MaskPosition.CHIN`""" - def __init__(self, point: str, x_shift: float, y_shift: float, scale: float, **_kwargs: Any): + def __init__( + self, + point: str, + x_shift: float, + y_shift: float, + scale: float, + api_kwargs: Dict[str, object] = None, + ): self.point = point self.x_shift = x_shift self.y_shift = y_shift diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index ca30d3cc7c0..0339ccf3e62 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -81,7 +81,7 @@ def __init__( foursquare_type: str = None, google_place_id: str = None, google_place_type: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.location = location diff --git a/telegram/_files/video.py b/telegram/_files/video.py index ecabbade92d..905fe5269b9 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Video.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize @@ -61,7 +61,7 @@ class Video(_BaseThumbedMedium): file_name (:obj:`str`): Optional. Original filename as defined by sender. mime_type (:obj:`str`): Optional. MIME type of a file as defined by sender. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -77,9 +77,8 @@ def __init__( thumb: PhotoSize = None, mime_type: str = None, file_size: int = None, - bot: "Bot" = None, file_name: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index 13a9bbd420c..c50165eb6d6 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram VideoNote.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize @@ -56,7 +56,7 @@ class VideoNote(_BaseThumbedMedium): duration (:obj:`int`): Duration of the video in seconds as defined by sender. thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -70,15 +70,14 @@ def __init__( duration: int, thumb: PhotoSize = None, file_size: int = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, thumb=thumb, - bot=bot, + api_kwargs=api_kwargs, ) # Required self.length = length diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index 98cebcfecc0..8f35367a931 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Voice.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._files._basemedium import _BaseMedium @@ -52,7 +52,7 @@ class Voice(_BaseMedium): duration (:obj:`int`): Duration of the audio in seconds as defined by sender. mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender. file_size (:obj:`int`): Optional. File size in bytes. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -65,8 +65,7 @@ def __init__( duration: int, mime_type: str = None, file_size: int = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_forcereply.py b/telegram/_forcereply.py index baecf2c8346..56dba949909 100644 --- a/telegram/_forcereply.py +++ b/telegram/_forcereply.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ForceReply.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -70,7 +70,7 @@ def __init__( self, selective: bool = None, input_field_placeholder: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.force_reply = True self.selective = selective diff --git a/telegram/_games/game.py b/telegram/_games/game.py index a17496147ed..57ddade3b0c 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -19,7 +19,7 @@ """This module contains an object that represents a Telegram Game.""" import sys -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._files.animation import Animation from telegram._files.photosize import PhotoSize @@ -88,7 +88,7 @@ def __init__( text: str = None, text_entities: List[MessageEntity] = None, animation: Animation = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.title = title diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 60ab4c58145..e6dffca623c 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineKeyboardButton.""" -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Dict, Optional, Union from telegram._games.callbackgame import CallbackGame from telegram._loginurl import LoginUrl @@ -174,7 +174,7 @@ def __init__( pay: bool = None, login_url: LoginUrl = None, web_app: WebAppInfo = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.text = text diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 4ea6b0b733e..ca12df7c55a 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -52,7 +52,11 @@ class InlineKeyboardMarkup(TelegramObject): __slots__ = ("inline_keyboard",) - def __init__(self, inline_keyboard: List[List[InlineKeyboardButton]], **_kwargs: Any): + def __init__( + self, + inline_keyboard: List[List[InlineKeyboardButton]], + api_kwargs: Dict[str, object] = None, + ): if not check_keyboard_type(inline_keyboard): raise ValueError( "The parameter `inline_keyboard` should be a list of " diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index 3cc6100d975..8d693eb7a2b 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -90,9 +90,8 @@ def __init__( query: str, offset: str, location: Location = None, - bot: "Bot" = None, chat_type: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.id = id # pylint: disable=invalid-name diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 7dc78f476ba..7d0cca6a410 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -19,7 +19,7 @@ # pylint: disable=redefined-builtin """This module contains the classes that represent Telegram InlineQueryResult.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict @@ -48,7 +48,11 @@ class InlineQueryResult(TelegramObject): __slots__ = ("type", "id") - def __init__(self, type: str, id: str, **_kwargs: Any): # pylint: disable=invalid-name + def __init__( + self, type: str, id: str, api_kwargs: Dict[str, object] = None + ): # pylint: disable=invalid-name + super().__init__(api_kwargs=api_kwargs) + # Required self.type = type self.id = str(id) # pylint: disable=invalid-name diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index eb8c90ee264..2320cbbc201 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -91,7 +91,7 @@ def __init__( thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 392827f9cce..1511cd1d65f 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultAudio.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -105,7 +105,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 09a1e5cdbcb..e9e64ea8342 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -93,10 +93,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required - super().__init__(InlineQueryResultType.AUDIO, id) + super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) self.audio_file_id = audio_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 22337dea7f7..c4a77f8541e 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -101,7 +101,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.DOCUMENT, id) diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 9bf742335f1..c2dafde7f90 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -98,7 +98,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.GIF, id) diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index 49be4c572f0..c4a551cb649 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -98,7 +98,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.MPEG4GIF, id) diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 03619e2fac1..ead483be2ae 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -102,10 +102,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required - super().__init__(InlineQueryResultType.PHOTO, id) + super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) self.photo_file_id = photo_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index 4727e8eac62..20adff39811 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedSticker.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -62,7 +62,7 @@ def __init__( sticker_file_id: str, reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.STICKER, id) diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 9b277074d85..390c45391ed 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -102,7 +102,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.VIDEO, id) diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 632e3ce0ac7..964e4be236c 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -97,7 +97,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.VOICE, id) diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index 520ac51affa..4b29a86e15e 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -92,7 +92,7 @@ def __init__( thumb_width: int = None, thumb_height: int = None, vcard: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.CONTACT, id) diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index b43c829d109..717a7b0f1b8 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultDocument""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -120,7 +120,7 @@ def __init__( thumb_height: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.DOCUMENT, id) diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 9ce7b593bb9..4a97016b19f 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultGame.""" -from typing import Any +from typing import Dict from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -51,7 +51,7 @@ def __init__( id: str, # pylint: disable=redefined-builtin game_short_name: str, reply_markup: InlineKeyboardMarkup = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.GAME, id) diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 63d1ff99b03..3e01c047b4e 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -120,7 +120,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index b5f81d72777..549078babbf 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultLocation.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -112,7 +112,7 @@ def __init__( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.LOCATION, id) diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 355592bc84d..fe05de98c69 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -120,7 +120,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 9475288ba37..9fcd97b3287 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -115,7 +115,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required super().__init__(InlineQueryResultType.PHOTO, id) diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 5449c92c61c..1959d4a6c38 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVenue.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Dict from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -114,7 +114,7 @@ def __init__( thumb_height: int = None, google_place_id: str = None, google_place_type: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index ccad6fdfc26..02c559f92b8 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVideo.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -130,7 +130,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index c57cc67a2f5..dc93d4646bc 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVoice.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -102,7 +102,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required diff --git a/telegram/_inline/inputcontactmessagecontent.py b/telegram/_inline/inputcontactmessagecontent.py index e7273a13731..9abfb10cb4b 100644 --- a/telegram/_inline/inputcontactmessagecontent.py +++ b/telegram/_inline/inputcontactmessagecontent.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputContactMessageContent.""" -from typing import Any +from typing import Dict from telegram._inline.inputmessagecontent import InputMessageContent @@ -54,8 +54,10 @@ def __init__( first_name: str, last_name: str = None, vcard: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.phone_number = phone_number self.first_name = first_name diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 1beeb199a8c..5262757ec7a 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains a class that represents a Telegram InputInvoiceMessageContent.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._inline.inputmessagecontent import InputMessageContent from telegram._payment.labeledprice import LabeledPrice @@ -179,7 +179,7 @@ def __init__( send_phone_number_to_provider: bool = None, send_email_to_provider: bool = None, is_flexible: bool = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.title = title diff --git a/telegram/_inline/inputlocationmessagecontent.py b/telegram/_inline/inputlocationmessagecontent.py index 65979fbfd79..d0b4c3ddaed 100644 --- a/telegram/_inline/inputlocationmessagecontent.py +++ b/telegram/_inline/inputlocationmessagecontent.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputLocationMessageContent.""" -from typing import Any +from typing import Dict from telegram._inline.inputmessagecontent import InputMessageContent @@ -72,7 +72,7 @@ def __init__( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.latitude = latitude diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index a3b5ae91f74..4028ed6629b 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -72,7 +72,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.message_text = message_text diff --git a/telegram/_inline/inputvenuemessagecontent.py b/telegram/_inline/inputvenuemessagecontent.py index 83ed9d60834..3229177621f 100644 --- a/telegram/_inline/inputvenuemessagecontent.py +++ b/telegram/_inline/inputvenuemessagecontent.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputVenueMessageContent.""" -from typing import Any +from typing import Dict from telegram._inline.inputmessagecontent import InputMessageContent @@ -82,8 +82,10 @@ def __init__( foursquare_type: str = None, google_place_id: str = None, google_place_type: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.latitude = latitude self.longitude = longitude diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index 862addbb771..43b4ce19a30 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -88,7 +88,7 @@ def __init__( request_location: bool = None, request_poll: KeyboardButtonPollType = None, web_app: WebAppInfo = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.text = text diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index 39d0b26788d..5d36b737a7e 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -17,7 +17,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/]. """This module contains an object that represents a type of a Telegram Poll.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -40,7 +40,9 @@ class KeyboardButtonPollType(TelegramObject): __slots__ = ("type",) - def __init__(self, type: str = None, **_kwargs: Any): # pylint: disable=redefined-builtin + def __init__( + self, type: str = None, api_kwargs: Dict[str, object] = None + ): # pylint: disable=redefined-builtin self.type = type self._id_attrs = (self.type,) diff --git a/telegram/_loginurl.py b/telegram/_loginurl.py index 261acaa8b9e..e4872709e54 100644 --- a/telegram/_loginurl.py +++ b/telegram/_loginurl.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram LoginUrl.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -76,7 +76,7 @@ def __init__( forward_text: bool = None, bot_username: str = None, request_write_access: bool = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.url = url diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index f1823d4232a..a9c3b0aafae 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -17,7 +17,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/]. """This module contains objects related to Telegram menu buttons.""" -from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Type +from typing import TYPE_CHECKING, ClassVar, Dict, Optional, Type from telegram import constants from telegram._telegramobject import TelegramObject @@ -54,7 +54,9 @@ class MenuButton(TelegramObject): __slots__ = ("type",) - def __init__(self, type: str, **_kwargs: Any): # pylint: disable=redefined-builtin + def __init__( + self, type: str, api_kwargs: Dict[str, object] = None + ): # pylint: disable=redefined-builtin self.type = type self._id_attrs = (self.type,) @@ -106,7 +108,7 @@ class MenuButtonCommands(MenuButton): __slots__ = () - def __init__(self, **_kwargs: Any): + def __init__(self, api_kwargs: Dict[str, object] = None): super().__init__(type=constants.MenuButtonType.COMMANDS) @@ -136,7 +138,7 @@ class MenuButtonWebApp(MenuButton): __slots__ = ("text", "web_app") - def __init__(self, text: str, web_app: WebAppInfo, **_kwargs: Any): + def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: Dict[str, object] = None): super().__init__(type=constants.MenuButtonType.WEB_APP) self.text = text self.web_app = web_app @@ -173,5 +175,5 @@ class MenuButtonDefault(MenuButton): __slots__ = () - def __init__(self, **_kwargs: Any): + def __init__(self, api_kwargs: Dict[str, object] = None): super().__init__(type=constants.MenuButtonType.DEFAULT) diff --git a/telegram/_message.py b/telegram/_message.py index 2c80aaa629f..b825074d88c 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -21,7 +21,7 @@ import datetime import sys from html import escape -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union from telegram._chat import Chat from telegram._dice import Dice @@ -368,7 +368,6 @@ class Message(TelegramObject): .. versionadded:: 20.0 reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. .. |custom_emoji_formatting_note| replace:: Custom emoji entities will currently be ignored by this function. Instead, the supplied replacement for the emoji will be used. @@ -487,7 +486,6 @@ def __init__( poll: Poll = None, forward_sender_name: str = None, reply_markup: InlineKeyboardMarkup = None, - bot: "Bot" = None, dice: Dice = None, via_bot: User = None, proximity_alert_triggered: ProximityAlertTriggered = None, @@ -500,8 +498,10 @@ def __init__( is_automatic_forward: bool = None, has_protected_content: bool = None, web_app_data: WebAppData = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.message_id = message_id # Optionals diff --git a/telegram/_messageautodeletetimerchanged.py b/telegram/_messageautodeletetimerchanged.py index 3308293b333..5a3d4d4a556 100644 --- a/telegram/_messageautodeletetimerchanged.py +++ b/telegram/_messageautodeletetimerchanged.py @@ -20,7 +20,7 @@ deletion. """ -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -49,7 +49,7 @@ class MessageAutoDeleteTimerChanged(TelegramObject): def __init__( self, message_auto_delete_time: int, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.message_auto_delete_time = message_auto_delete_time diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index 0debe9adf3a..ba03aac5d15 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram MessageEntity.""" -from typing import TYPE_CHECKING, Any, ClassVar, List, Optional +from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional from telegram import constants from telegram._telegramobject import TelegramObject @@ -87,7 +87,7 @@ def __init__( user: User = None, language: str = None, custom_emoji_id: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.type = enum.get_member(constants.MessageEntityType, type, type) diff --git a/telegram/_messageid.py b/telegram/_messageid.py index dd764dcfb2e..9c8301409cc 100644 --- a/telegram/_messageid.py +++ b/telegram/_messageid.py @@ -17,7 +17,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/]. """This module contains an object that represents an instance of a Telegram MessageId.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -34,7 +34,7 @@ class MessageId(TelegramObject): __slots__ = ("message_id",) - def __init__(self, message_id: int, **_kwargs: Any): + def __init__(self, message_id: int, api_kwargs: Dict[str, object] = None): self.message_id = message_id self._id_attrs = (self.message_id,) diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index c0a82ff579d..386997f2177 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -19,7 +19,7 @@ # pylint: disable=missing-module-docstring, redefined-builtin import json from base64 import b64decode -from typing import TYPE_CHECKING, Any, List, Optional, no_type_check +from typing import TYPE_CHECKING, Dict, List, Optional, no_type_check try: from cryptography.hazmat.backends import default_backend @@ -137,7 +137,13 @@ class EncryptedCredentials(TelegramObject): "_decrypted_data", ) - def __init__(self, data: str, hash: str, secret: str, bot: "Bot" = None, **_kwargs: Any): + def __init__( + self, + data: str, + hash: str, + secret: str, + api_kwargs: Dict[str, object] = None, + ): # Required self.data = data self.hash = hash @@ -208,7 +214,12 @@ class Credentials(TelegramObject): __slots__ = ("nonce", "secure_data") - def __init__(self, secure_data: "SecureData", nonce: str, bot: "Bot" = None, **_kwargs: Any): + def __init__( + self, + secure_data: "SecureData", + nonce: str, + api_kwargs: Dict[str, object] = None, + ): # Required self.secure_data = secure_data self.nonce = nonce @@ -365,8 +376,7 @@ def __init__( selfie: "FileCredentials" = None, files: List["FileCredentials"] = None, translation: List["FileCredentials"] = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.data = data self.front_side = front_side @@ -409,7 +419,7 @@ class _CredentialsBase(TelegramObject): __slots__ = ("hash", "secret", "file_hash", "data_hash") - def __init__(self, hash: str, secret: str, bot: "Bot" = None, **_kwargs: Any): + def __init__(self, hash: str, secret: str, api_kwargs: Dict[str, object] = None): self.hash = hash self.secret = secret @@ -436,8 +446,8 @@ class DataCredentials(_CredentialsBase): __slots__ = () - def __init__(self, data_hash: str, secret: str, **_kwargs: Any): - super().__init__(data_hash, secret, **_kwargs) + def __init__(self, data_hash: str, secret: str, api_kwargs: Dict[str, object] = None): + super().__init__(data_hash, secret, api_kwargs=api_kwargs) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" @@ -465,8 +475,8 @@ class FileCredentials(_CredentialsBase): __slots__ = () - def __init__(self, file_hash: str, secret: str, **_kwargs: Any): - super().__init__(file_hash, secret, **_kwargs) + def __init__(self, file_hash: str, secret: str, api_kwargs: Dict[str, object] = None): + super().__init__(file_hash, secret, api_kwargs=api_kwargs) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_passport/data.py b/telegram/_passport/data.py index 3b97722fc23..5af39cb68fd 100644 --- a/telegram/_passport/data.py +++ b/telegram/_passport/data.py @@ -71,8 +71,7 @@ def __init__( last_name_native: str = None, middle_name: str = None, middle_name_native: str = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.first_name = first_name @@ -119,8 +118,7 @@ def __init__( state: str, country_code: str, post_code: str, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.street_line1 = street_line1 @@ -144,7 +142,12 @@ class IdDocumentData(TelegramObject): __slots__ = ("document_no", "expiry_date") - def __init__(self, document_no: str, expiry_date: str, bot: "Bot" = None, **_kwargs: Any): + def __init__( + self, + document_no: str, + expiry_date: str, + api_kwargs: Dict[str, object] = None, + ): self.document_no = document_no self.expiry_date = expiry_date diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 6d549ea2864..49e156fe4c5 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram EncryptedPassportElement.""" from base64 import b64decode -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._passport.credentials import decrypt_json from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress @@ -110,7 +110,7 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -139,10 +139,11 @@ def __init__( reverse_side: PassportFile = None, selfie: PassportFile = None, translation: List[PassportFile] = None, - bot: "Bot" = None, credentials: "Credentials" = None, # pylint: disable=unused-argument - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.type = type # Optionals diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index c1a094adbd3..cd52404ccff 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """Contains information about Telegram Passport data shared with the bot by the user.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._passport.credentials import EncryptedCredentials from telegram._passport.encryptedpassportelement import EncryptedPassportElement @@ -60,11 +60,14 @@ def __init__( data: List[EncryptedPassportElement], credentials: EncryptedCredentials, bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + self.data = data self.credentials = credentials + # TODO: Check if still need `set_bot` here or if doing that in de_json suffices self.set_bot(bot) self._decrypted_data: Optional[List[EncryptedPassportElement]] = None self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index 4ef378136df..8001d58a4d4 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -47,7 +47,7 @@ class PassportElementError(TelegramObject): __slots__ = ("message", "source", "type") - def __init__(self, source: str, type: str, message: str, **_kwargs: Any): + def __init__(self, source: str, type: str, message: str, api_kwargs: Dict[str, object] = None): # Required self.source = str(source) self.type = str(type) @@ -86,7 +86,14 @@ class PassportElementErrorDataField(PassportElementError): __slots__ = ("data_hash", "field_name") - def __init__(self, type: str, field_name: str, data_hash: str, message: str, **_kwargs: Any): + def __init__( + self, + type: str, + field_name: str, + data_hash: str, + message: str, + api_kwargs: Dict[str, object] = None, + ): # Required super().__init__("data", type, message) self.field_name = field_name @@ -123,7 +130,9 @@ class PassportElementErrorFile(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("file", type, message) self.file_hash = file_hash @@ -159,7 +168,9 @@ class PassportElementErrorFiles(PassportElementError): __slots__ = ("file_hashes",) - def __init__(self, type: str, file_hashes: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hashes: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("files", type, message) self.file_hashes = file_hashes @@ -195,7 +206,9 @@ class PassportElementErrorFrontSide(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("front_side", type, message) self.file_hash = file_hash @@ -231,7 +244,9 @@ class PassportElementErrorReverseSide(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("reverse_side", type, message) self.file_hash = file_hash @@ -265,7 +280,9 @@ class PassportElementErrorSelfie(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("selfie", type, message) self.file_hash = file_hash @@ -303,7 +320,9 @@ class PassportElementErrorTranslationFile(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("translation_file", type, message) self.file_hash = file_hash @@ -341,7 +360,9 @@ class PassportElementErrorTranslationFiles(PassportElementError): __slots__ = ("file_hashes",) - def __init__(self, type: str, file_hashes: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, file_hashes: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("translation_files", type, message) self.file_hashes = file_hashes @@ -373,7 +394,9 @@ class PassportElementErrorUnspecified(PassportElementError): __slots__ = ("element_hash",) - def __init__(self, type: str, element_hash: str, message: str, **_kwargs: Any): + def __init__( + self, type: str, element_hash: str, message: str, api_kwargs: Dict[str, object] = None + ): # Required super().__init__("unspecified", type, message) self.element_hash = element_hash diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index c0e18f0a594..6c5604e7d9d 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Encrypted PassportFile.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._telegramobject import TelegramObject from telegram._utils.defaultvalue import DEFAULT_NONE @@ -54,7 +54,7 @@ class PassportFile(TelegramObject): Can't be used to download or reuse the file. file_size (:obj:`int`): File size in bytes. file_date (:obj:`int`): Unix time when the file was uploaded. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -72,10 +72,11 @@ def __init__( file_unique_id: str, file_date: int, file_size: int, - bot: "Bot" = None, credentials: "FileCredentials" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.file_id = file_id self.file_unique_id = file_unique_id diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index 363fdf9a07c..464b71e9261 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Invoice.""" -from typing import Any, ClassVar +from typing import ClassVar, Dict from telegram import constants from telegram._telegramobject import TelegramObject @@ -69,7 +69,7 @@ def __init__( start_parameter: str, currency: str, total_amount: int, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.title = title self.description = description diff --git a/telegram/_payment/labeledprice.py b/telegram/_payment/labeledprice.py index 885a68d3d5c..f588e4e2d4d 100644 --- a/telegram/_payment/labeledprice.py +++ b/telegram/_payment/labeledprice.py @@ -49,7 +49,7 @@ class LabeledPrice(TelegramObject): __slots__ = ("label", "amount") - def __init__(self, label: str, amount: int, **_kwargs: Any): + def __init__(self, label: str, amount: int, api_kwargs: Dict[str, object] = None): self.label = label self.amount = amount diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index f61d1f21705..83d74c51728 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -58,7 +58,7 @@ def __init__( phone_number: str = None, email: str = None, shipping_address: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.name = name self.phone_number = phone_number diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index 56d5c94b3e5..6b67e702721 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram PreCheckoutQuery.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Dict, Optional from telegram._payment.orderinfo import OrderInfo from telegram._telegramobject import TelegramObject @@ -65,7 +65,7 @@ class PreCheckoutQuery(TelegramObject): shipping_option_id (:obj:`str`): Optional. Identifier of the shipping option chosen by the user. order_info (:class:`telegram.OrderInfo`): Optional. Order info provided by the user. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -88,8 +88,7 @@ def __init__( invoice_payload: str, shipping_option_id: str = None, order_info: OrderInfo = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.id = id # pylint: disable=invalid-name self.from_user = from_user diff --git a/telegram/_payment/shippingaddress.py b/telegram/_payment/shippingaddress.py index c31c537669e..e7ffaa23121 100644 --- a/telegram/_payment/shippingaddress.py +++ b/telegram/_payment/shippingaddress.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingAddress.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -66,7 +66,7 @@ def __init__( street_line1: str, street_line2: str, post_code: str, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.country_code = country_code self.state = state diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index 3a8db60c9af..eece4d91643 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingOption.""" -from typing import TYPE_CHECKING, Any, List +from typing import TYPE_CHECKING, Dict, List from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict @@ -55,8 +55,10 @@ def __init__( id: str, # pylint: disable=redefined-builtin, invalid-name title: str, prices: List["LabeledPrice"], - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): + super().__init__(api_kwargs=api_kwargs) + self.id = id # pylint: disable=invalid-name self.title = title self.prices = prices diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index ab970690db5..596ec9c3404 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -53,7 +53,7 @@ class ShippingQuery(TelegramObject): from_user (:class:`telegram.User`): User who sent the query. invoice_payload (:obj:`str`): Bot specified invoice payload. shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. + """ @@ -65,8 +65,7 @@ def __init__( from_user: User, invoice_payload: str, shipping_address: ShippingAddress, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.id = id # pylint: disable=invalid-name self.from_user = from_user diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index 4e30a10fb6c..b27dd6bc465 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram SuccessfulPayment.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Dict, Optional from telegram._payment.orderinfo import OrderInfo from telegram._telegramobject import TelegramObject @@ -82,7 +82,7 @@ def __init__( provider_payment_charge_id: str, shipping_option_id: str = None, order_info: OrderInfo = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.currency = currency self.total_amount = total_amount diff --git a/telegram/_poll.py b/telegram/_poll.py index 8074199268a..7f92303337e 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -20,7 +20,7 @@ import datetime import sys -from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional +from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional from telegram import constants from telegram._messageentity import MessageEntity @@ -53,7 +53,7 @@ class PollOption(TelegramObject): __slots__ = ("voter_count", "text") - def __init__(self, text: str, voter_count: int, **_kwargs: Any): + def __init__(self, text: str, voter_count: int, api_kwargs: Dict[str, object] = None): self.text = text self.voter_count = voter_count @@ -85,7 +85,9 @@ class PollAnswer(TelegramObject): __slots__ = ("option_ids", "user", "poll_id") - def __init__(self, poll_id: str, user: User, option_ids: List[int], **_kwargs: Any): + def __init__( + self, poll_id: str, user: User, option_ids: List[int], api_kwargs: Dict[str, object] = None + ): self.poll_id = poll_id self.user = user self.option_ids = option_ids @@ -190,7 +192,7 @@ def __init__( explanation_entities: List[MessageEntity] = None, open_period: int = None, close_date: datetime.datetime = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): self.id = id # pylint: disable=invalid-name self.question = question diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index bcdb3318b91..7fb44b98e94 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram Proximity Alert.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Dict, Optional from telegram._telegramobject import TelegramObject from telegram._user import User @@ -49,7 +49,9 @@ class ProximityAlertTriggered(TelegramObject): __slots__ = ("traveler", "distance", "watcher") - def __init__(self, traveler: User, watcher: User, distance: int, **_kwargs: Any): + def __init__( + self, traveler: User, watcher: User, distance: int, api_kwargs: Dict[str, object] = None + ): self.traveler = traveler self.watcher = watcher self.distance = distance diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 23f8405ee80..dfc2a2f186d 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ReplyKeyboardMarkup.""" -from typing import Any, List, Sequence, Union +from typing import Dict, List, Sequence, Union from telegram._keyboardbutton import KeyboardButton from telegram._telegramobject import TelegramObject @@ -92,7 +92,7 @@ def __init__( one_time_keyboard: bool = None, selective: bool = None, input_field_placeholder: str = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): if not check_keyboard_type(keyboard): raise ValueError( diff --git a/telegram/_replykeyboardremove.py b/telegram/_replykeyboardremove.py index a98e8b60966..77fe5abae56 100644 --- a/telegram/_replykeyboardremove.py +++ b/telegram/_replykeyboardremove.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram ReplyKeyboardRemove.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -57,7 +57,7 @@ class ReplyKeyboardRemove(TelegramObject): __slots__ = ("selective", "remove_keyboard") - def __init__(self, selective: bool = None, **_kwargs: Any): + def __init__(self, selective: bool = None, api_kwargs: Dict[str, object] = None): # Required self.remove_keyboard = True # Optionals diff --git a/telegram/_sentwebappmessage.py b/telegram/_sentwebappmessage.py index dec1c913fb2..67c1af9a717 100644 --- a/telegram/_sentwebappmessage.py +++ b/telegram/_sentwebappmessage.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Sent Web App Message.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -44,7 +44,7 @@ class SentWebAppMessage(TelegramObject): __slots__ = ("inline_message_id",) - def __init__(self, inline_message_id: str = None, **_kwargs: Any): + def __init__(self, inline_message_id: str = None, api_kwargs: Dict[str, object] = None): # Optionals self.inline_message_id = inline_message_id diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index a4c9e2f3578..e46dc9b229b 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -17,9 +17,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """Base class for Telegram Objects.""" +import inspect import json from copy import deepcopy -from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type, TypeVar, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, TypeVar, Union from telegram._utils.types import JSONDict from telegram._utils.warnings import warn @@ -48,24 +49,14 @@ class TelegramObject: special cases like :attr:`Message.from_user` that deviate from the official Bot API. """ - # type hints in __new__ are not read by mypy (https://github.com/python/mypy/issues/1021). As a - # workaround we can type hint instance variables in __new__ using a syntax defined in PEP 526 - - # https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations - if TYPE_CHECKING: - _id_attrs: Tuple[object, ...] - _bot: Optional["Bot"] - # Adding slots reduces memory usage & allows for faster attribute access. - # Only instance variables should be added to __slots__. - __slots__ = ("_id_attrs", "_bot") - - # pylint: disable=unused-argument - def __new__(cls, *args: object, **kwargs: object) -> "TelegramObject": - # We add _id_attrs in __new__ instead of __init__ since we want to add this to the slots - # w/o calling __init__ in all of the subclasses. - instance = super().__new__(cls) - instance._id_attrs = () - instance._bot = None - return instance + __slots__ = ("_id_attrs", "_bot", "api_kwargs") + + __INIT_PARAMS: Optional[Set[str]] = None + + def __int__(self, api_kwargs: Dict[str, object] = None) -> None: + self._id_attrs: Tuple[object, ...] = () + self._bot: Optional["Bot"] = None + self.api_kwargs: Dict[str, object] = api_kwargs or {} def __str__(self) -> str: return str(self.to_dict()) @@ -177,14 +168,29 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ The Telegram object. """ + if cls.__INIT_PARAMS is None: + signature = inspect.signature(cls) + cls.__INIT_PARAMS = set(signature.parameters.keys()) + data = cls._parse_data(data) if data is None: return None - if cls == TelegramObject: - return cls() - return cls(bot=bot, **data) + # try-except is significantly faster in case we already have a correct argument set + try: + obj = cls(**data) + except TypeError as exc: + if str(exc).startswith("__init__() got an unexpected keyword argument"): + kwargs = {} + api_kwargs = {} + for key, value in data.items(): + (kwargs if key in cls.__INIT_PARAMS else api_kwargs)[key] = value + obj = cls(api_kwargs=api_kwargs, **kwargs) + else: + raise exc + + obj.set_bot(bot=bot) @classmethod def de_list( diff --git a/telegram/_update.py b/telegram/_update.py index c6375b88e75..06d185e7859 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -236,7 +236,7 @@ def __init__( my_chat_member: ChatMemberUpdated = None, chat_member: ChatMemberUpdated = None, chat_join_request: ChatJoinRequest = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.update_id = update_id diff --git a/telegram/_user.py b/telegram/_user.py index c920df1c7b7..d4507e8cb4a 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram User.""" from datetime import datetime -from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton from telegram._menubutton import MenuButton @@ -104,7 +104,6 @@ class User(TelegramObject): disabled for the bot. Returned only in :attr:`telegram.Bot.get_me` requests. supports_inline_queries (:obj:`str`): Optional. :obj:`True`, if the bot supports inline queries. Returned only in :attr:`telegram.Bot.get_me` requests. - bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. is_premium (:obj:`bool`): Optional. :obj:`True`, if this user is a Telegram Premium user. @@ -140,10 +139,9 @@ def __init__( can_join_groups: bool = None, can_read_all_group_messages: bool = None, supports_inline_queries: bool = None, - bot: "Bot" = None, is_premium: bool = None, added_to_attachment_menu: bool = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.id = id # pylint: disable=invalid-name diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 34d5be90f4d..5187b54f3ba 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram UserProfilePhotos.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject @@ -47,7 +47,9 @@ class UserProfilePhotos(TelegramObject): __slots__ = ("photos", "total_count") - def __init__(self, total_count: int, photos: List[List[PhotoSize]], **_kwargs: Any): + def __init__( + self, total_count: int, photos: List[List[PhotoSize]], api_kwargs: Dict[str, object] = None + ): # Required self.total_count = total_count self.photos = photos diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 5d381832462..56ea1cc580a 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -42,7 +42,10 @@ class VideoChatStarted(TelegramObject): __slots__ = () - def __init__(self, **_kwargs: object): # skipcq: PTC-W0049 + def __init__( + self, + api_kwargs: Dict[str, object] = None, + ): # skipcq: PTC-W0049 pass @@ -70,7 +73,11 @@ class VideoChatEnded(TelegramObject): __slots__ = ("duration",) - def __init__(self, duration: int, **_kwargs: object) -> None: + def __init__( + self, + duration: int, + api_kwargs: Dict[str, object] = None, + ) -> None: self.duration = duration self._id_attrs = (self.duration,) @@ -97,7 +104,11 @@ class VideoChatParticipantsInvited(TelegramObject): __slots__ = ("users",) - def __init__(self, users: List[User], **_kwargs: object) -> None: + def __init__( + self, + users: List[User], + api_kwargs: Dict[str, object] = None, + ) -> None: self.users = users self._id_attrs = (self.users,) @@ -148,7 +159,11 @@ class VideoChatScheduled(TelegramObject): __slots__ = ("start_date",) - def __init__(self, start_date: dtm.datetime, **_kwargs: object) -> None: + def __init__( + self, + start_date: dtm.datetime, + api_kwargs: Dict[str, object] = None, + ) -> None: self.start_date = start_date self._id_attrs = (self.start_date,) diff --git a/telegram/_webappdata.py b/telegram/_webappdata.py index 2f531cdbc38..de6e9b30ac6 100644 --- a/telegram/_webappdata.py +++ b/telegram/_webappdata.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram WebAppData.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -52,7 +52,7 @@ class WebAppData(TelegramObject): __slots__ = ("data", "button_text") - def __init__(self, data: str, button_text: str, **_kwargs: Any): + def __init__(self, data: str, button_text: str, api_kwargs: Dict[str, object] = None): # Required self.data = data self.button_text = button_text diff --git a/telegram/_webappinfo.py b/telegram/_webappinfo.py index 96db5f12cc4..8097009f388 100644 --- a/telegram/_webappinfo.py +++ b/telegram/_webappinfo.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Web App Info.""" -from typing import Any +from typing import Dict from telegram._telegramobject import TelegramObject @@ -47,7 +47,7 @@ class WebAppInfo(TelegramObject): __slots__ = ("url",) - def __init__(self, url: str, **_kwargs: Any): + def __init__(self, url: str, api_kwargs: Dict[str, object] = None): # Required self.url = url diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index adc4d6b45e9..1eda204e969 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram WebhookInfo.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional from telegram._telegramobject import TelegramObject from telegram._utils.datetime import from_timestamp @@ -102,7 +102,7 @@ def __init__( allowed_updates: List[str] = None, ip_address: str = None, last_synchronization_error_date: int = None, - **_kwargs: Any, + api_kwargs: Dict[str, object] = None, ): # Required self.url = url From 790c53e6250dc2fb2b42d29cc6d93bd0c697436a Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 9 Sep 2022 09:28:54 +0200 Subject: [PATCH 02/90] More work on api_kwargs --- telegram/_bot.py | 1 + telegram/_botcommand.py | 4 +- telegram/_botcommandscope.py | 19 +++---- telegram/_callbackquery.py | 11 ++-- telegram/_chat.py | 12 ++-- telegram/_chatadministratorrights.py | 6 +- telegram/_chatinvitelink.py | 5 +- telegram/_chatjoinrequest.py | 8 +-- telegram/_chatlocation.py | 5 +- telegram/_chatmember.py | 15 ++--- telegram/_chatmemberupdated.py | 5 +- telegram/_chatpermissions.py | 6 +- telegram/_choseninlineresult.py | 4 +- telegram/_dice.py | 6 +- telegram/_files/_basemedium.py | 9 +-- telegram/_files/_basethumbedmedium.py | 4 +- telegram/_files/animation.py | 11 ++-- telegram/_files/audio.py | 12 ++-- telegram/_files/chatphoto.py | 11 ++-- telegram/_files/contact.py | 6 +- telegram/_files/document.py | 10 +--- telegram/_files/file.py | 14 ++--- telegram/_files/inputmedia.py | 2 + telegram/_files/location.py | 6 +- telegram/_files/photosize.py | 15 +++-- telegram/_files/sticker.py | 14 +++-- telegram/_files/venue.py | 6 +- telegram/_files/video.py | 12 ++-- telegram/_files/videonote.py | 10 +--- telegram/_files/voice.py | 12 ++-- telegram/_forcereply.py | 6 +- telegram/_games/game.py | 3 +- telegram/_games/gamehighscore.py | 1 + telegram/_inline/inlinekeyboardbutton.py | 5 +- telegram/_inline/inlinekeyboardmarkup.py | 5 +- telegram/_inline/inlinequery.py | 8 +-- telegram/_inline/inlinequeryresult.py | 4 +- telegram/_inline/inlinequeryresultarticle.py | 7 ++- telegram/_inline/inlinequeryresultaudio.py | 8 +-- .../_inline/inlinequeryresultcachedaudio.py | 6 +- .../inlinequeryresultcacheddocument.py | 8 +-- .../_inline/inlinequeryresultcachedgif.py | 8 +-- .../inlinequeryresultcachedmpeg4gif.py | 8 +-- .../_inline/inlinequeryresultcachedphoto.py | 6 +- .../_inline/inlinequeryresultcachedsticker.py | 7 ++- .../_inline/inlinequeryresultcachedvideo.py | 8 +-- .../_inline/inlinequeryresultcachedvoice.py | 8 +-- telegram/_inline/inlinequeryresultcontact.py | 7 ++- telegram/_inline/inlinequeryresultdocument.py | 8 +-- telegram/_inline/inlinequeryresultgame.py | 7 +-- telegram/_inline/inlinequeryresultgif.py | 8 +-- telegram/_inline/inlinequeryresultlocation.py | 7 ++- telegram/_inline/inlinequeryresultmpeg4gif.py | 8 +-- telegram/_inline/inlinequeryresultphoto.py | 8 +-- telegram/_inline/inlinequeryresultvenue.py | 7 ++- telegram/_inline/inlinequeryresultvideo.py | 8 +-- telegram/_inline/inlinequeryresultvoice.py | 8 +-- .../_inline/inputcontactmessagecontent.py | 5 +- .../_inline/inputinvoicemessagecontent.py | 5 +- .../_inline/inputlocationmessagecontent.py | 6 +- telegram/_inline/inputtextmessagecontent.py | 5 +- telegram/_inline/inputvenuemessagecontent.py | 5 +- telegram/_keyboardbutton.py | 5 +- telegram/_keyboardbuttonpolltype.py | 5 +- telegram/_loginurl.py | 5 +- telegram/_menubutton.py | 9 +-- telegram/_message.py | 4 +- telegram/_messageautodeletetimerchanged.py | 6 +- telegram/_messageentity.py | 5 +- telegram/_messageid.py | 5 +- telegram/_passport/credentials.py | 32 +++++------ telegram/_passport/data.py | 20 +++---- .../_passport/encryptedpassportelement.py | 9 +-- telegram/_passport/passportdata.py | 11 ++-- telegram/_passport/passportelementerrors.py | 56 +++++++------------ telegram/_passport/passportfile.py | 8 +-- telegram/_payment/invoice.py | 6 +- telegram/_payment/labeledprice.py | 6 +- telegram/_payment/orderinfo.py | 5 +- telegram/_payment/precheckoutquery.py | 9 ++- telegram/_payment/shippingaddress.py | 6 +- telegram/_payment/shippingoption.py | 6 +- telegram/_payment/shippingquery.py | 9 ++- telegram/_payment/successfulpayment.py | 5 +- telegram/_poll.py | 9 ++- telegram/_proximityalerttriggered.py | 7 +-- telegram/_replykeyboardmarkup.py | 5 +- telegram/_replykeyboardremove.py | 5 +- telegram/_sentwebappmessage.py | 6 +- telegram/_telegramobject.py | 9 +-- telegram/_update.py | 5 +- telegram/_user.py | 9 ++- telegram/_userprofilephotos.py | 5 +- telegram/_videochat.py | 17 ++++-- telegram/_webappdata.py | 6 +- telegram/_webappinfo.py | 6 +- telegram/_webhookinfo.py | 5 +- 97 files changed, 386 insertions(+), 409 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index b397bb6158a..7825865f816 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -201,6 +201,7 @@ def __init__( private_key: bytes = None, private_key_password: bytes = None, ): + super().__init__(api_kwargs=None) if not token: raise InvalidToken("You must pass the token you received from https://t.me/Botfather!") self.token = token diff --git a/telegram/_botcommand.py b/telegram/_botcommand.py index 11f04bf7fc1..0aa44c62a78 100644 --- a/telegram/_botcommand.py +++ b/telegram/_botcommand.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Bot Command.""" -from typing import Dict from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class BotCommand(TelegramObject): @@ -42,7 +42,7 @@ class BotCommand(TelegramObject): __slots__ = ("description", "command") - def __init__(self, command: str, description: str, api_kwargs: Dict[str, object] = None): + def __init__(self, command: str, description: str, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.command = command self.description = description diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 6a0f24cab24..aedfc6d5ad5 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -75,7 +75,8 @@ class BotCommandScope(TelegramObject): CHAT_MEMBER: ClassVar[str] = constants.BotCommandScopeType.CHAT_MEMBER """:const:`telegram.constants.BotCommandScopeType.CHAT_MEMBER`""" - def __init__(self, type: str, api_kwargs: Dict[str, object] = None): + def __init__(self, type: str, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.type = type self._id_attrs = (self.type,) @@ -126,7 +127,7 @@ class BotCommandScopeDefault(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: Dict[str, object] = None): + def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.DEFAULT) @@ -141,7 +142,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: Dict[str, object] = None): + def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS) @@ -156,7 +157,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: Dict[str, object] = None): + def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_GROUP_CHATS) @@ -171,7 +172,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: Dict[str, object] = None): + def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS) @@ -195,7 +196,7 @@ class BotCommandScopeChat(BotCommandScope): __slots__ = ("chat_id",) - def __init__(self, chat_id: Union[str, int], api_kwargs: Dict[str, object] = None): + def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) @@ -224,7 +225,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): __slots__ = ("chat_id",) - def __init__(self, chat_id: Union[str, int], api_kwargs: Dict[str, object] = None): + def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) @@ -255,9 +256,7 @@ class BotCommandScopeChatMember(BotCommandScope): __slots__ = ("chat_id", "user_id") - def __init__( - self, chat_id: Union[str, int], user_id: int, api_kwargs: Dict[str, object] = None - ): + def __init__(self, chat_id: Union[str, int], user_id: int, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_MEMBER) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index f611cfd3d03..5d584db80ea 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. # pylint: disable=redefined-builtin """This module contains an object that represents a Telegram CallbackQuery""" -from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, ClassVar, List, Optional, Tuple, Union from telegram import constants from telegram._files.location import Location @@ -79,7 +79,7 @@ class CallbackQuery(TelegramObject): inline mode, that originated the query. game_short_name (:obj:`str`, optional): Short name of a Game to be returned, serves as the unique identifier for the game - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + Attributes: id (:obj:`str`): Unique identifier for this query. @@ -96,7 +96,7 @@ class CallbackQuery(TelegramObject): inline_message_id (:obj:`str`): Optional. Identifier of the message sent via the bot in inline mode, that originated the query. game_short_name (:obj:`str`): Optional. Short name of a Game to be returned. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + """ @@ -119,8 +119,9 @@ def __init__( data: str = None, inline_message_id: str = None, game_short_name: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.id = id # pylint: disable=invalid-name self.from_user = from_user @@ -131,8 +132,6 @@ def __init__( self.inline_message_id = inline_message_id self.game_short_name = game_short_name - self.set_bot(bot) - self._id_attrs = (self.id,) @classmethod diff --git a/telegram/_chat.py b/telegram/_chat.py index b94a30353a8..909c2783009 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Chat.""" from datetime import datetime -from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, ClassVar, List, Optional, Tuple, Union from telegram import constants from telegram._chatlocation import ChatLocation @@ -114,7 +114,7 @@ class Chat(TelegramObject): be forwarded to other chats. Returned only in :meth:`telegram.Bot.get_chat`. .. versionadded:: 13.9 - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + sticker_set_name (:obj:`str`, optional): For supergroups, name of group sticker set. Returned only in :meth:`telegram.Bot.get_chat`. can_set_sticker_set (:obj:`bool`, optional): :obj:`True`, if the bot can change group the @@ -268,8 +268,9 @@ def __init__( join_to_send_messages: bool = None, join_by_request: bool = None, has_restricted_voice_and_video_messages: bool = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.id = id # pylint: disable=invalid-name self.type = enum.get_member(constants.ChatType, type, type) @@ -279,7 +280,9 @@ def __init__( self.first_name = first_name self.last_name = last_name # TODO: Remove (also from tests), when Telegram drops this completely - self.all_members_are_administrators = _kwargs.get("all_members_are_administrators") + self.all_members_are_administrators = ( + api_kwargs.pop("all_members_are_administrators", None) if api_kwargs else None + ) self.photo = photo self.bio = bio self.has_private_forwards = has_private_forwards @@ -300,7 +303,6 @@ def __init__( self.join_by_request = join_by_request self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages - self.set_bot(bot) self._id_attrs = (self.id,) @property diff --git a/telegram/_chatadministratorrights.py b/telegram/_chatadministratorrights.py index 310accac5b7..cc2f53dfff3 100644 --- a/telegram/_chatadministratorrights.py +++ b/telegram/_chatadministratorrights.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the class which represents a Telegram ChatAdministratorRights.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class ChatAdministratorRights(TelegramObject): @@ -119,8 +118,9 @@ def __init__( can_post_messages: bool = None, can_edit_messages: bool = None, can_pin_messages: bool = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ) -> None: + super().__init__(api_kwargs=api_kwargs) # Required self.is_anonymous = is_anonymous self.can_manage_chat = can_manage_chat diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index 52baa05db6b..e3b96254eb6 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents an invite link for a chat.""" import datetime -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Optional from telegram._telegramobject import TelegramObject from telegram._user import User @@ -113,8 +113,9 @@ def __init__( member_limit: int = None, name: str = None, pending_join_request_count: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.invite_link = invite_link self.creator = creator diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index fc1929caecc..1ea9426eae8 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatJoinRequest.""" import datetime -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Optional from telegram._chat import Chat from telegram._chatinvitelink import ChatInviteLink @@ -53,7 +53,7 @@ class ChatJoinRequest(TelegramObject): bio (:obj:`str`, optional): Bio of the user. invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link that was used by the user to send the join request. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + Attributes: chat (:class:`telegram.Chat`): Chat to which the request was sent. @@ -74,8 +74,9 @@ def __init__( date: datetime.datetime, bio: str = None, invite_link: ChatInviteLink = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.chat = chat self.from_user = from_user @@ -85,7 +86,6 @@ def __init__( self.bio = bio self.invite_link = invite_link - self.set_bot(bot) self._id_attrs = (self.chat, self.from_user, self.date) @classmethod diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index f9741c6d103..c5956b389eb 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a location to which a chat is connected.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Optional from telegram._files.location import Location from telegram._telegramobject import TelegramObject @@ -52,8 +52,9 @@ def __init__( self, location: Location, address: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.location = location self.address = address diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index e563dd63770..b160563e850 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -87,8 +87,9 @@ def __init__( self, user: User, status: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required by all subclasses self.user = user self.status = status @@ -159,7 +160,7 @@ def __init__( user: User, is_anonymous: bool, custom_title: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.OWNER, user=user) self.is_anonymous = is_anonymous @@ -281,7 +282,7 @@ def __init__( can_edit_messages: bool = None, can_pin_messages: bool = None, custom_title: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.ADMINISTRATOR, user=user) self.can_be_edited = can_be_edited @@ -321,7 +322,7 @@ class ChatMemberMember(ChatMember): def __init__( self, user: User, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.MEMBER, user=user) @@ -409,7 +410,7 @@ def __init__( can_send_other_messages: bool, can_add_web_page_previews: bool, until_date: datetime.datetime, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.RESTRICTED, user=user) self.is_member = is_member @@ -445,7 +446,7 @@ class ChatMemberLeft(ChatMember): def __init__( self, user: User, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.LEFT, user=user) @@ -477,7 +478,7 @@ def __init__( self, user: User, until_date: datetime.datetime, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.BANNED, user=user) self.until_date = until_date diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index b422a4e6dea..505dc16a9f3 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatMemberUpdated.""" import datetime -from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union +from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union from telegram._chat import Chat from telegram._chatinvitelink import ChatInviteLink @@ -83,8 +83,9 @@ def __init__( old_chat_member: ChatMember, new_chat_member: ChatMember, invite_link: ChatInviteLink = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.chat = chat self.from_user = from_user diff --git a/telegram/_chatpermissions.py b/telegram/_chatpermissions.py index 35de9e01e92..21ec8f01c57 100644 --- a/telegram/_chatpermissions.py +++ b/telegram/_chatpermissions.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatPermission.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class ChatPermissions(TelegramObject): @@ -99,8 +98,9 @@ def __init__( can_change_info: bool = None, can_invite_users: bool = None, can_pin_messages: bool = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.can_send_messages = can_send_messages self.can_send_media_messages = can_send_media_messages diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index f9f2fdfc2f7..abf44ef02be 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChosenInlineResult.""" -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Optional from telegram._files.location import Location from telegram._telegramobject import TelegramObject @@ -72,7 +72,7 @@ def __init__( query: str, location: Location = None, inline_message_id: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_dice.py b/telegram/_dice.py index 6bc07f59dbf..bfcc5c78a8d 100644 --- a/telegram/_dice.py +++ b/telegram/_dice.py @@ -17,10 +17,11 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Dice.""" -from typing import ClassVar, Dict, List +from typing import ClassVar, List from telegram import constants from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class Dice(TelegramObject): @@ -67,7 +68,8 @@ class Dice(TelegramObject): __slots__ = ("emoji", "value") - def __init__(self, value: int, emoji: str, api_kwargs: Dict[str, object] = None): + def __init__(self, value: int, emoji: str, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.value = value self.emoji = emoji diff --git a/telegram/_files/_basemedium.py b/telegram/_files/_basemedium.py index 0ae4b1c0aef..485efde394b 100644 --- a/telegram/_files/_basemedium.py +++ b/telegram/_files/_basemedium.py @@ -24,7 +24,7 @@ from telegram._utils.types import JSONDict, ODVInput if TYPE_CHECKING: - from telegram import Bot, File + from telegram import File class _BaseMedium(TelegramObject): @@ -39,7 +39,7 @@ class _BaseMedium(TelegramObject): is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. file_size (:obj:`int`, optional): File size. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + Attributes: file_id (:obj:`str`): File identifier. @@ -54,14 +54,15 @@ class _BaseMedium(TelegramObject): __slots__ = ("file_id", "file_size", "file_unique_id") def __init__( - self, file_id: str, file_unique_id: str, file_size: int = None, bot: "Bot" = None + self, file_id: str, file_unique_id: str, file_size: int = None, api_kwargs: JSONDict = None ): + super().__init__(api_kwargs=api_kwargs) + # Required self.file_id: str = str(file_id) self.file_unique_id = str(file_unique_id) # Optionals self.file_size = file_size - self.set_bot(bot) self._id_attrs = (self.file_unique_id,) diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index 219174c4da7..e321fe4b7b9 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -45,7 +45,7 @@ class _BaseThumbedMedium(_BaseMedium): Can't be used to download or reuse the file. file_size (:obj:`int`, optional): File size. thumb (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by sender. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + Attributes: file_id (:obj:`str`): File identifier. @@ -66,7 +66,7 @@ def __init__( file_unique_id: str, file_size: int = None, thumb: PhotoSize = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index ab4526a5dcd..2c3ffa8cd3a 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -17,13 +17,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Animation.""" -from typing import TYPE_CHECKING, Any from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class Animation(_BaseThumbedMedium): @@ -45,7 +42,7 @@ class Animation(_BaseThumbedMedium): file_name (:obj:`str`, optional): Original animation filename as defined by sender. mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -77,14 +74,14 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, thumb=thumb, - bot=bot, + api_kwargs=api_kwargs, ) # Required self.width = width diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index fc2ea8a83e6..657d5a1edab 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -18,13 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Audio.""" -from typing import TYPE_CHECKING, Any - from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class Audio(_BaseThumbedMedium): @@ -47,7 +43,7 @@ class Audio(_BaseThumbedMedium): file_size (:obj:`int`, optional): File size in bytes. thumb (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to which the music file belongs. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -81,14 +77,14 @@ def __init__( file_size: int = None, thumb: PhotoSize = None, file_name: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, thumb=thumb, - bot=bot, + api_kwargs=api_kwargs, ) # Required self.duration = duration diff --git a/telegram/_files/chatphoto.py b/telegram/_files/chatphoto.py index b9afea46e12..1d026e38897 100644 --- a/telegram/_files/chatphoto.py +++ b/telegram/_files/chatphoto.py @@ -17,14 +17,14 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatPhoto.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from telegram._telegramobject import TelegramObject from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput if TYPE_CHECKING: - from telegram import Bot, File + from telegram import File class ChatPhoto(TelegramObject): @@ -46,8 +46,6 @@ class ChatPhoto(TelegramObject): big_file_unique_id (:obj:`str`): Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: small_file_id (:obj:`str`): File identifier of small (160x160) chat photo. @@ -78,15 +76,14 @@ def __init__( small_file_unique_id: str, big_file_id: str, big_file_unique_id: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.small_file_id = small_file_id self.small_file_unique_id = small_file_unique_id self.big_file_id = big_file_id self.big_file_unique_id = big_file_unique_id - self.set_bot(bot) - self._id_attrs = ( self.small_file_unique_id, self.big_file_unique_id, diff --git a/telegram/_files/contact.py b/telegram/_files/contact.py index 06044f0a19b..7e2ea9d742d 100644 --- a/telegram/_files/contact.py +++ b/telegram/_files/contact.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Contact.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class Contact(TelegramObject): @@ -55,8 +54,9 @@ def __init__( last_name: str = None, user_id: int = None, vcard: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.phone_number = str(phone_number) self.first_name = first_name diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 0cfcae79246..da16947bb2a 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -18,13 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Document.""" -from typing import TYPE_CHECKING, Dict - from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class Document(_BaseThumbedMedium): @@ -43,7 +39,7 @@ class Document(_BaseThumbedMedium): file_name (:obj:`str`, optional): Original filename as defined by sender. mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -69,7 +65,7 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_files/file.py b/telegram/_files/file.py index 9b4983568a1..a39cbe7405a 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -21,16 +21,16 @@ import urllib.parse as urllib_parse from base64 import b64decode from pathlib import Path -from typing import IO, TYPE_CHECKING, Dict, Optional, Union +from typing import IO, TYPE_CHECKING, Optional, Union from telegram._passport.credentials import decrypt from telegram._telegramobject import TelegramObject from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.files import is_local_file -from telegram._utils.types import FilePathInput, ODVInput +from telegram._utils.types import FilePathInput, JSONDict, ODVInput if TYPE_CHECKING: - from telegram import Bot, FileCredentials + from telegram import FileCredentials class File(TelegramObject): @@ -56,8 +56,6 @@ class File(TelegramObject): Can't be used to download or reuse the file. file_size (:obj:`int`, optional): Optional. File size in bytes, if known. file_path (:obj:`str`, optional): File path. Use :attr:`download` to get the file. - bot (:obj:`telegram.Bot`, optional): Bot to use with shortcut method. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: file_id (:obj:`str`): Identifier for this file. @@ -83,15 +81,17 @@ def __init__( file_unique_id: str, file_size: int = None, file_path: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.file_id = str(file_id) self.file_unique_id = str(file_unique_id) # Optionals self.file_size = file_size self.file_path = file_path - self.set_bot(bot) + self._credentials: Optional["FileCredentials"] = None self._id_attrs = (self.file_unique_id,) diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 68445c882c2..c5d11de0cac 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -81,7 +81,9 @@ def __init__( caption: str = None, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.type = media_type self.media = media self.caption = caption diff --git a/telegram/_files/location.py b/telegram/_files/location.py index 6e3cdc0430a..aca10065121 100644 --- a/telegram/_files/location.py +++ b/telegram/_files/location.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Location.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class Location(TelegramObject): @@ -73,8 +72,9 @@ def __init__( live_period: int = None, heading: int = None, proximity_alert_radius: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.longitude = longitude self.latitude = latitude diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 2f026e4323b..fba08c5f268 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -18,12 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram PhotoSize.""" -from typing import TYPE_CHECKING, Dict - from telegram._files._basemedium import _BaseMedium - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class PhotoSize(_BaseMedium): @@ -41,7 +37,7 @@ class PhotoSize(_BaseMedium): width (:obj:`int`): Photo width. height (:obj:`int`): Photo height. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -65,10 +61,13 @@ def __init__( width: int, height: int, file_size: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( - file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, bot=bot + file_id=file_id, + file_unique_id=file_unique_id, + file_size=file_size, + api_kwargs=api_kwargs, ) # Required self.width = width diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 944811dfbd8..39c13621ff6 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects that represent stickers.""" -from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional +from typing import TYPE_CHECKING, ClassVar, List, Optional from telegram import constants from telegram._files._basethumbedmedium import _BaseThumbedMedium @@ -67,7 +67,7 @@ class Sticker(_BaseThumbedMedium): mask_position (:class:`telegram.MaskPosition`, optional): For mask stickers, the position where the mask should be placed. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + premium_animation (:class:`telegram.File`, optional): For premium regular stickers, premium animation for the sticker. @@ -141,14 +141,14 @@ def __init__( mask_position: "MaskPosition" = None, premium_animation: "File" = None, custom_emoji_id: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, thumb=thumb, - bot=bot, + api_kwargs=api_kwargs, ) # Required self.width = width @@ -250,8 +250,9 @@ def __init__( is_video: bool, sticker_type: str, thumb: PhotoSize = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.name = name self.title = title self.is_animated = is_animated @@ -329,8 +330,9 @@ def __init__( x_shift: float, y_shift: float, scale: float, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.point = point self.x_shift = x_shift self.y_shift = y_shift diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index 0339ccf3e62..43428532d46 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Venue.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Optional from telegram._files.location import Location from telegram._telegramobject import TelegramObject @@ -81,8 +81,10 @@ def __init__( foursquare_type: str = None, google_place_id: str = None, google_place_type: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) + # Required self.location = location self.title = title diff --git a/telegram/_files/video.py b/telegram/_files/video.py index 905fe5269b9..e3bf20bb62b 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -18,13 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Video.""" -from typing import TYPE_CHECKING, Dict - from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class Video(_BaseThumbedMedium): @@ -46,7 +42,7 @@ class Video(_BaseThumbedMedium): file_name (:obj:`str`, optional): Original filename as defined by sender. mime_type (:obj:`str`, optional): MIME type of a file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -78,14 +74,14 @@ def __init__( mime_type: str = None, file_size: int = None, file_name: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, thumb=thumb, - bot=bot, + api_kwargs=api_kwargs, ) # Required self.width = width diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index c50165eb6d6..92e01641cd0 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -18,13 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram VideoNote.""" -from typing import TYPE_CHECKING, Dict - from telegram._files._basethumbedmedium import _BaseThumbedMedium from telegram._files.photosize import PhotoSize - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class VideoNote(_BaseThumbedMedium): @@ -44,7 +40,7 @@ class VideoNote(_BaseThumbedMedium): duration (:obj:`int`): Duration of the video in seconds as defined by sender. thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -70,7 +66,7 @@ def __init__( duration: int, thumb: PhotoSize = None, file_size: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index 8f35367a931..97409dd2d95 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -18,12 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Voice.""" -from typing import TYPE_CHECKING, Dict - from telegram._files._basemedium import _BaseMedium - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class Voice(_BaseMedium): @@ -41,7 +37,7 @@ class Voice(_BaseMedium): duration (:obj:`int`, optional): Duration of the audio in seconds as defined by sender. mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -65,13 +61,13 @@ def __init__( duration: int, mime_type: str = None, file_size: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__( file_id=file_id, file_unique_id=file_unique_id, file_size=file_size, - bot=bot, + api_kwargs=api_kwargs, ) # Required self.duration = duration diff --git a/telegram/_forcereply.py b/telegram/_forcereply.py index 56dba949909..fd53c3121c0 100644 --- a/telegram/_forcereply.py +++ b/telegram/_forcereply.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ForceReply.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class ForceReply(TelegramObject): @@ -70,8 +69,9 @@ def __init__( self, selective: bool = None, input_field_placeholder: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.force_reply = True self.selective = selective self.input_field_placeholder = input_field_placeholder diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 57ddade3b0c..115a10cdd5f 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -88,8 +88,9 @@ def __init__( text: str = None, text_entities: List[MessageEntity] = None, animation: Animation = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.title = title self.description = description diff --git a/telegram/_games/gamehighscore.py b/telegram/_games/gamehighscore.py index f985a98b63f..4af13eb4e39 100644 --- a/telegram/_games/gamehighscore.py +++ b/telegram/_games/gamehighscore.py @@ -49,6 +49,7 @@ class GameHighScore(TelegramObject): __slots__ = ("position", "user", "score") def __init__(self, position: int, user: User, score: int): + super().__init__(api_kwargs=api_kwargs) self.position = position self.user = user self.score = score diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index e6dffca623c..cc2cd6836e3 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineKeyboardButton.""" -from typing import TYPE_CHECKING, Dict, Optional, Union +from typing import TYPE_CHECKING, Optional, Union from telegram._games.callbackgame import CallbackGame from telegram._loginurl import LoginUrl @@ -174,8 +174,9 @@ def __init__( pay: bool = None, login_url: LoginUrl = None, web_app: WebAppInfo = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.text = text diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index ca12df7c55a..d5e28931def 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineKeyboardMarkup.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton from telegram._telegramobject import TelegramObject @@ -55,8 +55,9 @@ class InlineKeyboardMarkup(TelegramObject): def __init__( self, inline_keyboard: List[List[InlineKeyboardButton]], - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) if not check_keyboard_type(inline_keyboard): raise ValueError( "The parameter `inline_keyboard` should be a list of " diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index 8d693eb7a2b..63c49cfb455 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineQuery.""" -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Sequence, Union +from typing import TYPE_CHECKING, Callable, ClassVar, Optional, Sequence, Union from telegram import constants from telegram._files.location import Location @@ -65,7 +65,7 @@ class InlineQuery(TelegramObject): .. versionadded:: 13.5 location (:class:`telegram.Location`, optional): Sender location, only for bots that request user location. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -91,8 +91,9 @@ def __init__( offset: str, location: Location = None, chat_type: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.id = id # pylint: disable=invalid-name self.from_user = from_user @@ -103,7 +104,6 @@ def __init__( self.location = location self.chat_type = chat_type - self.set_bot(bot) self._id_attrs = (self.id,) @classmethod diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 7d0cca6a410..6af9dca7ed7 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -19,8 +19,6 @@ # pylint: disable=redefined-builtin """This module contains the classes that represent Telegram InlineQueryResult.""" -from typing import Dict - from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict @@ -49,7 +47,7 @@ class InlineQueryResult(TelegramObject): __slots__ = ("type", "id") def __init__( - self, type: str, id: str, api_kwargs: Dict[str, object] = None + self, type: str, id: str, api_kwargs: JSONDict = None ): # pylint: disable=invalid-name super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index 2320cbbc201..6a9db00213d 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultArticle.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -91,11 +92,11 @@ def __init__( thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.ARTICLE, id) + super().__init__(InlineQueryResultType.ARTICLE, id, api_kwargs=api_kwargs) self.title = title self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 1511cd1d65f..e2714affb49 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultAudio.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -105,11 +105,11 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.AUDIO, id) + super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) self.audio_url = audio_url self.title = title diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index e9e64ea8342..dd746b98a36 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -93,7 +93,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index c4a77f8541e..b5147be13e8 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -101,10 +101,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.DOCUMENT, id) + super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) self.title = title self.document_file_id = document_file_id diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index c2dafde7f90..c49c9e55cd0 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedGif.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -98,10 +98,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.GIF, id) + super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) self.gif_file_id = gif_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index c4a551cb649..44da19dcc5f 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -98,10 +98,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.MPEG4GIF, id) + super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) self.mpeg4_file_id = mpeg4_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index ead483be2ae..31c5c3a7732 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -102,7 +102,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index 20adff39811..cecea74f12c 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedSticker.""" -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -62,10 +63,10 @@ def __init__( sticker_file_id: str, reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.STICKER, id) + super().__init__(InlineQueryResultType.STICKER, id, api_kwargs=api_kwargs) self.sticker_file_id = sticker_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 390c45391ed..4d10f17ee4b 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -102,10 +102,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.VIDEO, id) + super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) self.video_file_id = video_file_id self.title = title diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 964e4be236c..898dd470470 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -97,10 +97,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.VOICE, id) + super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) self.voice_file_id = voice_file_id self.title = title diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index 4b29a86e15e..2634ca53d6a 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultContact.""" -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -92,10 +93,10 @@ def __init__( thumb_width: int = None, thumb_height: int = None, vcard: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.CONTACT, id) + super().__init__(InlineQueryResultType.CONTACT, id, api_kwargs=api_kwargs) self.phone_number = phone_number self.first_name = first_name diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 717a7b0f1b8..08489313bc4 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultDocument""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -120,10 +120,10 @@ def __init__( thumb_height: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.DOCUMENT, id) + super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) self.document_url = document_url self.title = title self.mime_type = mime_type diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 4a97016b19f..a67244038d1 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -18,10 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultGame.""" -from typing import Dict - from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict from telegram.constants import InlineQueryResultType @@ -51,10 +50,10 @@ def __init__( id: str, # pylint: disable=redefined-builtin game_short_name: str, reply_markup: InlineKeyboardMarkup = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.GAME, id) + super().__init__(InlineQueryResultType.GAME, id, api_kwargs=api_kwargs) self.id = id self.game_short_name = game_short_name diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 3e01c047b4e..7c7690c3830 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultGif.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -120,11 +120,11 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.GIF, id) + super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) self.gif_url = gif_url self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index 549078babbf..f8d6da5a74f 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultLocation.""" -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -112,10 +113,10 @@ def __init__( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.LOCATION, id) + super().__init__(InlineQueryResultType.LOCATION, id, api_kwargs=api_kwargs) self.latitude = latitude self.longitude = longitude self.title = title diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index fe05de98c69..d99c1b64c83 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" -from typing import TYPE_CHECKING, Any, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -120,11 +120,11 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.MPEG4GIF, id) + super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) self.mpeg4_url = mpeg4_url self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 9fcd97b3287..0ce94b54dd9 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -115,10 +115,10 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.PHOTO, id) + super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) self.photo_url = photo_url self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 1959d4a6c38..c6af29c0cf0 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVenue.""" -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult +from telegram._utils.types import JSONDict from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -114,11 +115,11 @@ def __init__( thumb_height: int = None, google_place_id: str = None, google_place_type: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.VENUE, id) + super().__init__(InlineQueryResultType.VENUE, id, api_kwargs=api_kwargs) self.latitude = latitude self.longitude = longitude self.title = title diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 02c559f92b8..85dffdf0e2a 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVideo.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -130,11 +130,11 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.VIDEO, id) + super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) self.video_url = video_url self.mime_type = mime_type self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index dc93d4646bc..f671401e718 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -18,13 +18,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVoice.""" -from typing import TYPE_CHECKING, Dict, List, Tuple, Union +from typing import TYPE_CHECKING, List, Tuple, Union from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity from telegram._utils.defaultvalue import DEFAULT_NONE -from telegram._utils.types import ODVInput +from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType if TYPE_CHECKING: @@ -102,11 +102,11 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required - super().__init__(InlineQueryResultType.VOICE, id) + super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) self.voice_url = voice_url self.title = title diff --git a/telegram/_inline/inputcontactmessagecontent.py b/telegram/_inline/inputcontactmessagecontent.py index 9abfb10cb4b..e341e6989c2 100644 --- a/telegram/_inline/inputcontactmessagecontent.py +++ b/telegram/_inline/inputcontactmessagecontent.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputContactMessageContent.""" -from typing import Dict - from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._utils.types import JSONDict class InputContactMessageContent(InputMessageContent): @@ -54,7 +53,7 @@ def __init__( first_name: str, last_name: str = None, vcard: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 5262757ec7a..1a7797a19e1 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains a class that represents a Telegram InputInvoiceMessageContent.""" -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._inline.inputmessagecontent import InputMessageContent from telegram._payment.labeledprice import LabeledPrice @@ -179,8 +179,9 @@ def __init__( send_phone_number_to_provider: bool = None, send_email_to_provider: bool = None, is_flexible: bool = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.title = title self.description = description diff --git a/telegram/_inline/inputlocationmessagecontent.py b/telegram/_inline/inputlocationmessagecontent.py index d0b4c3ddaed..04261f896db 100644 --- a/telegram/_inline/inputlocationmessagecontent.py +++ b/telegram/_inline/inputlocationmessagecontent.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputLocationMessageContent.""" -from typing import Dict - from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._utils.types import JSONDict class InputLocationMessageContent(InputMessageContent): @@ -72,8 +71,9 @@ def __init__( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.latitude = latitude self.longitude = longitude diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index 4028ed6629b..3b3849a925d 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputTextMessageContent.""" -from typing import Any, List, Tuple, Union +from typing import List, Tuple, Union from telegram._inline.inputmessagecontent import InputMessageContent from telegram._messageentity import MessageEntity @@ -72,8 +72,9 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.message_text = message_text # Optionals diff --git a/telegram/_inline/inputvenuemessagecontent.py b/telegram/_inline/inputvenuemessagecontent.py index 3229177621f..05024cbfe74 100644 --- a/telegram/_inline/inputvenuemessagecontent.py +++ b/telegram/_inline/inputvenuemessagecontent.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputVenueMessageContent.""" -from typing import Dict - from telegram._inline.inputmessagecontent import InputMessageContent +from telegram._utils.types import JSONDict class InputVenueMessageContent(InputMessageContent): @@ -82,7 +81,7 @@ def __init__( foursquare_type: str = None, google_place_id: str = None, google_place_type: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index 43b4ce19a30..c5ff71d48d8 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram KeyboardButton.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Optional from telegram._keyboardbuttonpolltype import KeyboardButtonPollType from telegram._telegramobject import TelegramObject @@ -88,8 +88,9 @@ def __init__( request_location: bool = None, request_poll: KeyboardButtonPollType = None, web_app: WebAppInfo = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.text = text # Optionals diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index 5d36b737a7e..f874f74621c 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a type of a Telegram Poll.""" -from typing import Dict from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class KeyboardButtonPollType(TelegramObject): @@ -41,8 +41,9 @@ class KeyboardButtonPollType(TelegramObject): __slots__ = ("type",) def __init__( - self, type: str = None, api_kwargs: Dict[str, object] = None + self, type: str = None, api_kwargs: JSONDict = None ): # pylint: disable=redefined-builtin + super().__init__(api_kwargs=api_kwargs) self.type = type self._id_attrs = (self.type,) diff --git a/telegram/_loginurl.py b/telegram/_loginurl.py index e4872709e54..1831719fe8e 100644 --- a/telegram/_loginurl.py +++ b/telegram/_loginurl.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram LoginUrl.""" -from typing import Dict from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class LoginUrl(TelegramObject): @@ -76,8 +76,9 @@ def __init__( forward_text: bool = None, bot_username: str = None, request_write_access: bool = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.url = url # Optional diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index a9c3b0aafae..ca4182c3c85 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -55,8 +55,9 @@ class MenuButton(TelegramObject): __slots__ = ("type",) def __init__( - self, type: str, api_kwargs: Dict[str, object] = None + self, type: str, api_kwargs: JSONDict = None ): # pylint: disable=redefined-builtin + super().__init__(api_kwargs=api_kwargs) self.type = type self._id_attrs = (self.type,) @@ -108,7 +109,7 @@ class MenuButtonCommands(MenuButton): __slots__ = () - def __init__(self, api_kwargs: Dict[str, object] = None): + def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.COMMANDS) @@ -138,7 +139,7 @@ class MenuButtonWebApp(MenuButton): __slots__ = ("text", "web_app") - def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: Dict[str, object] = None): + def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.WEB_APP) self.text = text self.web_app = web_app @@ -175,5 +176,5 @@ class MenuButtonDefault(MenuButton): __slots__ = () - def __init__(self, api_kwargs: Dict[str, object] = None): + def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.DEFAULT) diff --git a/telegram/_message.py b/telegram/_message.py index b825074d88c..ecbb8c65125 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -247,7 +247,6 @@ class Message(TelegramObject): .. versionadded:: 20.0 reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. ``login_url`` buttons are represented as ordinary url buttons. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. Attributes: message_id (:obj:`int`): Unique message identifier inside this chat. @@ -498,7 +497,7 @@ def __init__( is_automatic_forward: bool = None, has_protected_content: bool = None, web_app_data: WebAppData = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -563,7 +562,6 @@ def __init__( self.video_chat_participants_invited = video_chat_participants_invited self.reply_markup = reply_markup self.web_app_data = web_app_data - self.set_bot(bot) self._effective_attachment = DEFAULT_NONE diff --git a/telegram/_messageautodeletetimerchanged.py b/telegram/_messageautodeletetimerchanged.py index 5a3d4d4a556..1a117a04a18 100644 --- a/telegram/_messageautodeletetimerchanged.py +++ b/telegram/_messageautodeletetimerchanged.py @@ -20,9 +20,8 @@ deletion. """ -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class MessageAutoDeleteTimerChanged(TelegramObject): @@ -49,8 +48,9 @@ class MessageAutoDeleteTimerChanged(TelegramObject): def __init__( self, message_auto_delete_time: int, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.message_auto_delete_time = message_auto_delete_time self._id_attrs = (self.message_auto_delete_time,) diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index ba03aac5d15..6093b2cb258 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram MessageEntity.""" -from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional +from typing import TYPE_CHECKING, ClassVar, List, Optional from telegram import constants from telegram._telegramobject import TelegramObject @@ -87,8 +87,9 @@ def __init__( user: User = None, language: str = None, custom_emoji_id: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.type = enum.get_member(constants.MessageEntityType, type, type) self.offset = offset diff --git a/telegram/_messageid.py b/telegram/_messageid.py index 9c8301409cc..3ef9c5efbc0 100644 --- a/telegram/_messageid.py +++ b/telegram/_messageid.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents an instance of a Telegram MessageId.""" -from typing import Dict from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class MessageId(TelegramObject): @@ -34,7 +34,8 @@ class MessageId(TelegramObject): __slots__ = ("message_id",) - def __init__(self, message_id: int, api_kwargs: Dict[str, object] = None): + def __init__(self, message_id: int, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.message_id = message_id self._id_attrs = (self.message_id,) diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 386997f2177..9df4bb6f4b3 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -19,7 +19,7 @@ # pylint: disable=missing-module-docstring, redefined-builtin import json from base64 import b64decode -from typing import TYPE_CHECKING, Dict, List, Optional, no_type_check +from typing import TYPE_CHECKING, List, Optional, no_type_check try: from cryptography.hazmat.backends import default_backend @@ -142,8 +142,9 @@ def __init__( data: str, hash: str, secret: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.data = data self.hash = hash @@ -151,7 +152,6 @@ def __init__( self._id_attrs = (self.data, self.hash, self.secret) - self.set_bot(bot) self._decrypted_secret: Optional[str] = None self._decrypted_data: Optional["Credentials"] = None @@ -218,14 +218,13 @@ def __init__( self, secure_data: "SecureData", nonce: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.secure_data = secure_data self.nonce = nonce - self.set_bot(bot) - @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Credentials"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -294,9 +293,10 @@ def __init__( rental_agreement: "SecureValue" = None, passport_registration: "SecureValue" = None, temporary_registration: "SecureValue" = None, - bot: "Bot" = None, - **_kwargs: Any, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) + # Optionals self.temporary_registration = temporary_registration self.passport_registration = passport_registration @@ -310,8 +310,6 @@ def __init__( self.passport = passport self.personal_details = personal_details - self.set_bot(bot) - @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureData"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -376,8 +374,9 @@ def __init__( selfie: "FileCredentials" = None, files: List["FileCredentials"] = None, translation: List["FileCredentials"] = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.data = data self.front_side = front_side self.reverse_side = reverse_side @@ -385,8 +384,6 @@ def __init__( self.files = files self.translation = translation - self.set_bot(bot) - @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureValue"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -419,7 +416,8 @@ class _CredentialsBase(TelegramObject): __slots__ = ("hash", "secret", "file_hash", "data_hash") - def __init__(self, hash: str, secret: str, api_kwargs: Dict[str, object] = None): + def __init__(self, hash: str, secret: str, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.hash = hash self.secret = secret @@ -427,8 +425,6 @@ def __init__(self, hash: str, secret: str, api_kwargs: Dict[str, object] = None) self.file_hash = self.hash self.data_hash = self.hash - self.set_bot(bot) - class DataCredentials(_CredentialsBase): """ @@ -446,7 +442,7 @@ class DataCredentials(_CredentialsBase): __slots__ = () - def __init__(self, data_hash: str, secret: str, api_kwargs: Dict[str, object] = None): + def __init__(self, data_hash: str, secret: str, api_kwargs: JSONDict = None): super().__init__(data_hash, secret, api_kwargs=api_kwargs) def to_dict(self) -> JSONDict: @@ -475,7 +471,7 @@ class FileCredentials(_CredentialsBase): __slots__ = () - def __init__(self, file_hash: str, secret: str, api_kwargs: Dict[str, object] = None): + def __init__(self, file_hash: str, secret: str, api_kwargs: JSONDict = None): super().__init__(file_hash, secret, api_kwargs=api_kwargs) def to_dict(self) -> JSONDict: diff --git a/telegram/_passport/data.py b/telegram/_passport/data.py index 5af39cb68fd..8bccc195a10 100644 --- a/telegram/_passport/data.py +++ b/telegram/_passport/data.py @@ -17,12 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. # pylint: disable=missing-module-docstring -from typing import TYPE_CHECKING, Any from telegram._telegramobject import TelegramObject - -if TYPE_CHECKING: - from telegram import Bot +from telegram._utils.types import JSONDict class PersonalDetails(TelegramObject): @@ -71,8 +68,9 @@ def __init__( last_name_native: str = None, middle_name: str = None, middle_name_native: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.first_name = first_name self.last_name = last_name @@ -85,8 +83,6 @@ def __init__( self.last_name_native = last_name_native self.middle_name_native = middle_name_native - self.set_bot(bot) - class ResidentialAddress(TelegramObject): """ @@ -118,8 +114,9 @@ def __init__( state: str, country_code: str, post_code: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.street_line1 = street_line1 self.street_line2 = street_line2 @@ -128,8 +125,6 @@ def __init__( self.country_code = country_code self.post_code = post_code - self.set_bot(bot) - class IdDocumentData(TelegramObject): """ @@ -146,9 +141,8 @@ def __init__( self, document_no: str, expiry_date: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.document_no = document_no self.expiry_date = expiry_date - - self.set_bot(bot) diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 49e156fe4c5..6082162414e 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram EncryptedPassportElement.""" from base64 import b64decode -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._passport.credentials import decrypt_json from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress @@ -75,8 +75,6 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", @@ -110,7 +108,6 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - """ @@ -140,7 +137,7 @@ def __init__( selfie: PassportFile = None, translation: List[PassportFile] = None, credentials: "Credentials" = None, # pylint: disable=unused-argument - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -168,8 +165,6 @@ def __init__( self.selfie, ) - self.set_bot(bot) - @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["EncryptedPassportElement"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index cd52404ccff..4c319b8be0c 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """Contains information about Telegram Passport data shared with the bot by the user.""" -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._passport.credentials import EncryptedCredentials from telegram._passport.encryptedpassportelement import EncryptedPassportElement @@ -42,14 +42,14 @@ class PassportData(TelegramObject): data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. credentials (:class:`telegram.EncryptedCredentials`): Encrypted credentials. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + """ @@ -59,16 +59,13 @@ def __init__( self, data: List[EncryptedPassportElement], credentials: EncryptedCredentials, - bot: "Bot" = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) self.data = data self.credentials = credentials - # TODO: Check if still need `set_bot` here or if doing that in de_json suffices - self.set_bot(bot) self._decrypted_data: Optional[List[EncryptedPassportElement]] = None self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index 8001d58a4d4..e6aa355c927 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -19,9 +19,8 @@ # pylint: disable=redefined-builtin """This module contains the classes that represent Telegram PassportElementError.""" -from typing import Any - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class PassportElementError(TelegramObject): @@ -47,7 +46,8 @@ class PassportElementError(TelegramObject): __slots__ = ("message", "source", "type") - def __init__(self, source: str, type: str, message: str, api_kwargs: Dict[str, object] = None): + def __init__(self, source: str, type: str, message: str, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) # Required self.source = str(source) self.type = str(type) @@ -92,7 +92,7 @@ def __init__( field_name: str, data_hash: str, message: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # Required super().__init__("data", type, message) @@ -130,11 +130,9 @@ class PassportElementErrorFile(PassportElementError): __slots__ = ("file_hash",) - def __init__( - self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("file", type, message) + super().__init__("file", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -168,11 +166,9 @@ class PassportElementErrorFiles(PassportElementError): __slots__ = ("file_hashes",) - def __init__( - self, type: str, file_hashes: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("files", type, message) + super().__init__("files", type, message, api_kwargs=api_kwargs) self.file_hashes = file_hashes self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) @@ -206,11 +202,9 @@ class PassportElementErrorFrontSide(PassportElementError): __slots__ = ("file_hash",) - def __init__( - self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("front_side", type, message) + super().__init__("front_side", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -244,11 +238,9 @@ class PassportElementErrorReverseSide(PassportElementError): __slots__ = ("file_hash",) - def __init__( - self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("reverse_side", type, message) + super().__init__("reverse_side", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -280,11 +272,9 @@ class PassportElementErrorSelfie(PassportElementError): __slots__ = ("file_hash",) - def __init__( - self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("selfie", type, message) + super().__init__("selfie", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -320,11 +310,9 @@ class PassportElementErrorTranslationFile(PassportElementError): __slots__ = ("file_hash",) - def __init__( - self, type: str, file_hash: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("translation_file", type, message) + super().__init__("translation_file", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -360,11 +348,9 @@ class PassportElementErrorTranslationFiles(PassportElementError): __slots__ = ("file_hashes",) - def __init__( - self, type: str, file_hashes: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("translation_files", type, message) + super().__init__("translation_files", type, message, api_kwargs=api_kwargs) self.file_hashes = file_hashes self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) @@ -394,11 +380,9 @@ class PassportElementErrorUnspecified(PassportElementError): __slots__ = ("element_hash",) - def __init__( - self, type: str, element_hash: str, message: str, api_kwargs: Dict[str, object] = None - ): + def __init__(self, type: str, element_hash: str, message: str, api_kwargs: JSONDict = None): # Required - super().__init__("unspecified", type, message) + super().__init__("unspecified", type, message, api_kwargs=api_kwargs) self.element_hash = element_hash self._id_attrs = (self.source, self.type, self.element_hash, self.message) diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 6c5604e7d9d..8b25e5d5e06 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Encrypted PassportFile.""" -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._telegramobject import TelegramObject from telegram._utils.defaultvalue import DEFAULT_NONE @@ -44,7 +44,7 @@ class PassportFile(TelegramObject): Can't be used to download or reuse the file. file_size (:obj:`int`): File size in bytes. file_date (:obj:`int`): Unix time when the file was uploaded. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -73,7 +73,7 @@ def __init__( file_date: int, file_size: int, credentials: "FileCredentials" = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -83,7 +83,7 @@ def __init__( self.file_size = file_size self.file_date = file_date # Optionals - self.set_bot(bot) + self._credentials = credentials self._id_attrs = (self.file_unique_id,) diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index 464b71e9261..ad42f6b2c79 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Invoice.""" -from typing import ClassVar, Dict +from typing import ClassVar from telegram import constants from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class Invoice(TelegramObject): @@ -69,8 +70,9 @@ def __init__( start_parameter: str, currency: str, total_amount: int, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.title = title self.description = description self.start_parameter = start_parameter diff --git a/telegram/_payment/labeledprice.py b/telegram/_payment/labeledprice.py index f588e4e2d4d..527c06df5b6 100644 --- a/telegram/_payment/labeledprice.py +++ b/telegram/_payment/labeledprice.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram LabeledPrice.""" -from typing import Any - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class LabeledPrice(TelegramObject): @@ -49,7 +48,8 @@ class LabeledPrice(TelegramObject): __slots__ = ("label", "amount") - def __init__(self, label: str, amount: int, api_kwargs: Dict[str, object] = None): + def __init__(self, label: str, amount: int, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.label = label self.amount = amount diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index 83d74c51728..36f1469a0c2 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram OrderInfo.""" -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Optional from telegram._payment.shippingaddress import ShippingAddress from telegram._telegramobject import TelegramObject @@ -58,8 +58,9 @@ def __init__( phone_number: str = None, email: str = None, shipping_address: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.name = name self.phone_number = phone_number self.email = email diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index 6b67e702721..ed00cb73281 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram PreCheckoutQuery.""" -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Optional from telegram._payment.orderinfo import OrderInfo from telegram._telegramobject import TelegramObject @@ -53,7 +53,7 @@ class PreCheckoutQuery(TelegramObject): shipping_option_id (:obj:`str`, optional): Identifier of the shipping option chosen by the user. order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -88,8 +88,9 @@ def __init__( invoice_payload: str, shipping_option_id: str = None, order_info: OrderInfo = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.id = id # pylint: disable=invalid-name self.from_user = from_user self.currency = currency @@ -98,8 +99,6 @@ def __init__( self.shipping_option_id = shipping_option_id self.order_info = order_info - self.set_bot(bot) - self._id_attrs = (self.id,) @classmethod diff --git a/telegram/_payment/shippingaddress.py b/telegram/_payment/shippingaddress.py index e7ffaa23121..b08e57d6d2a 100644 --- a/telegram/_payment/shippingaddress.py +++ b/telegram/_payment/shippingaddress.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingAddress.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class ShippingAddress(TelegramObject): @@ -66,8 +65,9 @@ def __init__( street_line1: str, street_line2: str, post_code: str, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.country_code = country_code self.state = state self.city = city diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index eece4d91643..c9e76642454 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingOption.""" -from typing import TYPE_CHECKING, Dict, List +from typing import TYPE_CHECKING, List from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict @@ -52,10 +52,10 @@ class ShippingOption(TelegramObject): def __init__( self, - id: str, # pylint: disable=redefined-builtin, invalid-name + id: str, # pylint: disable=redefined-builtin title: str, prices: List["LabeledPrice"], - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index 596ec9c3404..7c14d1392cf 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingQuery.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._payment.shippingaddress import ShippingAddress from telegram._payment.shippingoption import ShippingOption @@ -45,7 +45,7 @@ class ShippingQuery(TelegramObject): from_user (:class:`telegram.User`): User who sent the query. invoice_payload (:obj:`str`): Bot specified invoice payload. shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: @@ -65,15 +65,14 @@ def __init__( from_user: User, invoice_payload: str, shipping_address: ShippingAddress, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.id = id # pylint: disable=invalid-name self.from_user = from_user self.invoice_payload = invoice_payload self.shipping_address = shipping_address - self.set_bot(bot) - self._id_attrs = (self.id,) @classmethod diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index b27dd6bc465..6eac4fe7cd9 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram SuccessfulPayment.""" -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Optional from telegram._payment.orderinfo import OrderInfo from telegram._telegramobject import TelegramObject @@ -82,8 +82,9 @@ def __init__( provider_payment_charge_id: str, shipping_option_id: str = None, order_info: OrderInfo = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.currency = currency self.total_amount = total_amount self.invoice_payload = invoice_payload diff --git a/telegram/_poll.py b/telegram/_poll.py index 7f92303337e..81484786e02 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -53,7 +53,8 @@ class PollOption(TelegramObject): __slots__ = ("voter_count", "text") - def __init__(self, text: str, voter_count: int, api_kwargs: Dict[str, object] = None): + def __init__(self, text: str, voter_count: int, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.text = text self.voter_count = voter_count @@ -86,8 +87,9 @@ class PollAnswer(TelegramObject): __slots__ = ("option_ids", "user", "poll_id") def __init__( - self, poll_id: str, user: User, option_ids: List[int], api_kwargs: Dict[str, object] = None + self, poll_id: str, user: User, option_ids: List[int], api_kwargs: JSONDict = None ): + super().__init__(api_kwargs=api_kwargs) self.poll_id = poll_id self.user = user self.option_ids = option_ids @@ -192,8 +194,9 @@ def __init__( explanation_entities: List[MessageEntity] = None, open_period: int = None, close_date: datetime.datetime = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) self.id = id # pylint: disable=invalid-name self.question = question self.options = options diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index 7fb44b98e94..0cf89f9d692 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram Proximity Alert.""" -from typing import TYPE_CHECKING, Dict, Optional +from typing import TYPE_CHECKING, Optional from telegram._telegramobject import TelegramObject from telegram._user import User @@ -49,9 +49,8 @@ class ProximityAlertTriggered(TelegramObject): __slots__ = ("traveler", "distance", "watcher") - def __init__( - self, traveler: User, watcher: User, distance: int, api_kwargs: Dict[str, object] = None - ): + def __init__(self, traveler: User, watcher: User, distance: int, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) self.traveler = traveler self.watcher = watcher self.distance = distance diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index dfc2a2f186d..7962a61a827 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ReplyKeyboardMarkup.""" -from typing import Dict, List, Sequence, Union +from typing import List, Sequence, Union from telegram._keyboardbutton import KeyboardButton from telegram._telegramobject import TelegramObject @@ -92,8 +92,9 @@ def __init__( one_time_keyboard: bool = None, selective: bool = None, input_field_placeholder: str = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) if not check_keyboard_type(keyboard): raise ValueError( "The parameter `keyboard` should be a list of list of " diff --git a/telegram/_replykeyboardremove.py b/telegram/_replykeyboardremove.py index 77fe5abae56..3e326604f58 100644 --- a/telegram/_replykeyboardremove.py +++ b/telegram/_replykeyboardremove.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ReplyKeyboardRemove.""" -from typing import Dict from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class ReplyKeyboardRemove(TelegramObject): @@ -57,7 +57,8 @@ class ReplyKeyboardRemove(TelegramObject): __slots__ = ("selective", "remove_keyboard") - def __init__(self, selective: bool = None, api_kwargs: Dict[str, object] = None): + def __init__(self, selective: bool = None, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) # Required self.remove_keyboard = True # Optionals diff --git a/telegram/_sentwebappmessage.py b/telegram/_sentwebappmessage.py index 67c1af9a717..4f6c2df8a4e 100644 --- a/telegram/_sentwebappmessage.py +++ b/telegram/_sentwebappmessage.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Sent Web App Message.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class SentWebAppMessage(TelegramObject): @@ -44,7 +43,8 @@ class SentWebAppMessage(TelegramObject): __slots__ = ("inline_message_id",) - def __init__(self, inline_message_id: str = None, api_kwargs: Dict[str, object] = None): + def __init__(self, inline_message_id: str = None, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) # Optionals self.inline_message_id = inline_message_id diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index e46dc9b229b..deae14999a8 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -53,10 +53,10 @@ class TelegramObject: __INIT_PARAMS: Optional[Set[str]] = None - def __int__(self, api_kwargs: Dict[str, object] = None) -> None: + def __init__(self, api_kwargs: JSONDict = None) -> None: self._id_attrs: Tuple[object, ...] = () self._bot: Optional["Bot"] = None - self.api_kwargs: Dict[str, object] = api_kwargs or {} + self.api_kwargs: JSONDict = api_kwargs or {} def __str__(self) -> str: return str(self.to_dict()) @@ -191,6 +191,7 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ raise exc obj.set_bot(bot=bot) + return obj @classmethod def de_list( @@ -257,13 +258,13 @@ def set_bot(self, bot: Optional["Bot"]) -> None: def __eq__(self, other: object) -> bool: if isinstance(other, self.__class__): - if self._id_attrs == (): + if not self._id_attrs: warn( f"Objects of type {self.__class__.__name__} can not be meaningfully tested for" " equivalence.", stacklevel=2, ) - if other._id_attrs == (): + if not other._id_attrs: warn( f"Objects of type {other.__class__.__name__} can not be meaningfully tested" " for equivalence.", diff --git a/telegram/_update.py b/telegram/_update.py index 06d185e7859..5c3d23156a9 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Update.""" -from typing import TYPE_CHECKING, Any, ClassVar, List, Optional +from typing import TYPE_CHECKING, ClassVar, List, Optional from telegram import constants from telegram._callbackquery import CallbackQuery @@ -236,8 +236,9 @@ def __init__( my_chat_member: ChatMemberUpdated = None, chat_member: ChatMemberUpdated = None, chat_join_request: ChatJoinRequest = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.update_id = update_id # Optionals diff --git a/telegram/_user.py b/telegram/_user.py index d4507e8cb4a..02af2446df5 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram User.""" from datetime import datetime -from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, List, Optional, Tuple, Union from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton from telegram._menubutton import MenuButton @@ -33,7 +33,6 @@ from telegram import ( Animation, Audio, - Bot, Contact, Document, InlineKeyboardMarkup, @@ -82,7 +81,7 @@ class User(TelegramObject): disabled for the bot. Returned only in :attr:`telegram.Bot.get_me` requests. supports_inline_queries (:obj:`str`, optional): :obj:`True`, if the bot supports inline queries. Returned only in :attr:`telegram.Bot.get_me` requests. - bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. + is_premium (:obj:`bool`, optional): :obj:`True`, if this user is a Telegram Premium user. .. versionadded:: 20.0 @@ -141,8 +140,9 @@ def __init__( supports_inline_queries: bool = None, is_premium: bool = None, added_to_attachment_menu: bool = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.id = id # pylint: disable=invalid-name self.first_name = first_name @@ -156,7 +156,6 @@ def __init__( self.supports_inline_queries = supports_inline_queries self.is_premium = is_premium self.added_to_attachment_menu = added_to_attachment_menu - self.set_bot(bot) self._id_attrs = (self.id,) diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 5187b54f3ba..8088516b8db 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram UserProfilePhotos.""" -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject @@ -48,8 +48,9 @@ class UserProfilePhotos(TelegramObject): __slots__ = ("photos", "total_count") def __init__( - self, total_count: int, photos: List[List[PhotoSize]], api_kwargs: Dict[str, object] = None + self, total_count: int, photos: List[List[PhotoSize]], api_kwargs: JSONDict = None ): + super().__init__(api_kwargs=api_kwargs) # Required self.total_count = total_count self.photos = photos diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 56ea1cc580a..6da1bc9dd4d 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -44,9 +44,9 @@ class VideoChatStarted(TelegramObject): def __init__( self, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): # skipcq: PTC-W0049 - pass + super().__init__(api_kwargs=api_kwargs) class VideoChatEnded(TelegramObject): @@ -76,8 +76,9 @@ class VideoChatEnded(TelegramObject): def __init__( self, duration: int, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ) -> None: + super().__init__(api_kwargs=api_kwargs) self.duration = duration self._id_attrs = (self.duration,) @@ -107,8 +108,9 @@ class VideoChatParticipantsInvited(TelegramObject): def __init__( self, users: List[User], - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ) -> None: + super().__init__(api_kwargs=api_kwargs) self.users = users self._id_attrs = (self.users,) @@ -162,8 +164,9 @@ class VideoChatScheduled(TelegramObject): def __init__( self, start_date: dtm.datetime, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ) -> None: + super().__init__(api_kwargs=api_kwargs) self.start_date = start_date self._id_attrs = (self.start_date,) @@ -178,7 +181,9 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["VideoChatSch data["start_date"] = from_timestamp(data["start_date"]) - return cls(**data, bot=bot) + obj = cls(**data) + obj.set_bot(bot) + return obj def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_webappdata.py b/telegram/_webappdata.py index de6e9b30ac6..d5d048ca3d4 100644 --- a/telegram/_webappdata.py +++ b/telegram/_webappdata.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram WebAppData.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class WebAppData(TelegramObject): @@ -52,7 +51,8 @@ class WebAppData(TelegramObject): __slots__ = ("data", "button_text") - def __init__(self, data: str, button_text: str, api_kwargs: Dict[str, object] = None): + def __init__(self, data: str, button_text: str, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) # Required self.data = data self.button_text = button_text diff --git a/telegram/_webappinfo.py b/telegram/_webappinfo.py index 8097009f388..3417ac39409 100644 --- a/telegram/_webappinfo.py +++ b/telegram/_webappinfo.py @@ -18,9 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Web App Info.""" -from typing import Dict - from telegram._telegramobject import TelegramObject +from telegram._utils.types import JSONDict class WebAppInfo(TelegramObject): @@ -47,7 +46,8 @@ class WebAppInfo(TelegramObject): __slots__ = ("url",) - def __init__(self, url: str, api_kwargs: Dict[str, object] = None): + def __init__(self, url: str, api_kwargs: JSONDict = None): + super().__init__(api_kwargs=api_kwargs) # Required self.url = url diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 1eda204e969..82ec39b7fb3 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram WebhookInfo.""" -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, List, Optional from telegram._telegramobject import TelegramObject from telegram._utils.datetime import from_timestamp @@ -102,8 +102,9 @@ def __init__( allowed_updates: List[str] = None, ip_address: str = None, last_synchronization_error_date: int = None, - api_kwargs: Dict[str, object] = None, + api_kwargs: JSONDict = None, ): + super().__init__(api_kwargs=api_kwargs) # Required self.url = url self.has_custom_certificate = has_custom_certificate From 1cc0f2ebe6368b77a711ab3848588e2288c6ad61 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 9 Sep 2022 14:29:53 +0200 Subject: [PATCH 03/90] More work on api_kwargs --- telegram/_botcommandscope.py | 4 ++-- telegram/_callbackquery.py | 2 +- telegram/_chat.py | 2 +- telegram/_chatjoinrequest.py | 2 +- telegram/_chatlocation.py | 2 +- telegram/_chatmember.py | 2 +- telegram/_files/_basethumbedmedium.py | 2 +- telegram/_files/sticker.py | 4 ++-- telegram/_games/gamehighscore.py | 2 +- telegram/_inline/inlinequery.py | 4 ++-- telegram/_inline/inputinvoicemessagecontent.py | 2 +- telegram/_menubutton.py | 4 ++-- telegram/_message.py | 2 +- telegram/_passport/credentials.py | 6 +++--- telegram/_passport/encryptedpassportelement.py | 4 ++-- telegram/_passport/passportdata.py | 2 +- telegram/_passport/passportfile.py | 2 +- telegram/_payment/precheckoutquery.py | 4 ++-- telegram/_payment/shippingquery.py | 4 ++-- telegram/_poll.py | 2 +- telegram/_proximityalerttriggered.py | 2 +- telegram/_webhookinfo.py | 2 +- 22 files changed, 31 insertions(+), 31 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index aedfc6d5ad5..1ddfcc22a71 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -109,8 +109,8 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc } if cls is BotCommandScope: - return _class_mapping.get(data["type"], cls)(**data, bot=bot) - return cls(**data) + return _class_mapping.get(data["type"], cls).de_json(data=data, bot=bot) + return super().de_json(data=data, bot=bot) class BotCommandScopeDefault(BotCommandScope): diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 5d584db80ea..cafd2dddf96 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -145,7 +145,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["CallbackQuer data["from_user"] = User.de_json(data.get("from"), bot) data["message"] = Message.de_json(data.get("message"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) async def answer( self, diff --git a/telegram/_chat.py b/telegram/_chat.py index 909c2783009..2bf1adf153e 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -347,7 +347,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]: data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot) data["location"] = ChatLocation.de_json(data.get("location"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) async def leave( self, diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index 1ea9426eae8..9658adff1ff 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -101,7 +101,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatJoinRequ data["date"] = from_timestamp(data.get("date", None)) data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index c5956b389eb..6cd496e5117 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -70,4 +70,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatLocation data["location"] = Location.de_json(data.get("location"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index b160563e850..b9bdbd2fd21 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -117,7 +117,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"] } if cls is ChatMember: - return _class_mapping.get(data["status"], cls)(**data, bot=bot) + return _class_mapping.get(data["status"], cls).de_json(data=data, bot=bot) return cls(**data) def to_dict(self) -> JSONDict: diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index e321fe4b7b9..921e67225ec 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -88,4 +88,4 @@ def de_json( data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 39c13621ff6..01367d68ba8 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -182,7 +182,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Sticker"]: data["mask_position"] = MaskPosition.de_json(data.get("mask_position"), bot) data["premium_animation"] = File.de_json(data.get("premium_animation"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) class StickerSet(TelegramObject): @@ -273,7 +273,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["StickerSet"] data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) data["stickers"] = Sticker.de_list(data.get("stickers"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_games/gamehighscore.py b/telegram/_games/gamehighscore.py index 4af13eb4e39..be58fd8f39b 100644 --- a/telegram/_games/gamehighscore.py +++ b/telegram/_games/gamehighscore.py @@ -48,7 +48,7 @@ class GameHighScore(TelegramObject): __slots__ = ("position", "user", "score") - def __init__(self, position: int, user: User, score: int): + def __init__(self, position: int, user: User, score: int, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.position = position self.user = user diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index 63c49cfb455..82b16125c73 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -85,7 +85,7 @@ class InlineQuery(TelegramObject): def __init__( self, - id: str, # pylint: disable=redefined-builtin, invalid-name + id: str, # pylint: disable=redefined-builtin from_user: User, query: str, offset: str, @@ -117,7 +117,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineQuery" data["from_user"] = User.de_json(data.get("from"), bot) data["location"] = Location.de_json(data.get("location"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) async def answer( self, diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 1a7797a19e1..86432847289 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -248,4 +248,4 @@ def de_json( data["prices"] = LabeledPrice.de_list(data.get("prices"), bot) - return cls(**data, bot=bot) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index ca4182c3c85..432b7608a56 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -88,7 +88,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] if cls is MenuButton and data["type"] in _class_mapping: return _class_mapping[data["type"]].de_json(data, bot=bot) - return cls(**data, bot=bot) + return super().de_json(data=data, bot=bot) COMMANDS: ClassVar[str] = constants.MenuButtonType.COMMANDS """:const:`telegram.constants.MenuButtonType.COMMANDS`""" @@ -156,7 +156,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButtonWe data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_message.py b/telegram/_message.py index ecbb8c65125..ba7edb6e240 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -653,7 +653,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Message"]: ) data["web_app_data"] = WebAppData.de_json(data.get("web_app_data"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) @property def effective_attachment( diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 9df4bb6f4b3..a67dd453846 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -235,7 +235,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Credentials" data["secure_data"] = SecureData.de_json(data.get("secure_data"), bot=bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) class SecureData(TelegramObject): @@ -334,7 +334,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureData"] data["passport"] = SecureValue.de_json(data.get("passport"), bot=bot) data["personal_details"] = SecureValue.de_json(data.get("personal_details"), bot=bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) class SecureValue(TelegramObject): @@ -399,7 +399,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureValue" data["files"] = FileCredentials.de_list(data.get("files"), bot=bot) data["translation"] = FileCredentials.de_list(data.get("translation"), bot=bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 6082162414e..74ad493f4da 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -179,7 +179,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["EncryptedPas data["selfie"] = PassportFile.de_json(data.get("selfie"), bot) data["translation"] = PassportFile.de_list(data.get("translation"), bot) or None - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) @classmethod def de_json_decrypted( @@ -242,7 +242,7 @@ def de_json_decrypted( or None ) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 4c319b8be0c..31e5320483c 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -80,7 +80,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PassportData data["data"] = EncryptedPassportElement.de_list(data.get("data"), bot) data["credentials"] = EncryptedCredentials.de_json(data.get("credentials"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 8b25e5d5e06..083c6e6b739 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -111,7 +111,7 @@ def de_json_decrypted( data["credentials"] = credentials - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) @classmethod def de_list_decrypted( diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index ed00cb73281..8693f20f951 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -81,7 +81,7 @@ class PreCheckoutQuery(TelegramObject): def __init__( self, - id: str, # pylint: disable=redefined-builtin, invalid-name + id: str, # pylint: disable=redefined-builtin from_user: User, currency: str, total_amount: int, @@ -112,7 +112,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PreCheckoutQ data["from_user"] = User.de_json(data.pop("from"), bot) data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) async def answer( # pylint: disable=invalid-name self, diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index 7c14d1392cf..3299b65ec5f 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -61,7 +61,7 @@ class ShippingQuery(TelegramObject): def __init__( self, - id: str, # pylint: disable=redefined-builtin, invalid-name + id: str, # pylint: disable=redefined-builtin from_user: User, invoice_payload: str, shipping_address: ShippingAddress, @@ -86,7 +86,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ShippingQuer data["from_user"] = User.de_json(data.pop("from"), bot) data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) async def answer( # pylint: disable=invalid-name self, diff --git a/telegram/_poll.py b/telegram/_poll.py index 81484786e02..5a5b0d015e7 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -181,7 +181,7 @@ class Poll(TelegramObject): def __init__( self, - id: str, # pylint: disable=redefined-builtin, invalid-name + id: str, # pylint: disable=redefined-builtin question: str, options: List[PollOption], total_voter_count: int, diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index 0cf89f9d692..af07399eaf0 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -68,4 +68,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ProximityAle data["traveler"] = User.de_json(data.get("traveler"), bot) data["watcher"] = User.de_json(data.get("watcher"), bot) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 82ec39b7fb3..4779e12bcc4 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -143,4 +143,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["WebhookInfo" data.get("last_synchronization_error_date") ) - return cls(bot=bot, **data) + return super().de_json(data=data, bot=bot) From 54d999f96bae788f976befc428d54677ec3998af Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 9 Sep 2022 14:47:31 +0200 Subject: [PATCH 04/90] Only copy data if necessary --- telegram/_telegramobject.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index deae14999a8..36cf3808068 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -154,6 +154,10 @@ def _get_attrs( @staticmethod def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]: + """Should be called by subclasses that override de_json to ensure that the input + is not altered. Whoever calls de_json might still want to use the original input + for something else. + """ return None if data is None else data.copy() @classmethod @@ -172,8 +176,6 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ signature = inspect.signature(cls) cls.__INIT_PARAMS = set(signature.parameters.keys()) - data = cls._parse_data(data) - if data is None: return None From 702b0084fa9ff661b365e9a9da8086de4ca354c4 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 9 Sep 2022 17:57:26 +0200 Subject: [PATCH 05/90] pre-commit updates --- telegram/_callbackquery.py | 2 +- telegram/_chat.py | 2 +- telegram/_inline/inlinekeyboardmarkup.py | 6 +++--- telegram/_inline/inlinequeryresult.py | 4 +--- telegram/_menubutton.py | 2 +- telegram/_replykeyboardmarkup.py | 6 +++--- telegram/_telegramobject.py | 13 ++++++++----- telegram/_user.py | 2 +- telegram/ext/_picklepersistence.py | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index cafd2dddf96..0cc302dd5b3 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -112,7 +112,7 @@ class CallbackQuery(TelegramObject): def __init__( self, - id: str, # pylint: disable=invalid-name + id: str, from_user: User, chat_instance: str, message: Message = None, diff --git a/telegram/_chat.py b/telegram/_chat.py index 2bf1adf153e..c6a765cf800 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -245,7 +245,7 @@ class Chat(TelegramObject): def __init__( self, - id: int, # pylint: disable=invalid-name + id: int, type: str, title: str = None, username: str = None, diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index d5e28931def..e39f76caa88 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -110,7 +110,7 @@ def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> "InlineK **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ - return cls([[button]], **kwargs) + return cls([[button]], **kwargs) # type: ignore[arg-type] @classmethod def from_row( @@ -128,7 +128,7 @@ def from_row( **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ - return cls([button_row], **kwargs) + return cls([button_row], **kwargs) # type: ignore[arg-type] @classmethod def from_column( @@ -147,7 +147,7 @@ def from_column( """ button_grid = [[button] for button in button_column] - return cls(button_grid, **kwargs) + return cls(button_grid, **kwargs) # type: ignore[arg-type] def __hash__(self) -> int: return hash(tuple(tuple(button for button in row) for row in self.inline_keyboard)) diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 6af9dca7ed7..376901e729f 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -46,9 +46,7 @@ class InlineQueryResult(TelegramObject): __slots__ = ("type", "id") - def __init__( - self, type: str, id: str, api_kwargs: JSONDict = None - ): # pylint: disable=invalid-name + def __init__(self, type: str, id: str, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Required diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 432b7608a56..4b83630f18c 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -156,7 +156,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButtonWe data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot) - return super().de_json(data=data, bot=bot) + return super().de_json(data=data, bot=bot) # type: ignore[return-value] def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 7962a61a827..472b0d20e98 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -178,7 +178,7 @@ def from_button( one_time_keyboard=one_time_keyboard, selective=selective, input_field_placeholder=input_field_placeholder, - **kwargs, + **kwargs, # type: ignore[arg-type] ) @classmethod @@ -231,7 +231,7 @@ def from_row( one_time_keyboard=one_time_keyboard, selective=selective, input_field_placeholder=input_field_placeholder, - **kwargs, + **kwargs, # type: ignore[arg-type] ) @classmethod @@ -285,7 +285,7 @@ def from_column( one_time_keyboard=one_time_keyboard, selective=selective, input_field_placeholder=input_field_placeholder, - **kwargs, + **kwargs, # type: ignore[arg-type] ) def __hash__(self) -> int: diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 36cf3808068..e9e6f5263ec 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -102,7 +102,7 @@ def __deepcopy__(self: TO_co, memodict: dict) -> TO_co: result.set_bot(bot) # Assign the bots back self.set_bot(bot) - return result # type: ignore[return-value] + return result def _get_attrs( self, @@ -140,7 +140,7 @@ def _get_attrs( value = getattr(self, key, None) if value is not None: if recursive and hasattr(value, "to_dict"): - data[key] = value.to_dict() + data[key] = value.to_dict() # pylint: disable=no-member else: data[key] = value elif not recursive: @@ -184,10 +184,13 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ obj = cls(**data) except TypeError as exc: if str(exc).startswith("__init__() got an unexpected keyword argument"): - kwargs = {} - api_kwargs = {} + kwargs: JSONDict = {} + api_kwargs: JSONDict = {} for key, value in data.items(): - (kwargs if key in cls.__INIT_PARAMS else api_kwargs)[key] = value + if key in cls.__INIT_PARAMS: # pylint: disable=unsupported-membership-test + kwargs[key] = value + else: + api_kwargs[key] = value obj = cls(api_kwargs=api_kwargs, **kwargs) else: raise exc diff --git a/telegram/_user.py b/telegram/_user.py index 02af2446df5..f88f2dea36e 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -129,7 +129,7 @@ class User(TelegramObject): def __init__( self, - id: int, # pylint: disable=invalid-name + id: int, first_name: str, is_bot: bool, last_name: str = None, diff --git a/telegram/ext/_picklepersistence.py b/telegram/ext/_picklepersistence.py index e08bce5d82e..82782a8f5a3 100644 --- a/telegram/ext/_picklepersistence.py +++ b/telegram/ext/_picklepersistence.py @@ -54,7 +54,7 @@ def _reconstruct_to(cls: Type[TO], kwargs: dict) -> TO: """ obj = cls.__new__(cls) obj.__setstate__(kwargs) - return obj # type: ignore[return-value] + return obj def _custom_reduction(cls: TO) -> Tuple[Callable, Tuple[Type[TO], dict]]: From b4f7efe16686c220a721d8c5230f2041db29cb20 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 9 Sep 2022 22:15:28 +0200 Subject: [PATCH 06/90] add _apply_api_kwargs --- telegram/_telegramobject.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index e9e6f5263ec..6c561cfbf26 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -56,8 +56,28 @@ class TelegramObject: def __init__(self, api_kwargs: JSONDict = None) -> None: self._id_attrs: Tuple[object, ...] = () self._bot: Optional["Bot"] = None + # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs self.api_kwargs: JSONDict = api_kwargs or {} + def _apply_api_kwargs(self) -> None: + """Loops through the api kwargs and for every key that exists as attribute of the + object (and is None), it moves the value from `api_kwargs` to the attribute. + + This method is currently only called in the unpickling process, i.e. not on "normal" init. + This is because + * automating this is tricky to get right: It should be called at the *end* of the __init__, + preferably only once at the end of the __init__ of the last child class. This could be + done via __init_subclass__, but it's hard to not destroy the signature of __init__ in the + process. + * calling it manually in every __init__ is tedious + * There probably is no use case for it anyway. If you manually initialize a TO subclass, + then you can pass everything as proper argument. + """ + # we convert to list to ensure that the list doesn't change length while we loop + for key in list(self.api_kwargs.keys()): + if getattr(self, key, True) is None: + setattr(self, key, self.api_kwargs.pop(key)) + def __str__(self) -> str: return str(self.to_dict()) From 912aa0e6de087373ed865a12beb0e58630841527 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 9 Sep 2022 23:46:20 +0200 Subject: [PATCH 07/90] Get started on tests --- telegram/_botcommandscope.py | 4 +- telegram/_callbackquery.py | 2 +- telegram/_chatjoinrequest.py | 2 +- telegram/_chatmember.py | 11 ++-- telegram/_chatmemberupdated.py | 2 +- telegram/_choseninlineresult.py | 2 +- telegram/_files/_basethumbedmedium.py | 4 +- telegram/_inline/inlinequery.py | 2 +- telegram/_message.py | 2 +- telegram/_payment/precheckoutquery.py | 2 +- telegram/_payment/shippingquery.py | 2 +- telegram/_telegramobject.py | 4 +- tests/conftest.py | 5 +- tests/test_application.py | 4 +- tests/test_botcommandscope.py | 5 +- tests/test_callbackquery.py | 2 +- tests/test_chatjoinrequest.py | 5 +- tests/test_chatjoinrequesthandler.py | 5 +- tests/test_conversationhandler.py | 95 +++++++++++++++------------ tests/test_file.py | 10 +-- tests/test_inlinequery.py | 5 +- tests/test_inlinequeryresultvoice.py | 1 - tests/test_message.py | 12 ++-- tests/test_messagehandler.py | 4 +- tests/test_passportfile.py | 5 +- tests/test_precheckoutquery.py | 5 +- tests/test_user.py | 5 +- 27 files changed, 119 insertions(+), 88 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 1ddfcc22a71..0fa902ee9b0 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -108,8 +108,8 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc cls.CHAT_MEMBER: BotCommandScopeChatMember, } - if cls is BotCommandScope: - return _class_mapping.get(data["type"], cls).de_json(data=data, bot=bot) + if cls is BotCommandScope and data["type"] in _class_mapping: + return _class_mapping[data["type"]].de_json(data=data, bot=bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 0cc302dd5b3..afd0816bdce 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -142,7 +142,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["CallbackQuer if not data: return None - data["from_user"] = User.de_json(data.get("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["message"] = Message.de_json(data.get("message"), bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index 9658adff1ff..b4a9db7149e 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -97,7 +97,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatJoinRequ return None data["chat"] = Chat.de_json(data.get("chat"), bot) - data["from_user"] = User.de_json(data.get("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["date"] = from_timestamp(data.get("date", None)) data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot) diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index b9bdbd2fd21..18d346dfa36 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -104,9 +104,6 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"] if not data: return None - data["user"] = User.de_json(data.get("user"), bot) - data["until_date"] = from_timestamp(data.get("until_date", None)) - _class_mapping: Dict[str, Type["ChatMember"]] = { cls.OWNER: ChatMemberOwner, cls.ADMINISTRATOR: ChatMemberAdministrator, @@ -117,8 +114,12 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"] } if cls is ChatMember: - return _class_mapping.get(data["status"], cls).de_json(data=data, bot=bot) - return cls(**data) + return _class_mapping.get(data.pop("status"), cls).de_json(data=data, bot=bot) + + data["user"] = User.de_json(data.get("user"), bot) + data["until_date"] = from_timestamp(data.get("until_date", None)) + + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index 505dc16a9f3..bd47a34dff8 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -113,7 +113,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMemberUp return None data["chat"] = Chat.de_json(data.get("chat"), bot) - data["from_user"] = User.de_json(data.get("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["date"] = from_timestamp(data.get("date")) data["old_chat_member"] = ChatMember.de_json(data.get("old_chat_member"), bot) data["new_chat_member"] = ChatMember.de_json(data.get("new_chat_member"), bot) diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index abf44ef02be..b8ca3a2235f 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -95,7 +95,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChosenInline return None # Required - data["from_user"] = User.de_json(data.pop("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) # Optionals data["location"] = Location.de_json(data.get("location"), bot) diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index 921e67225ec..744f60387f9 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -86,6 +86,8 @@ def de_json( if not data: return None - data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) + # In case this wasn't already done by the subclass + if not isinstance(data.get("thumb"), PhotoSize): + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index 82b16125c73..af76e224e9a 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -114,7 +114,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineQuery" if not data: return None - data["from_user"] = User.de_json(data.get("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["location"] = Location.de_json(data.get("location"), bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_message.py b/telegram/_message.py index ba7edb6e240..89b8aa5fa2f 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -603,7 +603,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Message"]: if not data: return None - data["from_user"] = User.de_json(data.get("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["sender_chat"] = Chat.de_json(data.get("sender_chat"), bot) data["date"] = from_timestamp(data["date"]) data["chat"] = Chat.de_json(data.get("chat"), bot) diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index 8693f20f951..76a5878571d 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -109,7 +109,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PreCheckoutQ if not data: return None - data["from_user"] = User.de_json(data.pop("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index 3299b65ec5f..a74ee2d0eb2 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -83,7 +83,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ShippingQuer if not data: return None - data["from_user"] = User.de_json(data.pop("from"), bot) + data["from_user"] = User.de_json(data.pop("from", None), bot) data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 6c561cfbf26..7834da057f1 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -154,7 +154,7 @@ def _get_attrs( # and then get their attributes. The `[:-1]` slice excludes the `object` class for cls in self.__class__.__mro__[:-1]: for key in cls.__slots__: # type: ignore[attr-defined] - if not include_private and key.startswith("_"): + if (not include_private and key.startswith("_")) or (key == "api_kwargs"): continue value = getattr(self, key, None) @@ -203,7 +203,7 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ try: obj = cls(**data) except TypeError as exc: - if str(exc).startswith("__init__() got an unexpected keyword argument"): + if "__init__() got an unexpected keyword argument" in str(exc): kwargs: JSONDict = {} api_kwargs: JSONDict = {} for key, value in data.items(): diff --git a/tests/conftest.py b/tests/conftest.py index 2515d6a58b9..d8ec91ddb48 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -281,15 +281,16 @@ def make_message(text, **kwargs): bot = kwargs.pop("bot", None) if bot is None: bot = make_bot(get_bot()) - return Message( + message = Message( message_id=1, from_user=kwargs.pop("user", User(id=1, first_name="", is_bot=False)), date=kwargs.pop("date", DATE), chat=kwargs.pop("chat", Chat(id=1, type="")), text=text, - bot=bot, **kwargs, ) + message.set_bot(bot) + return message def make_command_message(text, **kwargs): diff --git a/tests/test_application.py b/tests/test_application.py index 2b4630ef5e7..eb5b741b116 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -646,9 +646,9 @@ async def start3(b, u): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ), ) + update.message.set_bot(bot) async with app: # If ApplicationHandlerStop raised handlers in other groups should not be called. @@ -736,9 +736,9 @@ async def error(u, c): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ), ) + update.message.set_bot(bot) async with app: # If an unhandled exception was caught, no further handlers from the same group should diff --git a/tests/test_botcommandscope.py b/tests/test_botcommandscope.py index 46efcc9501b..7a52c08fe37 100644 --- a/tests/test_botcommandscope.py +++ b/tests/test_botcommandscope.py @@ -108,7 +108,10 @@ def scope_class_and_type(request): @pytest.fixture(scope="class") def bot_command_scope(scope_class_and_type, chat_id): - return scope_class_and_type[0](type=scope_class_and_type[1], chat_id=chat_id, user_id=42) + # 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 + ) # All the scope types are very similar, so we test everything via parametrization diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index 60538cffe14..933ea80eda9 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -31,8 +31,8 @@ def callback_query(bot, request): TestCallbackQuery.chat_instance, data=TestCallbackQuery.data, game_short_name=TestCallbackQuery.game_short_name, - bot=bot, ) + cbq.set_bot(bot) if request.param == "message": cbq.message = TestCallbackQuery.message cbq.message.set_bot(bot) diff --git a/tests/test_chatjoinrequest.py b/tests/test_chatjoinrequest.py index 97fbaa1272b..fe4aa190404 100644 --- a/tests/test_chatjoinrequest.py +++ b/tests/test_chatjoinrequest.py @@ -33,14 +33,15 @@ def time(): @pytest.fixture(scope="class") def chat_join_request(bot, time): - return ChatJoinRequest( + cjr = ChatJoinRequest( chat=TestChatJoinRequest.chat, from_user=TestChatJoinRequest.from_user, date=time, bio=TestChatJoinRequest.bio, invite_link=TestChatJoinRequest.invite_link, - bot=bot, ) + cjr.set_bot(bot) + return cjr class TestChatJoinRequest: diff --git a/tests/test_chatjoinrequesthandler.py b/tests/test_chatjoinrequesthandler.py index 35cd3aaec25..82ca178d430 100644 --- a/tests/test_chatjoinrequesthandler.py +++ b/tests/test_chatjoinrequesthandler.py @@ -76,7 +76,7 @@ def time(): @pytest.fixture(scope="class") def chat_join_request(time, bot): - return ChatJoinRequest( + cjr = ChatJoinRequest( chat=Chat(1, Chat.SUPERGROUP), from_user=User(2, "first_name", False), date=time, @@ -89,8 +89,9 @@ def chat_join_request(time, bot): is_revoked=False, is_primary=False, ), - bot=bot, ) + cjr.set_bot(bot) + return cjr @pytest.fixture(scope="function") diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 86ef2ad24a5..7cda31a5873 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -517,8 +517,8 @@ async def callback(_, __): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -568,8 +568,8 @@ async def test_conversation_handler_end(self, caplog, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) @@ -607,8 +607,8 @@ async def test_conversation_handler_fallback(self, app, bot, user1, user2): from_user=user1, text="/eat", entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/eat"))], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) @@ -659,8 +659,8 @@ async def callback(_, __): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) try: @@ -692,8 +692,8 @@ async def test_conversation_handler_per_chat(self, app, bot, user1, user2): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) @@ -738,8 +738,8 @@ async def test_conversation_handler_per_user(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) # First check that updates without user won't be handled message.from_user = None @@ -796,10 +796,12 @@ async def two(update, context): # User one, starts the state machine. message = ( - Message(0, None, self.group, from_user=user1, text="msg w/ inlinekeyboard", bot=bot) + Message(0, None, self.group, from_user=user1, text="msg w/ inlinekeyboard") if not inline else None ) + if message: + message.set_bot(bot) inline_message_id = "42" if inline else None async with app: @@ -809,18 +811,18 @@ async def two(update, context): None, message=message, data="1", - bot=bot, inline_message_id=inline_message_id, ) + cbq_1.set_bot(bot) cbq_2 = CallbackQuery( 0, user1, None, message=message, data="2", - bot=bot, inline_message_id=inline_message_id, ) + cbq_2.set_bot(bot) await app.process_update(Update(update_id=0, callback_query=cbq_1)) # Make sure that we're in the correct state @@ -858,8 +860,8 @@ async def test_end_on_first_message(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) assert handler.check_update(Update(update_id=0, message=message)) @@ -884,8 +886,8 @@ async def test_end_on_first_message_non_blocking_handler(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) # give the task a chance to finish @@ -903,7 +905,8 @@ async def test_none_on_first_message(self, app, bot, user1): app.add_handler(handler) # User starts the state machine and a callback function returns None - message = Message(0, None, self.group, from_user=user1, text="/start", bot=bot) + message = Message(0, None, self.group, from_user=user1, text="/start") + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) # Check that the same message is accepted again, i.e. the conversation immediately @@ -929,8 +932,8 @@ async def test_none_on_first_message_non_blocking_handler(self, app, bot, user1) entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) # Give the task a chance to finish @@ -945,7 +948,8 @@ async def test_per_chat_message_without_chat(self, bot, user1): handler = ConversationHandler( entry_points=[CommandHandler("start", self.start_end)], states={}, fallbacks=[] ) - cbq = CallbackQuery(0, user1, None, None, bot=bot) + cbq = CallbackQuery(0, user1, None, None) + cbq.set_bot(bot) update = Update(0, callback_query=cbq) assert not handler.check_update(update) @@ -953,7 +957,8 @@ async def test_channel_message_without_chat(self, bot): handler = ConversationHandler( entry_points=[MessageHandler(filters.ALL, self.start_end)], states={}, fallbacks=[] ) - message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, "Misses Test"), bot=bot) + message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, "Misses Test")) + message.set_bot(bot) update = Update(0, channel_post=message) assert not handler.check_update(update) @@ -965,12 +970,18 @@ async def test_all_update_types(self, app, bot, user1): handler = ConversationHandler( entry_points=[CommandHandler("start", self.start_end)], states={}, fallbacks=[] ) - message = Message(0, None, self.group, from_user=user1, text="ignore", bot=bot) - callback_query = CallbackQuery(0, user1, None, message=message, data="data", bot=bot) - chosen_inline_result = ChosenInlineResult(0, user1, "query", bot=bot) - inline_query = InlineQuery(0, user1, "query", 0, bot=bot) - pre_checkout_query = PreCheckoutQuery(0, user1, "USD", 100, [], bot=bot) - shipping_query = ShippingQuery(0, user1, [], None, bot=bot) + message = Message(0, None, self.group, from_user=user1, text="ignore") + message.set_bot(bot) + callback_query = CallbackQuery(0, user1, None, message=message, data="data") + callback_query.set_bot(bot) + chosen_inline_result = ChosenInlineResult(0, user1, "query") + chosen_inline_result.set_bot(bot) + inline_query = InlineQuery(0, user1, "query") + inline_query.set_bot(bot) + pre_checkout_query = PreCheckoutQuery(0, user1, "USD", 100, []) + pre_checkout_query.set_bot(bot) + shipping_query = ShippingQuery(0, user1, [], None) + shipping_query.set_bot(bot) assert not handler.check_update(Update(0, callback_query=callback_query)) assert not handler.check_update(Update(0, chosen_inline_result=chosen_inline_result)) assert not handler.check_update(Update(0, inline_query=inline_query)) @@ -1002,8 +1013,8 @@ async def test_no_running_job_queue_warning(self, app, bot, user1, recwarn, jq): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1040,8 +1051,8 @@ class DictJB(JobQueue): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.start() @@ -1088,8 +1099,8 @@ async def raise_error(*a, **kw): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) # start the conversation async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1136,8 +1147,8 @@ async def raise_error(*a, **kw): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) # start the conversation async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1172,8 +1183,8 @@ async def test_conversation_timeout(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + start_message.set_bot(bot) brew_message = Message( 0, None, @@ -1183,8 +1194,8 @@ async def test_conversation_timeout(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/brew")) ], - bot=bot, ) + brew_message.set_bot(bot) pour_coffee_message = Message( 0, None, @@ -1194,8 +1205,8 @@ async def test_conversation_timeout(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/pourCoffee")) ], - bot=bot, ) + pour_coffee_message.set_bot(bot) async with app: await app.start() @@ -1242,8 +1253,8 @@ def timeout(*a, **kw): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: # start the conversation await app.process_update(Update(update_id=0, message=message)) @@ -1287,8 +1298,8 @@ def timeout(*args, **kwargs): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) brew_message = Message( 0, None, @@ -1298,8 +1309,8 @@ def timeout(*args, **kwargs): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/brew")) ], - bot=bot, ) + brew_message.set_bot(bot) async with app: await app.start() @@ -1335,8 +1346,8 @@ async def start_callback(u, c): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) update = Update(update_id=0, message=message) async def timeout_callback(u, c): @@ -1394,8 +1405,8 @@ async def test_conversation_timeout_keeps_extending(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.start() @@ -1445,8 +1456,8 @@ async def test_conversation_timeout_two_users(self, app, bot, user1, user2): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.start() @@ -1505,8 +1516,8 @@ async def test_conversation_handler_timeout_state(self, app, bot, user1): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.start() @@ -1578,8 +1589,8 @@ async def test_conversation_handler_timeout_state_context(self, app, bot, user1) entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.start() @@ -1660,8 +1671,8 @@ async def slowbrew(_update, context): entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], - bot=bot, ) + message.set_bot(bot) async with app: await app.start() @@ -1706,11 +1717,11 @@ async def test_nested_conversation_handler(self, app, bot, user1, user2): self.group, from_user=user1, text="/start", - bot=bot, entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -1833,12 +1844,12 @@ def test_callback(u, c): None, self.group, text="/start", - bot=bot, from_user=user1, entities=[ MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], ) + message.set_bot(bot) async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -2094,8 +2105,10 @@ async def callback(_, __): app.add_handler(conv_handler) async with app: - start_message = make_command_message("/start", bot=bot) - fallback_message = make_command_message("/fallback", bot=bot) + start_message = make_command_message("/start") + start_message.set_bot(bot) + fallback_message = make_command_message("/fallback") + fallback_message.set_bot(bot) # This loop makes sure that we test all of entry points, states handler & fallbacks for message in [start_message, start_message, fallback_message]: diff --git a/tests/test_file.py b/tests/test_file.py index ac06936f744..fd4f3b8cda8 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -30,24 +30,26 @@ @pytest.fixture(scope="class") def file(bot): - return File( + file = File( TestFile.file_id, TestFile.file_unique_id, file_path=TestFile.file_path, file_size=TestFile.file_size, - bot=bot, ) + file.set_bot(bot) + return file @pytest.fixture(scope="class") def local_file(bot): - return File( + file = File( TestFile.file_id, TestFile.file_unique_id, file_path=str(data_file("local_file.txt")), file_size=TestFile.file_size, - bot=bot, ) + file.set_bot(bot) + return file class TestFile: diff --git a/tests/test_inlinequery.py b/tests/test_inlinequery.py index b58efaacb7e..75cb9803a57 100644 --- a/tests/test_inlinequery.py +++ b/tests/test_inlinequery.py @@ -25,14 +25,15 @@ @pytest.fixture(scope="class") def inline_query(bot): - return InlineQuery( + ilq = InlineQuery( TestInlineQuery.id_, TestInlineQuery.from_user, TestInlineQuery.query, TestInlineQuery.offset, location=TestInlineQuery.location, - bot=bot, ) + ilq.set_bot(bot) + return ilq class TestInlineQuery: diff --git a/tests/test_inlinequeryresultvoice.py b/tests/test_inlinequeryresultvoice.py index 75c7486c776..03058e37507 100644 --- a/tests/test_inlinequeryresultvoice.py +++ b/tests/test_inlinequeryresultvoice.py @@ -31,7 +31,6 @@ @pytest.fixture(scope="class") def inline_query_result_voice(): return InlineQueryResultVoice( - type=TestInlineQueryResultVoice.type_, id=TestInlineQueryResultVoice.id_, voice_url=TestInlineQueryResultVoice.voice_url, title=TestInlineQueryResultVoice.title, diff --git a/tests/test_message.py b/tests/test_message.py index 8f3fb6fcf7a..a65dba72cb8 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -61,13 +61,14 @@ @pytest.fixture(scope="class") def message(bot): - return Message( + message = Message( message_id=TestMessage.id_, date=TestMessage.date, chat=TestMessage.chat, from_user=TestMessage.from_user, - bot=bot, ) + message.set_bot(bot) + return message @pytest.fixture( @@ -83,7 +84,7 @@ def message(bot): {"edit_date": datetime.utcnow()}, { "text": "a text message", - "enitites": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)], + "entites": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)], }, { "caption": "A message caption", @@ -247,14 +248,15 @@ def message(bot): ], ) def message_params(bot, request): - return Message( + message = Message( message_id=TestMessage.id_, from_user=TestMessage.from_user, date=TestMessage.date, chat=TestMessage.chat, - bot=bot, **request.param, ) + message.set_bot(bot) + return message class TestMessage: diff --git a/tests/test_messagehandler.py b/tests/test_messagehandler.py index 42f91aa8d1b..daf8a451588 100644 --- a/tests/test_messagehandler.py +++ b/tests/test_messagehandler.py @@ -64,7 +64,9 @@ def false_update(request): @pytest.fixture(scope="class") def message(bot): - return Message(1, None, Chat(1, ""), from_user=User(1, "", False), bot=bot) + message = Message(1, None, Chat(1, ""), from_user=User(1, "", False)) + message.set_bot(bot) + return message class TestMessageHandler: diff --git a/tests/test_passportfile.py b/tests/test_passportfile.py index bb748e205e7..1df1f6e10bb 100644 --- a/tests/test_passportfile.py +++ b/tests/test_passportfile.py @@ -24,13 +24,14 @@ @pytest.fixture(scope="class") def passport_file(bot): - return PassportFile( + pf = PassportFile( file_id=TestPassportFile.file_id, file_unique_id=TestPassportFile.file_unique_id, file_size=TestPassportFile.file_size, file_date=TestPassportFile.file_date, - bot=bot, ) + pf.set_bot(bot) + return pf class TestPassportFile: diff --git a/tests/test_precheckoutquery.py b/tests/test_precheckoutquery.py index 815238a6b00..7730752e712 100644 --- a/tests/test_precheckoutquery.py +++ b/tests/test_precheckoutquery.py @@ -25,7 +25,7 @@ @pytest.fixture(scope="class") def pre_checkout_query(bot): - return PreCheckoutQuery( + pcq = PreCheckoutQuery( TestPreCheckoutQuery.id_, TestPreCheckoutQuery.from_user, TestPreCheckoutQuery.currency, @@ -33,8 +33,9 @@ def pre_checkout_query(bot): TestPreCheckoutQuery.invoice_payload, shipping_option_id=TestPreCheckoutQuery.shipping_option_id, order_info=TestPreCheckoutQuery.order_info, - bot=bot, ) + pcq.set_bot(bot) + return pcq class TestPreCheckoutQuery: diff --git a/tests/test_user.py b/tests/test_user.py index 8f6b0cf1a4f..fbf85b09d8d 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -42,7 +42,7 @@ def json_dict(): @pytest.fixture(scope="function") def user(bot): - return User( + user = User( id=TestUser.id_, first_name=TestUser.first_name, is_bot=TestUser.is_bot, @@ -52,10 +52,11 @@ def user(bot): can_join_groups=TestUser.can_join_groups, can_read_all_group_messages=TestUser.can_read_all_group_messages, supports_inline_queries=TestUser.supports_inline_queries, - bot=bot, is_premium=TestUser.is_premium, added_to_attachment_menu=TestUser.added_to_attachment_menu, ) + user.set_bot(bot) + return user class TestUser: From 55a7c948bc8865e66ad468018b358788ee1cb88c Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:47:48 +0200 Subject: [PATCH 08/90] More tests --- telegram/_botcommandscope.py | 2 +- telegram/_chat.py | 15 +++++++++------ telegram/_chatmember.py | 4 ++-- telegram/_menubutton.py | 2 +- telegram/_telegramobject.py | 11 +++++++++-- tests/test_chat.py | 11 ++++++----- tests/test_chatmember.py | 2 +- tests/test_chatmemberupdated.py | 2 +- tests/test_conversationhandler.py | 2 +- tests/test_menubutton.py | 10 ++++++++-- tests/test_message.py | 4 +--- tests/test_picklepersistence.py | 7 +++++-- tests/test_shippingquery.py | 7 ++++--- tests/test_telegramobject.py | 12 ++++++++---- 14 files changed, 57 insertions(+), 34 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 0fa902ee9b0..7cf91dee695 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -108,7 +108,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc cls.CHAT_MEMBER: BotCommandScopeChatMember, } - if cls is BotCommandScope and data["type"] in _class_mapping: + if cls is BotCommandScope and data.get("type") in _class_mapping: return _class_mapping[data["type"]].de_json(data=data, bot=bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_chat.py b/telegram/_chat.py index c6a765cf800..7489de670f7 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -220,7 +220,6 @@ class Chat(TelegramObject): "title", "photo", "linked_chat_id", - "all_members_are_administrators", "message_auto_delete_time", "has_protected_content", "has_private_forwards", @@ -279,10 +278,6 @@ def __init__( self.username = username self.first_name = first_name self.last_name = last_name - # TODO: Remove (also from tests), when Telegram drops this completely - self.all_members_are_administrators = ( - api_kwargs.pop("all_members_are_administrators", None) if api_kwargs else None - ) self.photo = photo self.bio = bio self.has_private_forwards = has_private_forwards @@ -347,7 +342,15 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]: data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot) data["location"] = ChatLocation.de_json(data.get("location"), bot) - return super().de_json(data=data, bot=bot) + api_kwargs = {} + # This is a deprecated field that TG still returns for backwards compatibility + # Let's filter it out to speed up the de-json process + if "all_members_are_administrators" in data: + api_kwargs["all_members_are_administrators"] = data.pop( + "all_members_are_administrators" + ) + + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) async def leave( self, diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index 18d346dfa36..877bc613c68 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -113,8 +113,8 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"] cls.BANNED: ChatMemberBanned, } - if cls is ChatMember: - return _class_mapping.get(data.pop("status"), cls).de_json(data=data, bot=bot) + if cls is ChatMember and data.get("status") in _class_mapping: + return _class_mapping[data["status"]].de_json(data=data, bot=bot) data["user"] = User.de_json(data.get("user"), bot) data["until_date"] = from_timestamp(data.get("until_date", None)) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 4b83630f18c..85a457510c9 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -86,7 +86,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] cls.DEFAULT: MenuButtonDefault, } - if cls is MenuButton and data["type"] in _class_mapping: + if cls is MenuButton and data.get("type") in _class_mapping: return _class_mapping[data["type"]].de_json(data, bot=bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 7834da057f1..08759362dc5 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -192,6 +192,12 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ The Telegram object. """ + return cls._de_json(data=data, bot=bot) + + @classmethod + def _de_json( + cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot", api_kwargs: JSONDict = None + ) -> Optional[TO_co]: if cls.__INIT_PARAMS is None: signature = inspect.signature(cls) cls.__INIT_PARAMS = set(signature.parameters.keys()) @@ -199,13 +205,14 @@ def de_json(cls: Type[TO_co], data: Optional[JSONDict], bot: "Bot") -> Optional[ if data is None: return None + api_kwargs = api_kwargs or {} + # try-except is significantly faster in case we already have a correct argument set try: - obj = cls(**data) + obj = cls(**data, api_kwargs=api_kwargs) except TypeError as exc: if "__init__() got an unexpected keyword argument" in str(exc): kwargs: JSONDict = {} - api_kwargs: JSONDict = {} for key, value in data.items(): if key in cls.__INIT_PARAMS: # pylint: disable=unsupported-membership-test kwargs[key] = value diff --git a/tests/test_chat.py b/tests/test_chat.py index b3c89eff8db..1da9f4ae1ea 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -26,13 +26,11 @@ @pytest.fixture(scope="class") def chat(bot): - return Chat( + chat = Chat( TestChat.id_, title=TestChat.title, type=TestChat.type_, username=TestChat.username, - all_members_are_administrators=TestChat.all_members_are_administrators, - bot=bot, sticker_set_name=TestChat.sticker_set_name, can_set_sticker_set=TestChat.can_set_sticker_set, permissions=TestChat.permissions, @@ -46,6 +44,8 @@ def chat(bot): join_by_request=True, has_restricted_voice_and_video_messages=True, ) + chat.set_bot(bot) + return chat class TestChat: @@ -104,7 +104,6 @@ def test_de_json(self, bot): assert chat.title == self.title assert chat.type == self.type_ assert chat.username == self.username - assert chat.all_members_are_administrators == self.all_members_are_administrators assert chat.sticker_set_name == self.sticker_set_name assert chat.can_set_sticker_set == self.can_set_sticker_set assert chat.permissions == self.permissions @@ -121,6 +120,9 @@ def test_de_json(self, bot): chat.has_restricted_voice_and_video_messages == self.has_restricted_voice_and_video_messages ) + assert chat.api_kwargs == { + "all_members_are_administrators": self.all_members_are_administrators + } def test_to_dict(self, chat): chat_dict = chat.to_dict() @@ -130,7 +132,6 @@ def test_to_dict(self, chat): assert chat_dict["title"] == chat.title assert chat_dict["type"] == chat.type assert chat_dict["username"] == chat.username - assert chat_dict["all_members_are_administrators"] == chat.all_members_are_administrators assert chat_dict["permissions"] == chat.permissions.to_dict() assert chat_dict["slow_mode_delay"] == chat.slow_mode_delay assert chat_dict["bio"] == chat.bio diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py index fda62ce8e08..9717ed0a0ba 100644 --- a/tests/test_chatmember.py +++ b/tests/test_chatmember.py @@ -35,7 +35,7 @@ ) from telegram._utils.datetime import to_timestamp -ignored = ["self", "_kwargs"] +ignored = ["self", "api_kwargs"] class CMDefaults: diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index b21760772be..77ecf58a98c 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -227,7 +227,7 @@ def test_difference_required(self, user, chat): [ name for name, param in inspect.signature(ChatMember).parameters.items() - if name != "self" and param.default != inspect.Parameter.empty + if name not in ["self", "api_kwargs"] and param.default != inspect.Parameter.empty ], ) def test_difference_optionals(self, optional_attribute, user, chat): diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 7cda31a5873..e4274c10f5e 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -976,7 +976,7 @@ async def test_all_update_types(self, app, bot, user1): callback_query.set_bot(bot) chosen_inline_result = ChosenInlineResult(0, user1, "query") chosen_inline_result.set_bot(bot) - inline_query = InlineQuery(0, user1, "query") + inline_query = InlineQuery(0, user1, "query", offset="") inline_query.set_bot(bot) pre_checkout_query = PreCheckoutQuery(0, user1, "USD", 100, []) pre_checkout_query.set_bot(bot) diff --git a/tests/test_menubutton.py b/tests/test_menubutton.py index 4e3047f0d07..413116003c0 100644 --- a/tests/test_menubutton.py +++ b/tests/test_menubutton.py @@ -78,8 +78,14 @@ def scope_class_and_type(request): @pytest.fixture(scope="class") def menu_button(scope_class_and_type): - return scope_class_and_type[0]( - type=scope_class_and_type[1], text=TestMenuButton.text, web_app=TestMenuButton.web_app + # 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=TestMenuButton.text, + web_app=TestMenuButton.web_app.to_dict(), + ), + bot=None, ) diff --git a/tests/test_message.py b/tests/test_message.py index a65dba72cb8..49a05366832 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -84,7 +84,7 @@ def message(bot): {"edit_date": datetime.utcnow()}, { "text": "a text message", - "entites": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)], + "entities": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)], }, { "caption": "A message caption", @@ -165,7 +165,6 @@ def message(bot): ] }, }, - {"quote": True}, {"dice": Dice(4, "🎲")}, {"via_bot": User(9, "A_Bot", True)}, { @@ -232,7 +231,6 @@ def message(bot): "passport_data", "poll", "reply_markup", - "default_quote", "dice", "via_bot", "proximity_alert_triggered", diff --git a/tests/test_picklepersistence.py b/tests/test_picklepersistence.py index f267f73243f..6e5041a5702 100644 --- a/tests/test_picklepersistence.py +++ b/tests/test_picklepersistence.py @@ -227,7 +227,8 @@ def pickle_files_wo_callback_data(user_data, chat_data, bot_data, conversations) def update(bot): user = User(id=321, first_name="test_user", is_bot=False) chat = Chat(id=123, type="group") - message = Message(1, datetime.datetime.now(), chat, from_user=user, text="Hi there", bot=bot) + message = Message(1, datetime.datetime.now(), chat, from_user=user, text="Hi there") + message.set_bot(bot) return Update(0, message=message) @@ -877,7 +878,9 @@ async def test_custom_pickler_unpickler_simple( assert not len(recwarn) data_with_bot = {} async with make_bot(token=bot.token) as other_bot: - data_with_bot["unknown_bot_in_user"] = User(1, "Dev", False, bot=other_bot) + user = User(1, "Dev", False) + user.set_bot(other_bot) + data_with_bot["unknown_bot_in_user"] = user await pickle_persistence.update_chat_data(12345, data_with_bot) assert len(recwarn) == 1 assert recwarn[-1].category is PTBUserWarning diff --git a/tests/test_shippingquery.py b/tests/test_shippingquery.py index 9b1878b30fb..5ef4d2f4eb3 100644 --- a/tests/test_shippingquery.py +++ b/tests/test_shippingquery.py @@ -25,17 +25,18 @@ @pytest.fixture(scope="class") def shipping_query(bot): - return ShippingQuery( + sq = ShippingQuery( TestShippingQuery.id_, TestShippingQuery.from_user, TestShippingQuery.invoice_payload, TestShippingQuery.shipping_address, - bot=bot, ) + sq.set_bot(bot) + return sq class TestShippingQuery: - id_ = 5 + id_ = "5" invoice_payload = "invoice_payload" from_user = User(0, "", False) shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1") diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index c721149a90a..0f54886007a 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -128,8 +128,10 @@ def test_pickle(self, bot): chat = Chat(2, Chat.PRIVATE) user = User(3, "first_name", False) date = datetime.datetime.now() - photo = PhotoSize("file_id", "unique", 21, 21, bot=bot) - msg = Message(1, date, chat, from_user=user, text="foobar", bot=bot, photo=[photo]) + photo = PhotoSize("file_id", "unique", 21, 21) + photo.set_bot(bot) + msg = Message(1, date, chat, from_user=user, text="foobar", photo=[photo]) + msg.set_bot(bot) # Test pickling of TGObjects, we choose Message since it's contains the most subclasses. assert msg.get_bot() @@ -147,8 +149,10 @@ def test_deepcopy_telegram_obj(self, bot): chat = Chat(2, Chat.PRIVATE) user = User(3, "first_name", False) date = datetime.datetime.now() - photo = PhotoSize("file_id", "unique", 21, 21, bot=bot) - msg = Message(1, date, chat, from_user=user, text="foobar", bot=bot, photo=[photo]) + photo = PhotoSize("file_id", "unique", 21, 21) + photo.set_bot(bot) + msg = Message(1, date, chat, from_user=user, text="foobar", photo=[photo]) + msg.set_bot(bot) new_msg = deepcopy(msg) From 674dd4ca69163f00df35971db24f525691e16ab0 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sat, 10 Sep 2022 22:51:07 +0200 Subject: [PATCH 09/90] Handle deprecated field in stickerset --- telegram/_files/sticker.py | 8 +++++++- tests/test_sticker.py | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 01367d68ba8..895e92eca55 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -273,7 +273,13 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["StickerSet"] data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) data["stickers"] = Sticker.de_list(data.get("stickers"), bot) - return super().de_json(data=data, bot=bot) + api_kwargs = {} + # This is a deprecated field that TG still returns for backwards compatibility + # Let's filter it out to speed up the de-json process + if "contains_masks" in data: + api_kwargs["contains_masks"] = data.pop("contains_masks") + + return super()._de_json(data=data, bot=bot, api_kwargs=api_kwargs) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/tests/test_sticker.py b/tests/test_sticker.py index 80fc47f265b..0db8ff1b7bf 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -477,6 +477,7 @@ class TestStickerSet: stickers = [Sticker("file_id", "file_un_id", 512, 512, True, True, Sticker.REGULAR)] name = "NOTAREALNAME" sticker_type = Sticker.REGULAR + contains_masks = True def test_de_json(self, bot, sticker): name = f"test_by_{bot.username}" @@ -488,6 +489,7 @@ def test_de_json(self, bot, sticker): "stickers": [x.to_dict() for x in self.stickers], "thumb": sticker.thumb.to_dict(), "sticker_type": self.sticker_type, + "contains_masks": self.contains_masks, } sticker_set = StickerSet.de_json(json_dict, bot) @@ -498,6 +500,7 @@ def test_de_json(self, bot, sticker): assert sticker_set.stickers == self.stickers assert sticker_set.thumb == sticker.thumb assert sticker_set.sticker_type == self.sticker_type + assert sticker_set.api_kwargs == {"contains_masks": self.contains_masks} async def test_create_sticker_set( self, bot, chat_id, sticker_file, animated_sticker_file, video_sticker_file From e8884e775054538139fe4405adfd754e5309631c Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 09:55:28 +0200 Subject: [PATCH 10/90] Finish on existing tests --- telegram/_telegramobject.py | 2 +- tests/test_telegramobject.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 08759362dc5..db692e372b3 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -169,7 +169,7 @@ def _get_attrs( if recursive and data.get("from_user"): data["from"] = data.pop("from_user", None) if remove_bot: - data.pop("_bot", None) + data["_bot"] = None return data @staticmethod diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 0f54886007a..4a670a84051 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -28,6 +28,7 @@ class TestTelegramObject: class Sub(TelegramObject): def __init__(self, private, normal, b): + super().__init__() self._private = private self.normal = normal self._bot = b @@ -156,6 +157,9 @@ def test_deepcopy_telegram_obj(self, bot): new_msg = deepcopy(msg) + assert new_msg == msg + assert new_msg is not msg + # The same bot should be present when deepcopying. assert new_msg.get_bot() == bot and new_msg.get_bot() is bot From 07a300101c143f3a2c85cf3b8288bb0f4a8709ed Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 10:12:12 +0200 Subject: [PATCH 11/90] include api_kwargs in to_dict --- telegram/_telegramobject.py | 8 ++++++-- tests/test_telegramobject.py | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index db692e372b3..6b699881f73 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -154,7 +154,7 @@ def _get_attrs( # and then get their attributes. The `[:-1]` slice excludes the `object` class for cls in self.__class__.__mro__[:-1]: for key in cls.__slots__: # type: ignore[attr-defined] - if (not include_private and key.startswith("_")) or (key == "api_kwargs"): + if not include_private and key.startswith("_"): continue value = getattr(self, key, None) @@ -246,6 +246,7 @@ def de_list( def to_json(self) -> str: """Gives a JSON representation of object. + This includes all entries of :attr:`api_kwargs`. Returns: :obj:`str` @@ -254,11 +255,14 @@ def to_json(self) -> str: def to_dict(self) -> JSONDict: """Gives representation of object as :obj:`dict`. + This includes all entries of :attr:`api_kwargs`. Returns: :obj:`dict` """ - return self._get_attrs(recursive=True) + out = self._get_attrs(recursive=True) + out.update(out.pop("api_kwargs", {})) # type: ignore[call-overload] + return out def get_bot(self) -> "Bot": """Returns the :class:`telegram.Bot` instance associated with this object. diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 4a670a84051..5e91931c072 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -59,12 +59,17 @@ class TelegramObjectSubclass(TelegramObject): __slots__ = ("a", "_b") # Added slots so that the attrs are converted to dict def __init__(self): + super().__init__() self.a = 1 self._b = 2 subclass_instance = TelegramObjectSubclass() assert subclass_instance.to_dict() == {"a": 1} + def test_to_dict_api_kwargs(self): + to = TelegramObject(api_kwargs={"foo": "bar"}) + assert to.to_dict() == {"foo": "bar"} + def test_slot_behaviour(self, mro_slots): inst = TelegramObject() for attr in inst.__slots__: From 0e0da1ab67cfa0be904b92f06972963471c9bef4 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 10:22:23 +0200 Subject: [PATCH 12/90] API kwargs on unpickling --- telegram/_telegramobject.py | 1 + tests/test_telegramobject.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 6b699881f73..e5eafc6ff3e 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -106,6 +106,7 @@ def __setstate__(self, state: dict) -> None: """ for key, val in state.items(): setattr(self, key, val) + self._apply_api_kwargs() def __deepcopy__(self: TO_co, memodict: dict) -> TO_co: """This method deepcopies the object and sets the bot on the newly created copy.""" diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 5e91931c072..f91b1d3f2b5 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -33,6 +33,11 @@ def __init__(self, private, normal, b): self.normal = normal self._bot = b + class ChangingTO(TelegramObject): + # Don't use in any tests, this is just for testing the pickle behaviour and the + # class is altered during the test procedure + pass + def test_to_json(self, monkeypatch): # to_json simply takes whatever comes from to_dict, therefore we only need to test it once telegram_object = TelegramObject() @@ -151,6 +156,18 @@ def test_pickle(self, bot): assert unpickled.date == date assert unpickled.photo[0] == photo + def test_pickle_apply_api_kwargs(self, bot): + """Makes sure that when a class gets new attributes, the api_kwargs are moved to the + new attributes on unpickling.""" + obj = self.ChangingTO(api_kwargs={"foo": "bar"}) + pickled = pickle.dumps(obj) + + self.ChangingTO.foo = None + obj = pickle.loads(pickled) + + assert obj.foo == "bar" + assert obj.api_kwargs == {} + def test_deepcopy_telegram_obj(self, bot): chat = Chat(2, Chat.PRIVATE) user = User(3, "first_name", False) From 3789e592a91f0422ea579f120385013e3a17619e Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 10:33:06 +0200 Subject: [PATCH 13/90] deepsource --- telegram/_keyboardbuttonpolltype.py | 2 +- telegram/_passport/credentials.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index f874f74621c..78d1b94beae 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -41,7 +41,7 @@ class KeyboardButtonPollType(TelegramObject): __slots__ = ("type",) def __init__( - self, type: str = None, api_kwargs: JSONDict = None + self, type: str = None, api_kwargs: JSONDict = None # skipcq: PYL-W0622 ): # pylint: disable=redefined-builtin super().__init__(api_kwargs=api_kwargs) self.type = type diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index a67dd453846..f6c5bfd7ef9 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -46,7 +46,7 @@ @no_type_check -def decrypt(secret, hash, data): +def decrypt(secret, hash, data): # skipcq: PYL-W0622 """ Decrypt per telegram docs at https://core.telegram.org/passport. @@ -95,7 +95,7 @@ def decrypt(secret, hash, data): @no_type_check -def decrypt_json(secret, hash, data): +def decrypt_json(secret, hash, data): # skipcq: PYL-W0622 """Decrypts data using secret and hash and then decodes utf-8 string and loads json""" return json.loads(decrypt(secret, hash, data).decode("utf-8")) @@ -140,7 +140,7 @@ class EncryptedCredentials(TelegramObject): def __init__( self, data: str, - hash: str, + hash: str, # skipcq: PYL-W0622 secret: str, api_kwargs: JSONDict = None, ): @@ -416,7 +416,7 @@ class _CredentialsBase(TelegramObject): __slots__ = ("hash", "secret", "file_hash", "data_hash") - def __init__(self, hash: str, secret: str, api_kwargs: JSONDict = None): + def __init__(self, hash: str, secret: str, api_kwargs: JSONDict = None): # skipcq: PYL-W0622 super().__init__(api_kwargs=api_kwargs) self.hash = hash self.secret = secret From 16be53cd81a05a97290929a14fc588af5ff91596 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 10:41:32 +0200 Subject: [PATCH 14/90] more deepsource --- telegram/_menubutton.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 85a457510c9..b61ea8a1b58 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -55,7 +55,7 @@ class MenuButton(TelegramObject): __slots__ = ("type",) def __init__( - self, type: str, api_kwargs: JSONDict = None + self, type: str, api_kwargs: JSONDict = None # skipcq: PYL-W0622 ): # pylint: disable=redefined-builtin super().__init__(api_kwargs=api_kwargs) self.type = type From f0f9a3faba5d7cef2a68ab42aaf3a38b5316e616 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 10:52:35 +0200 Subject: [PATCH 15/90] fix another failing test --- tests/test_picklepersistence.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_picklepersistence.py b/tests/test_picklepersistence.py index 6e5041a5702..909cf42ff0b 100644 --- a/tests/test_picklepersistence.py +++ b/tests/test_picklepersistence.py @@ -238,6 +238,7 @@ class TestPicklePersistence: class DictSub(TelegramObject): # Used for testing our custom (Un)Pickler. def __init__(self, private, normal, b): + super().__init__() self._private = private self.normal = normal self._bot = b @@ -246,6 +247,7 @@ class SlotsSub(TelegramObject): __slots__ = ("new_var", "_private") def __init__(self, new_var, private): + super().__init__() self.new_var = new_var self._private = private From f29da8dbe04bc4a67c88dbf35a3c4541c909c559 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 11:37:08 +0200 Subject: [PATCH 16/90] Documentation of TelegramObject --- docs/source/conf.py | 3 +++ docs/substitutions/global.rst | 5 +++++ telegram/_telegramobject.py | 19 +++++++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 docs/substitutions/global.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index 3227dcc5431..0d1049ba756 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -64,6 +64,9 @@ # The master toctree document. master_doc = "index" +# Global substitutions +rst_prolog = (Path.cwd() / "../substitutions/global.rst").read_text(encoding="utf-8") + # -- Extension settings ------------------------------------------------ napoleon_use_admonition_for_examples = True diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst new file mode 100644 index 00000000000..54461f93a3d --- /dev/null +++ b/docs/substitutions/global.rst @@ -0,0 +1,5 @@ +.. |toapikwargsbase| replace:: These arguments are also considered by :meth:`~telegram.TelegramObject.to_dict` and :meth:`~telegram.TelegramObject.to_json`, i.e. when passing objects to Telegram. Passing them to Telegram is however not guaranteed to work for all kinds of objects, e.g. this will fail for objects that can not directly be JSON serialized. + +.. |toapikwargsarg| replace:: Arbitrary keyword arguments. Can be used to store data for which there are no dedicated attributes. |toapikwargsbase| + +.. |toapikwargsattr| replace:: Optional. Arbitrary keyword arguments. Used to store data for which there are no dedicated attributes. |toapikwargsbase| \ No newline at end of file diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index e5eafc6ff3e..dc454539225 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -47,6 +47,17 @@ class TelegramObject: .. versionchanged:: 20.0 ``telegram_object['from']`` will look up the key ``from_user``. This is to account for special cases like :attr:`Message.from_user` that deviate from the official Bot API. + + Arguments: + api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| + + .. versionadded:: 20.0 + + Attributes: + api_kwargs (Dict[:obj:`str`, any]): |toapikwargsattr| + + .. versionadded:: 20.0 + """ __slots__ = ("_id_attrs", "_bot", "api_kwargs") @@ -247,7 +258,9 @@ def de_list( def to_json(self) -> str: """Gives a JSON representation of object. - This includes all entries of :attr:`api_kwargs`. + + .. versionchanged:: 20.0 + Now includes all entries of :attr:`api_kwargs`. Returns: :obj:`str` @@ -256,7 +269,9 @@ def to_json(self) -> str: def to_dict(self) -> JSONDict: """Gives representation of object as :obj:`dict`. - This includes all entries of :attr:`api_kwargs`. + + .. versionchanged:: 20.0 + Now includes all entries of :attr:`api_kwargs`. Returns: :obj:`dict` From 0789f21c790319924f487de69c8101405dcd2ea2 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 11 Sep 2022 13:36:38 +0200 Subject: [PATCH 17/90] More Documentation --- docs/substitutions/global.rst | 8 +++- telegram/_botcommand.py | 3 ++ telegram/_botcommandscope.py | 36 ++++++++++++----- telegram/_callbackquery.py | 4 +- telegram/_chat.py | 6 +-- telegram/_chatinvitelink.py | 2 +- telegram/_chatjoinrequest.py | 4 +- telegram/_chatlocation.py | 5 ++- telegram/_chatmember.py | 36 ++++++++++++----- telegram/_chatmemberupdated.py | 3 ++ telegram/_chatpermissions.py | 3 ++ telegram/_choseninlineresult.py | 4 +- telegram/_dice.py | 3 ++ telegram/_files/_basemedium.py | 4 +- telegram/_files/_basethumbedmedium.py | 4 +- telegram/_files/animation.py | 5 ++- telegram/_files/audio.py | 6 ++- telegram/_files/chatphoto.py | 3 ++ telegram/_files/contact.py | 4 +- telegram/_files/document.py | 5 ++- telegram/_files/file.py | 3 ++ telegram/_files/location.py | 4 +- telegram/_files/photosize.py | 5 ++- telegram/_files/sticker.py | 13 ++++-- telegram/_files/venue.py | 4 +- telegram/_files/video.py | 5 ++- telegram/_files/videonote.py | 6 +-- telegram/_files/voice.py | 6 +-- telegram/_forcereply.py | 8 ++-- telegram/_games/game.py | 3 ++ telegram/_inline/inlinekeyboardbutton.py | 7 ++-- telegram/_inline/inlinekeyboardmarkup.py | 7 ++-- telegram/_inline/inlinequery.py | 3 +- telegram/_inline/inlinequeryresult.py | 4 +- telegram/_inline/inlinequeryresultarticle.py | 1 - telegram/_inline/inlinequeryresultaudio.py | 1 - .../_inline/inlinequeryresultcachedaudio.py | 1 - .../inlinequeryresultcacheddocument.py | 1 - .../_inline/inlinequeryresultcachedgif.py | 1 - .../inlinequeryresultcachedmpeg4gif.py | 1 - .../_inline/inlinequeryresultcachedphoto.py | 1 - .../_inline/inlinequeryresultcachedsticker.py | 1 - .../_inline/inlinequeryresultcachedvideo.py | 1 - .../_inline/inlinequeryresultcachedvoice.py | 1 - telegram/_inline/inlinequeryresultcontact.py | 4 +- telegram/_inline/inlinequeryresultdocument.py | 4 +- telegram/_inline/inlinequeryresultgame.py | 4 +- telegram/_inline/inlinequeryresultgif.py | 4 +- telegram/_inline/inlinequeryresultlocation.py | 4 +- telegram/_inline/inlinequeryresultmpeg4gif.py | 4 +- telegram/_inline/inlinequeryresultphoto.py | 4 +- telegram/_inline/inlinequeryresultvenue.py | 4 +- telegram/_inline/inlinequeryresultvideo.py | 4 +- telegram/_inline/inlinequeryresultvoice.py | 6 ++- .../_inline/inputcontactmessagecontent.py | 4 +- .../_inline/inputinvoicemessagecontent.py | 4 +- .../_inline/inputlocationmessagecontent.py | 4 +- telegram/_inline/inputtextmessagecontent.py | 4 +- telegram/_inline/inputvenuemessagecontent.py | 4 +- telegram/_keyboardbutton.py | 6 +-- telegram/_keyboardbuttonpolltype.py | 3 ++ telegram/_loginurl.py | 3 ++ telegram/_menubutton.py | 2 - telegram/_message.py | 1 + telegram/_messageautodeletetimerchanged.py | 4 +- telegram/_messageentity.py | 4 +- telegram/_messageid.py | 8 +++- telegram/_passport/credentials.py | 22 +++++++++- telegram/_passport/data.py | 9 +++++ .../_passport/encryptedpassportelement.py | 3 ++ telegram/_passport/passportdata.py | 5 ++- telegram/_passport/passportelementerrors.py | 40 ++++++++++++++----- telegram/_passport/passportfile.py | 5 ++- telegram/_payment/invoice.py | 4 +- telegram/_payment/labeledprice.py | 4 +- telegram/_payment/orderinfo.py | 4 +- telegram/_payment/precheckoutquery.py | 5 ++- telegram/_payment/shippingaddress.py | 4 +- telegram/_payment/shippingoption.py | 4 +- telegram/_payment/shippingquery.py | 5 ++- telegram/_payment/successfulpayment.py | 4 +- telegram/_poll.py | 9 +++++ telegram/_proximityalerttriggered.py | 3 ++ telegram/_replykeyboardmarkup.py | 8 ++-- telegram/_replykeyboardremove.py | 5 ++- telegram/_telegramobject.py | 1 - telegram/_update.py | 5 ++- telegram/_user.py | 10 ++--- telegram/_userprofilephotos.py | 3 ++ telegram/_utils/defaultvalue.py | 1 - telegram/_videochat.py | 7 +--- telegram/_webhookinfo.py | 6 +-- telegram/ext/_basepersistence.py | 1 - telegram/ext/_callbackcontext.py | 1 - telegram/ext/_choseninlineresulthandler.py | 1 - telegram/ext/_dictpersistence.py | 1 - telegram/ext/_extbot.py | 1 - telegram/ext/_inlinequeryhandler.py | 1 - telegram/ext/_jobqueue.py | 2 - telegram/ext/_picklepersistence.py | 1 - 100 files changed, 355 insertions(+), 159 deletions(-) diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 54461f93a3d..5daba832814 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -2,4 +2,10 @@ .. |toapikwargsarg| replace:: Arbitrary keyword arguments. Can be used to store data for which there are no dedicated attributes. |toapikwargsbase| -.. |toapikwargsattr| replace:: Optional. Arbitrary keyword arguments. Used to store data for which there are no dedicated attributes. |toapikwargsbase| \ No newline at end of file +.. |toapikwargsattr| replace:: Optional. Arbitrary keyword arguments. Used to store data for which there are no dedicated attributes. |toapikwargsbase| + +.. |removedbot| replace:: Removed argument and attribute ``bot``. Use :meth:`~telegram.TelegramObject.set_bot` and :meth:`~telegram.TelegramObject.get_bot` instead. + +.. |removedkwargs| replace:: Removed the possibility to pass arbitrary keyword arguments. + +.. |removedbotandkwargs| replace:: |removedbot| |removedkwargs| \ No newline at end of file diff --git a/telegram/_botcommand.py b/telegram/_botcommand.py index 0aa44c62a78..91f6263142f 100644 --- a/telegram/_botcommand.py +++ b/telegram/_botcommand.py @@ -29,6 +29,9 @@ class BotCommand(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`command` and :attr:`description` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: command (:obj:`str`): Text of the command; 1-32 characters. Can contain only lowercase English letters, digits and underscores. diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 7cf91dee695..730bff40dfc 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -51,6 +51,9 @@ class BotCommandScope(TelegramObject): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): Scope type. @@ -121,6 +124,8 @@ class BotCommandScopeDefault(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.DEFAULT`. """ @@ -128,7 +133,7 @@ class BotCommandScopeDefault(BotCommandScope): __slots__ = () def __init__(self, api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.DEFAULT) + super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs) class BotCommandScopeAllPrivateChats(BotCommandScope): @@ -136,6 +141,8 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_PRIVATE_CHATS`. """ @@ -143,7 +150,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): __slots__ = () def __init__(self, api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS) + super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs) class BotCommandScopeAllGroupChats(BotCommandScope): @@ -151,6 +158,8 @@ class BotCommandScopeAllGroupChats(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_GROUP_CHATS`. """ @@ -158,7 +167,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope): __slots__ = () def __init__(self, api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.ALL_GROUP_CHATS) + super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs) class BotCommandScopeAllChatAdministrators(BotCommandScope): @@ -166,6 +175,8 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_CHAT_ADMINISTRATORS`. """ @@ -173,7 +184,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): __slots__ = () def __init__(self, api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS) + super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) class BotCommandScopeChat(BotCommandScope): @@ -184,10 +195,12 @@ class BotCommandScopeChat(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``) - Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT`. chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the @@ -197,7 +210,7 @@ class BotCommandScopeChat(BotCommandScope): __slots__ = ("chat_id",) def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.CHAT) + super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) @@ -213,10 +226,12 @@ class BotCommandScopeChatAdministrators(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``) - Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.CHAT_ADMINISTRATORS`. chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the @@ -226,7 +241,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): __slots__ = ("chat_id",) def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS) + super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) @@ -242,6 +257,9 @@ class BotCommandScopeChatMember(BotCommandScope): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``) @@ -257,7 +275,7 @@ class BotCommandScopeChatMember(BotCommandScope): __slots__ = ("chat_id", "user_id") def __init__(self, chat_id: Union[str, int], user_id: int, api_kwargs: JSONDict = None): - super().__init__(type=BotCommandScope.CHAT_MEMBER) + super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index afd0816bdce..240904b99c9 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -65,6 +65,9 @@ class CallbackQuery(TelegramObject): .. versionadded:: 13.6 + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this query. from_user (:class:`telegram.User`): Sender. @@ -80,7 +83,6 @@ class CallbackQuery(TelegramObject): game_short_name (:obj:`str`, optional): Short name of a Game to be returned, serves as the unique identifier for the game - Attributes: id (:obj:`str`): Unique identifier for this query. from_user (:class:`telegram.User`): Sender. diff --git a/telegram/_chat.py b/telegram/_chat.py index 7489de670f7..7d591343acf 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -73,6 +73,9 @@ class Chat(TelegramObject): ``api_kwargs``. Use a named argument for those, and notice that some positional arguments changed position as a result. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. @@ -139,9 +142,6 @@ class Chat(TelegramObject): in the private chat. Returned only in :meth:`telegram.Bot.get_chat`. .. versionadded:: 20.0 - - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: id (:obj:`int`): Unique identifier for this chat. type (:obj:`str`): Type of chat. diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index e3b96254eb6..8adf692814d 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -42,6 +42,7 @@ class ChatInviteLink(TelegramObject): * The argument & attribute :attr:`creates_join_request` is now required to comply with the Bot API. * Comparing objects of this class now also takes :attr:`creates_join_request` into account. + * |removedkwargs| Args: invite_link (:obj:`str`): The invite link. @@ -65,7 +66,6 @@ class ChatInviteLink(TelegramObject): created using this link. .. versionadded:: 13.8 - Attributes: invite_link (:obj:`str`): The invite link. If the link was created by another chat administrator, then the second part of the link will be replaced with ``'…'``. diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index b4a9db7149e..d9c082d472c 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -46,6 +46,9 @@ class ChatJoinRequest(TelegramObject): .. versionadded:: 13.8 + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: chat (:class:`telegram.Chat`): Chat to which the request was sent. from_user (:class:`telegram.User`): User that sent the join request. @@ -54,7 +57,6 @@ class ChatJoinRequest(TelegramObject): invite_link (:class:`telegram.ChatInviteLink`, optional): Chat invite link that was used by the user to send the join request. - Attributes: chat (:class:`telegram.Chat`): Chat to which the request was sent. from_user (:class:`telegram.User`): User that sent the join request. diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index 6cd496e5117..638aa6d6674 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -34,12 +34,13 @@ class ChatLocation(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`location` is equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: location (:class:`telegram.Location`): The location to which the supergroup is connected. Can't be a live location. address (:obj:`str`): Location address; 1-64 characters, as defined by the chat owner - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: location (:class:`telegram.Location`): The location to which the supergroup is connected. address (:obj:`str`): Location address, as defined by the chat owner diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index 877bc613c68..b23f7268e86 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -54,6 +54,7 @@ class ChatMember(TelegramObject): use :class:`ChatMember` directly. * The constant ``ChatMember.CREATOR`` was replaced by :attr:`~telegram.ChatMember.OWNER` * The constant ``ChatMember.KICKED`` was replaced by :attr:`~telegram.ChatMember.BANNED` + * |removedkwargs| Args: user (:class:`telegram.User`): Information about the user. @@ -138,6 +139,9 @@ class ChatMemberOwner(ChatMember): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: user (:class:`telegram.User`): Information about the user. is_anonymous (:obj:`bool`): :obj:`True`, if the @@ -163,7 +167,7 @@ def __init__( custom_title: str = None, api_kwargs: JSONDict = None, ): - super().__init__(status=ChatMember.OWNER, user=user) + super().__init__(status=ChatMember.OWNER, user=user, api_kwargs=api_kwargs) self.is_anonymous = is_anonymous self.custom_title = custom_title @@ -174,9 +178,11 @@ class ChatMemberAdministrator(ChatMember): .. versionadded:: 13.7 .. versionchanged:: 20.0 - Argument and attribute ``can_manage_voice_chats`` were renamed to - :paramref:`can_manage_video_chats` and :attr:`can_manage_video_chats` in accordance to - Bot API 6.0. + + * Argument and attribute ``can_manage_voice_chats`` were renamed to + :paramref:`can_manage_video_chats` and :attr:`can_manage_video_chats` in accordance to + Bot API 6.0. + * |removedkwargs| Args: user (:class:`telegram.User`): Information about the user. @@ -285,7 +291,7 @@ def __init__( custom_title: str = None, api_kwargs: JSONDict = None, ): - super().__init__(status=ChatMember.ADMINISTRATOR, user=user) + super().__init__(status=ChatMember.ADMINISTRATOR, user=user, api_kwargs=api_kwargs) self.can_be_edited = can_be_edited self.is_anonymous = is_anonymous self.can_manage_chat = can_manage_chat @@ -308,6 +314,9 @@ class ChatMemberMember(ChatMember): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: user (:class:`telegram.User`): Information about the user. @@ -325,7 +334,7 @@ def __init__( user: User, api_kwargs: JSONDict = None, ): - super().__init__(status=ChatMember.MEMBER, user=user) + super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) class ChatMemberRestricted(ChatMember): @@ -335,6 +344,9 @@ class ChatMemberRestricted(ChatMember): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: user (:class:`telegram.User`): Information about the user. is_member (:obj:`bool`): :obj:`True`, if the user is a @@ -413,7 +425,7 @@ def __init__( until_date: datetime.datetime, api_kwargs: JSONDict = None, ): - super().__init__(status=ChatMember.RESTRICTED, user=user) + super().__init__(status=ChatMember.RESTRICTED, user=user, api_kwargs=api_kwargs) self.is_member = is_member self.can_change_info = can_change_info self.can_invite_users = can_invite_users @@ -433,6 +445,9 @@ class ChatMemberLeft(ChatMember): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: user (:class:`telegram.User`): Information about the user. @@ -449,7 +464,7 @@ def __init__( user: User, api_kwargs: JSONDict = None, ): - super().__init__(status=ChatMember.LEFT, user=user) + super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs) class ChatMemberBanned(ChatMember): @@ -459,6 +474,9 @@ class ChatMemberBanned(ChatMember): .. versionadded:: 13.7 + .. versionchanged:: 20.0 + |removedkwargs| + Args: user (:class:`telegram.User`): Information about the user. until_date (:class:`datetime.datetime`): Date when restrictions @@ -481,5 +499,5 @@ def __init__( until_date: datetime.datetime, api_kwargs: JSONDict = None, ): - super().__init__(status=ChatMember.BANNED, user=user) + super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs) self.until_date = until_date diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index bd47a34dff8..f69df208233 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -44,6 +44,9 @@ class ChatMemberUpdated(TelegramObject): Note: In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. + .. versionchanged:: 20.0 + |removedkwargs| + Args: chat (:class:`telegram.Chat`): Chat the user belongs to. from_user (:class:`telegram.User`): Performer of the action, which resulted in the change. diff --git a/telegram/_chatpermissions.py b/telegram/_chatpermissions.py index 21ec8f01c57..5e70405623b 100644 --- a/telegram/_chatpermissions.py +++ b/telegram/_chatpermissions.py @@ -35,6 +35,9 @@ class ChatPermissions(TelegramObject): permissions that are set, but also sets all the others to :obj:`False`. However, since not documented, this behaviour may change unbeknown to PTB. + .. versionchanged:: 20.0 + |removedkwargs| + Args: can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text messages, contacts, locations and venues. diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index b8ca3a2235f..334d80738c8 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -43,6 +43,9 @@ class ChosenInlineResult(TelegramObject): * It is necessary to enable inline feedback via `@Botfather `_ in order to receive these objects in updates. + .. versionchanged:: 20.0 + |removedkwargs| + Args: result_id (:obj:`str`): The unique identifier for the result that was chosen. from_user (:class:`telegram.User`): The user that chose the result. @@ -52,7 +55,6 @@ class ChosenInlineResult(TelegramObject): only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message. query (:obj:`str`): The query that was used to obtain the result. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: result_id (:obj:`str`): The unique identifier for the result that was chosen. diff --git a/telegram/_dice.py b/telegram/_dice.py index bfcc5c78a8d..67486e0868e 100644 --- a/telegram/_dice.py +++ b/telegram/_dice.py @@ -55,6 +55,9 @@ class Dice(TelegramObject): /Code-snippets#map-a-slot-machine-dice-value-to-the-corresponding-symbols>`_. However, this behaviour is undocumented and might be changed by Telegram. + .. versionchanged:: 20.0 + |removedkwargs| + Args: value (:obj:`int`): Value of the dice. 1-6 for dice, darts and bowling balls, 1-5 for basketball and football/soccer ball, 1-64 for slot machine. diff --git a/telegram/_files/_basemedium.py b/telegram/_files/_basemedium.py index 485efde394b..994978d000a 100644 --- a/telegram/_files/_basemedium.py +++ b/telegram/_files/_basemedium.py @@ -32,6 +32,9 @@ class _BaseMedium(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -40,7 +43,6 @@ class _BaseMedium(TelegramObject): Can't be used to download or reuse the file. file_size (:obj:`int`, optional): File size. - Attributes: file_id (:obj:`str`): File identifier. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index 744f60387f9..d20b819e5bb 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -37,6 +37,9 @@ class _BaseThumbedMedium(_BaseMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -46,7 +49,6 @@ class _BaseThumbedMedium(_BaseMedium): file_size (:obj:`int`, optional): File size. thumb (:class:`telegram.PhotoSize`, optional): Thumbnail as defined by sender. - Attributes: file_id (:obj:`str`): File identifier. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index 2c3ffa8cd3a..96013821a92 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -29,6 +29,9 @@ class Animation(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -43,8 +46,6 @@ class Animation(_BaseThumbedMedium): mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): File identifier. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index 657d5a1edab..82f36331ba3 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -29,6 +29,10 @@ class Audio(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -44,8 +48,6 @@ class Audio(_BaseThumbedMedium): thumb (:class:`telegram.PhotoSize`, optional): Thumbnail of the album cover to which the music file belongs. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): Identifier for this file. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/chatphoto.py b/telegram/_files/chatphoto.py index 1d026e38897..d7620b526f2 100644 --- a/telegram/_files/chatphoto.py +++ b/telegram/_files/chatphoto.py @@ -34,6 +34,9 @@ class ChatPhoto(TelegramObject): considered equal, if their :attr:`small_file_unique_id` and :attr:`big_file_unique_id` are equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download and only for as long diff --git a/telegram/_files/contact.py b/telegram/_files/contact.py index 7e2ea9d742d..e727357ef68 100644 --- a/telegram/_files/contact.py +++ b/telegram/_files/contact.py @@ -28,13 +28,15 @@ class Contact(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`phone_number` is equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: phone_number (:obj:`str`): Contact's phone number. first_name (:obj:`str`): Contact's first name. last_name (:obj:`str`, optional): Contact's last name. user_id (:obj:`int`, optional): Contact's user identifier in Telegram. vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: phone_number (:obj:`str`): Contact's phone number. diff --git a/telegram/_files/document.py b/telegram/_files/document.py index da16947bb2a..24fec68562c 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -30,6 +30,9 @@ class Document(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -40,8 +43,6 @@ class Document(_BaseThumbedMedium): mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): File identifier. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/file.py b/telegram/_files/file.py index a39cbe7405a..49232caae1e 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -48,6 +48,9 @@ class File(TelegramObject): * If you obtain an instance of this class from :attr:`telegram.PassportFile.get_file`, then it will automatically be decrypted as it downloads when you call :meth:`download()`. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/location.py b/telegram/_files/location.py index aca10065121..b660d156650 100644 --- a/telegram/_files/location.py +++ b/telegram/_files/location.py @@ -28,6 +28,9 @@ class Location(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`longitude` and :attr:`latitude` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: longitude (:obj:`float`): Longitude as defined by sender. latitude (:obj:`float`): Latitude as defined by sender. @@ -39,7 +42,6 @@ class Location(TelegramObject): 1-:tg-const:`telegram.constants.LocationLimit.HEADING`. For active live locations only. proximity_alert_radius (:obj:`int`, optional): Maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: longitude (:obj:`float`): Longitude as defined by sender. diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index fba08c5f268..6ffbf8349ee 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -28,6 +28,9 @@ class PhotoSize(_BaseMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -38,8 +41,6 @@ class PhotoSize(_BaseMedium): height (:obj:`int`): Photo height. file_size (:obj:`int`, optional): File size in bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): Identifier for this file. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 895e92eca55..6ca76a462b1 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -42,6 +42,9 @@ class Sticker(_BaseThumbedMedium): arguments had to be changed. Use keyword arguments to make sure that the arguments are passed correctly. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -72,11 +75,10 @@ class Sticker(_BaseThumbedMedium): premium animation for the sticker. .. versionadded:: 20.0 - custom_emoji (:obj:`str`, optional): For custom emoji stickers, unique identifier of the + custom_emoji_id (:obj:`str`, optional): For custom emoji stickers, unique identifier of the custom emoji. .. versionadded:: 20.0 - _kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: file_id (:obj:`str`): Identifier for this file. @@ -197,7 +199,9 @@ class StickerSet(TelegramObject): passed correctly. .. versionchanged:: 20.0: - The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type` instead. + + * The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type` instead. + * |removedkwargs| Args: name (:obj:`str`): Sticker set name. @@ -297,6 +301,9 @@ class MaskPosition(TelegramObject): considered equal, if their :attr:`point`, :attr:`x_shift`, :attr:`y_shift` and, :attr:`scale` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: point (:obj:`str`): The part of the face relative to which the mask should be placed. One of :attr:`FOREHEAD`, :attr:`EYES`, :attr:`MOUTH`, or :attr:`CHIN`. diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index 43428532d46..48a3c3103a7 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -38,6 +38,9 @@ class Venue(TelegramObject): Foursquare details and Google Pace details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. + .. versionchanged:: 20.0 + |removedkwargs| + Args: location (:class:`telegram.Location`): Venue location. title (:obj:`str`): Name of the venue. @@ -49,7 +52,6 @@ class Venue(TelegramObject): google_place_type (:obj:`str`, optional): Google Places type of the venue. (See `supported types `_.) - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: location (:class:`telegram.Location`): Venue location. diff --git a/telegram/_files/video.py b/telegram/_files/video.py index e3bf20bb62b..e5b6d54af09 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -29,6 +29,9 @@ class Video(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -43,8 +46,6 @@ class Video(_BaseThumbedMedium): mime_type (:obj:`str`, optional): MIME type of a file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): Identifier for this file. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index 92e01641cd0..9dbd7509f1c 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -29,6 +29,9 @@ class VideoNote(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -41,8 +44,6 @@ class VideoNote(_BaseThumbedMedium): thumb (:class:`telegram.PhotoSize`, optional): Video thumbnail. file_size (:obj:`int`, optional): File size in bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): Identifier for this file. file_unique_id (:obj:`str`): Unique identifier for this file, which @@ -53,7 +54,6 @@ class VideoNote(_BaseThumbedMedium): thumb (:class:`telegram.PhotoSize`): Optional. Video thumbnail. file_size (:obj:`int`): Optional. File size in bytes. - """ __slots__ = ("duration", "length") diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index 97409dd2d95..d3f257c84cb 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -28,6 +28,9 @@ class Voice(_BaseMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -38,8 +41,6 @@ class Voice(_BaseMedium): mime_type (:obj:`str`, optional): MIME type of the file as defined by sender. file_size (:obj:`int`, optional): File size in bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): Identifier for this file. file_unique_id (:obj:`str`): Unique identifier for this file, which @@ -49,7 +50,6 @@ class Voice(_BaseMedium): mime_type (:obj:`str`): Optional. MIME type of the file as defined by sender. file_size (:obj:`int`): Optional. File size in bytes. - """ __slots__ = ("duration", "mime_type") diff --git a/telegram/_forcereply.py b/telegram/_forcereply.py index fd53c3121c0..27efd33a56f 100644 --- a/telegram/_forcereply.py +++ b/telegram/_forcereply.py @@ -33,8 +33,10 @@ class ForceReply(TelegramObject): considered equal, if their :attr:`selective` is equal. .. versionchanged:: 20.0 - The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` - is now always set to :obj:`True` as expected by the Bot API. + + * The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` + is now always set to :obj:`True` as expected by the Bot API. + * |removedkwargs| Args: selective (:obj:`bool`, optional): Use this parameter if you want to force reply from @@ -50,8 +52,6 @@ class ForceReply(TelegramObject): .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: force_reply (:obj:`True`): Shows reply interface to the user, as if they manually selected the bots message and tapped 'Reply'. diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 115a10cdd5f..815481452a0 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -39,6 +39,9 @@ class Game(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`title`, :attr:`description` and :attr:`photo` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index cc2cd6836e3..6714fdf4a03 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -67,8 +67,10 @@ class InlineKeyboardButton(TelegramObject): :class:`telegram.InlineKeyboardMarkup` .. versionchanged:: 20.0 - :attr:`web_app` is considered as well when comparing objects of this type in terms of - equality. + + * :attr:`web_app` is considered as well when comparing objects of this type in terms of + equality. + * |removedkwargs| Args: text (:obj:`str`): Label text on the button. @@ -115,7 +117,6 @@ class InlineKeyboardButton(TelegramObject): pay (:obj:`bool`, optional): Specify :obj:`True`, to send a Pay button. This type of button must always be the `first` button in the first row and can only be used in invoice messages. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: text (:obj:`str`): Label text on the button. diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index e39f76caa88..997fa03f53e 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -39,10 +39,12 @@ class InlineKeyboardMarkup(TelegramObject): .. seealso:: `Inline Keyboard Example 1 `_, `Inline Keyboard Example 2 `_ + .. versionchanged:: 20.0 + |removedkwargs| + Args: inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, each represented by a list of InlineKeyboardButton objects. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, @@ -107,7 +109,6 @@ def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> "InlineK Args: button (:class:`telegram.InlineKeyboardButton`): The button to use in the markup - **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ return cls([[button]], **kwargs) # type: ignore[arg-type] @@ -125,7 +126,6 @@ def from_row( Args: button_row (List[:class:`telegram.InlineKeyboardButton`]): The button to use in the markup - **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ return cls([button_row], **kwargs) # type: ignore[arg-type] @@ -143,7 +143,6 @@ def from_column( Args: button_column (List[:class:`telegram.InlineKeyboardButton`]): The button to use in the markup - **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ button_grid = [[button] for button in button_column] diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index af76e224e9a..e25d0de06be 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -49,6 +49,7 @@ class InlineQuery(TelegramObject): ``{read, write, connect, pool}_timeout``, :paramref:`answer.api_kwargs`, ``auto_pagination``. Use a named argument for those, and notice that some positional arguments changed position as a result. + * |removedbotandkwargs| Args: id (:obj:`str`): Unique identifier for this query. @@ -66,8 +67,6 @@ class InlineQuery(TelegramObject): location (:class:`telegram.Location`, optional): Sender location, only for bots that request user location. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: id (:obj:`str`): Unique identifier for this query. from_user (:class:`telegram.User`): Sender. diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 376901e729f..12d63949bbf 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -33,10 +33,12 @@ class InlineQueryResult(TelegramObject): All URLs passed in inline query results will be available to end users and therefore must be assumed to be *public*. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): Type of the result. id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): Type of the result. diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index 6a9db00213d..ed98f7a6159 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -48,7 +48,6 @@ class InlineQueryResultArticle(InlineQueryResult): thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. thumb_width (:obj:`int`, optional): Thumbnail width. thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.ARTICLE`. diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index e2714affb49..80982328cee 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -56,7 +56,6 @@ class InlineQueryResultAudio(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the audio. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.AUDIO`. diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index dd746b98a36..62c97ee3118 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -53,7 +53,6 @@ class InlineQueryResultCachedAudio(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the audio. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.AUDIO`. diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index b5147be13e8..29d08a3ce56 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -55,7 +55,6 @@ class InlineQueryResultCachedDocument(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the file. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.DOCUMENT`. diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index c49c9e55cd0..b60903eb1a2 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -55,7 +55,6 @@ class InlineQueryResultCachedGif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the gif. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index 44da19dcc5f..da565c47116 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -55,7 +55,6 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the MPEG-4 file. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 31c5c3a7732..7496d55e042 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -56,7 +56,6 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the photo. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index cecea74f12c..91143b33b36 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -42,7 +42,6 @@ class InlineQueryResultCachedSticker(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the sticker. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.STICKER`. diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 4d10f17ee4b..26c1c9c9084 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -56,7 +56,6 @@ class InlineQueryResultCachedVideo(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the video. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 898dd470470..472f9febcf2 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -54,7 +54,6 @@ class InlineQueryResultCachedVoice(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the voice message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VOICE`. diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index 2634ca53d6a..f091321d957 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -35,6 +35,9 @@ class InlineQueryResultContact(InlineQueryResult): Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the contact. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. phone_number (:obj:`str`): Contact's phone number. @@ -49,7 +52,6 @@ class InlineQueryResultContact(InlineQueryResult): thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. thumb_width (:obj:`int`, optional): Thumbnail width. thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.CONTACT`. diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 08489313bc4..3ce2d25ba4d 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -38,6 +38,9 @@ class InlineQueryResultDocument(InlineQueryResult): specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. title (:obj:`str`): Title for the result. @@ -61,7 +64,6 @@ class InlineQueryResultDocument(InlineQueryResult): thumb_url (:obj:`str`, optional): URL of the thumbnail (JPEG only) for the file. thumb_width (:obj:`int`, optional): Thumbnail width. thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.DOCUMENT`. diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index a67244038d1..2b59a8734b9 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -27,12 +27,14 @@ class InlineQueryResultGame(InlineQueryResult): """Represents a :class:`telegram.Game`. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. game_short_name (:obj:`str`): Short name of the game. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GAME`. diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 7c7690c3830..3fd45079c8c 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -37,6 +37,9 @@ class InlineQueryResultGif(InlineQueryResult): the user with optional caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the animation. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. gif_url (:obj:`str`): A valid URL for the GIF file. File size must not exceed 1MB. @@ -61,7 +64,6 @@ class InlineQueryResultGif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the GIF animation. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.GIF`. diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index f8d6da5a74f..7cef95d0ae1 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -35,6 +35,9 @@ class InlineQueryResultLocation(InlineQueryResult): Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the location. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. latitude (:obj:`float`): Location latitude in degrees. @@ -57,7 +60,6 @@ class InlineQueryResultLocation(InlineQueryResult): thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. thumb_width (:obj:`int`, optional): Thumbnail width. thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.LOCATION`. diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index d99c1b64c83..a40326e843e 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -38,6 +38,9 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): use :attr:`input_message_content` to send a message with the specified content instead of the animation. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. mpeg4_url (:obj:`str`): A valid URL for the MP4 file. File size must not exceed 1MB. @@ -61,7 +64,6 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the video animation. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.MPEG4GIF`. diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 0ce94b54dd9..f3d1166406d 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -37,6 +37,9 @@ class InlineQueryResultPhoto(InlineQueryResult): caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the photo. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size @@ -59,7 +62,6 @@ class InlineQueryResultPhoto(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the photo. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.PHOTO`. diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index c6af29c0cf0..874b59d15d5 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -39,6 +39,9 @@ class InlineQueryResultVenue(InlineQueryResult): Foursquare details and Google Pace details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. latitude (:obj:`float`): Latitude of the venue location in degrees. @@ -60,7 +63,6 @@ class InlineQueryResultVenue(InlineQueryResult): thumb_url (:obj:`str`, optional): Url of the thumbnail for the result. thumb_width (:obj:`int`, optional): Thumbnail width. thumb_height (:obj:`int`, optional): Thumbnail height. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VENUE`. diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 85dffdf0e2a..de04d0e9514 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -42,6 +42,9 @@ class InlineQueryResultVideo(InlineQueryResult): If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must replace its content using :attr:`input_message_content`. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. video_url (:obj:`str`): A valid URL for the embedded video player or video file. @@ -67,7 +70,6 @@ class InlineQueryResultVideo(InlineQueryResult): message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VIDEO`. diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index f671401e718..cd4814ef75d 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -35,9 +35,12 @@ class InlineQueryResultVoice(InlineQueryResult): """ Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use - :attr:`input_message_content` to send a message with the specified content instead of the + :attr:`input_message_content` to send a message with the specified content instead of the voice message. + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. voice_url (:obj:`str`): A valid URL for the voice recording. @@ -56,7 +59,6 @@ class InlineQueryResultVoice(InlineQueryResult): to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the message to be sent instead of the voice recording. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InlineQueryResultType.VOICE`. diff --git a/telegram/_inline/inputcontactmessagecontent.py b/telegram/_inline/inputcontactmessagecontent.py index e341e6989c2..3d9ac9fcbe1 100644 --- a/telegram/_inline/inputcontactmessagecontent.py +++ b/telegram/_inline/inputcontactmessagecontent.py @@ -28,13 +28,15 @@ class InputContactMessageContent(InputMessageContent): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`phone_number` is equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: phone_number (:obj:`str`): Contact's phone number. first_name (:obj:`str`): Contact's first name. last_name (:obj:`str`, optional): Contact's last name. vcard (:obj:`str`, optional): Additional data about the contact in the form of a vCard, 0-2048 bytes. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: phone_number (:obj:`str`): Contact's phone number. diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 86432847289..e4eb6bb8ce1 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -38,6 +38,9 @@ class InputInvoiceMessageContent(InputMessageContent): .. versionadded:: 13.5 + .. versionchanged:: 20.0 + |removedkwargs| + Args: title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- :tg-const:`telegram.Invoice.MAX_TITLE_LENGTH` characters. @@ -89,7 +92,6 @@ class InputInvoiceMessageContent(InputMessageContent): should be sent to provider. is_flexible (:obj:`bool`, optional): Pass :obj:`True`, if the final price depends on the shipping method. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- diff --git a/telegram/_inline/inputlocationmessagecontent.py b/telegram/_inline/inputlocationmessagecontent.py index 04261f896db..789b6ecce10 100644 --- a/telegram/_inline/inputlocationmessagecontent.py +++ b/telegram/_inline/inputlocationmessagecontent.py @@ -30,6 +30,9 @@ class InputLocationMessageContent(InputMessageContent): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`latitude` and :attr:`longitude` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: latitude (:obj:`float`): Latitude of the location in degrees. longitude (:obj:`float`): Longitude of the location in degrees. @@ -43,7 +46,6 @@ class InputLocationMessageContent(InputMessageContent): proximity_alert_radius (:obj:`int`, optional): For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and :tg-const:`telegram.constants.LocationLimit.HEADING` if specified. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: latitude (:obj:`float`): Latitude of the location in degrees. diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index 3b3849a925d..f115e6027fa 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -35,6 +35,9 @@ class InputTextMessageContent(InputMessageContent): .. seealso:: `Inline Example `_ + .. versionchanged:: 20.0 + |removedkwargs| + Args: message_text (:obj:`str`): Text of the message to be sent, 1-:tg-const:`telegram.constants.MessageLimit.TEXT_LENGTH` characters after entities @@ -47,7 +50,6 @@ class InputTextMessageContent(InputMessageContent): :paramref:`parse_mode`. disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the sent message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: message_text (:obj:`str`): Text of the message to be sent, diff --git a/telegram/_inline/inputvenuemessagecontent.py b/telegram/_inline/inputvenuemessagecontent.py index 05024cbfe74..c45a7b88ada 100644 --- a/telegram/_inline/inputvenuemessagecontent.py +++ b/telegram/_inline/inputvenuemessagecontent.py @@ -33,6 +33,9 @@ class InputVenueMessageContent(InputMessageContent): Foursquare details and Google Pace details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. + .. versionchanged:: 20.0 + |removedkwargs| + Args: latitude (:obj:`float`): Latitude of the location in degrees. longitude (:obj:`float`): Longitude of the location in degrees. @@ -46,7 +49,6 @@ class InputVenueMessageContent(InputMessageContent): google_place_type (:obj:`str`, optional): Google Places type of the venue. (See `supported types `_.) - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: latitude (:obj:`float`): Latitude of the location in degrees. diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index c5ff71d48d8..1b4b187a71d 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -48,8 +48,9 @@ class KeyboardButton(TelegramObject): Older clients will display unsupported message. .. versionchanged:: 20.0 - :attr:`web_app` is considered as well when comparing objects of this type in terms of - equality. + * :attr:`web_app` is considered as well when comparing objects of this type in terms of + equality. + * |removedkwargs| Args: text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be @@ -67,7 +68,6 @@ class KeyboardButton(TelegramObject): Available in private chats only. .. versionadded:: 20.0 - Attributes: text (:obj:`str`): Text of the button. request_contact (:obj:`bool`): Optional. The user's phone number will be sent. diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index 78d1b94beae..ba01939fe92 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -31,6 +31,9 @@ class KeyboardButtonPollType(TelegramObject): .. seealso:: `Pollbot Example `_ + .. versionchanged:: 20.0 + |removedkwargs| + Attributes: type (:obj:`str`): Optional. If :tg-const:`telegram.Poll.QUIZ` is passed, the user will be allowed to create only polls in the quiz mode. If :tg-const:`telegram.Poll.REGULAR` is diff --git a/telegram/_loginurl.py b/telegram/_loginurl.py index 1831719fe8e..560cf3749d1 100644 --- a/telegram/_loginurl.py +++ b/telegram/_loginurl.py @@ -38,6 +38,9 @@ class LoginUrl(TelegramObject): and the integrity of the data as described in `Checking authorization `_ + .. versionchanged:: 20.0 + |removedkwargs| + Args: url (:obj:`str`): An HTTPS URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index b61ea8a1b58..dbd8dc3ea58 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -102,7 +102,6 @@ class MenuButtonCommands(MenuButton): """Represents a menu button, which opens the bot's list of commands. .. versionadded:: 20.0 - Attributes: type (:obj:`str`): :tg-const:`telegram.constants.MenuButtonType.COMMANDS`. """ @@ -169,7 +168,6 @@ class MenuButtonDefault(MenuButton): """Describes that no specific value for the menu button was set. .. versionadded:: 20.0 - Attributes: type (:obj:`str`): :tg-const:`telegram.constants.MenuButtonType.DEFAULT`. """ diff --git a/telegram/_message.py b/telegram/_message.py index 89b8aa5fa2f..6461c5a0663 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -97,6 +97,7 @@ class Message(TelegramObject): ``{read, write, connect, pool}_timeout``, ``api_kwargs``, ``contact``, ``quote``, ``filename``, ``loaction``, ``venue``. Use a named argument for those, and notice that some positional arguments changed position as a result. + * |removedbotandkwargs| Args: message_id (:obj:`int`): Unique message identifier inside this chat. diff --git a/telegram/_messageautodeletetimerchanged.py b/telegram/_messageautodeletetimerchanged.py index 1a117a04a18..e4d8f6b7094 100644 --- a/telegram/_messageautodeletetimerchanged.py +++ b/telegram/_messageautodeletetimerchanged.py @@ -32,10 +32,12 @@ class MessageAutoDeleteTimerChanged(TelegramObject): .. versionadded:: 13.4 + .. versionchanged:: 20.0 + |removedkwargs| + Args: message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the chat. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index 6093b2cb258..438a2157f78 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -38,6 +38,9 @@ class MessageEntity(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`type`, :attr:`offset` and :attr:`length` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (@username), :attr:`HASHTAG`, :attr:`BOT_COMMAND`, @@ -62,7 +65,6 @@ class MessageEntity(TelegramObject): information about the sticker. .. versionadded:: 20.0 - Attributes: type (:obj:`str`): Type of the entity. offset (:obj:`int`): Offset in UTF-16 code units to the start of the entity. diff --git a/telegram/_messageid.py b/telegram/_messageid.py index 3ef9c5efbc0..a14c3d3420b 100644 --- a/telegram/_messageid.py +++ b/telegram/_messageid.py @@ -28,8 +28,14 @@ class MessageId(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`message_id` is equal. + .. versionchanged:: 20.0 + |removedkwargs| + + Args: + message_id (:obj:`int`): Unique message identifier. + Attributes: - message_id (:obj:`int`): Unique message identifier + message_id (:obj:`int`): Unique message identifier. """ __slots__ = ("message_id",) diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index f6c5bfd7ef9..053c8255c0e 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -112,13 +112,15 @@ class EncryptedCredentials(TelegramObject): This object is decrypted only when originating from :obj:`telegram.PassportData.decrypted_credentials`. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's nonce, data hashes and secrets used for EncryptedPassportElement decryption and authentication or base64 encrypted data. hash (:obj:`str`): Base64-encoded data hash for data authentication. secret (:obj:`str`): Decrypted or encrypted secret used for decryption. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's @@ -207,6 +209,9 @@ def decrypted_data(self) -> "Credentials": class Credentials(TelegramObject): """ + .. versionchanged:: 20.0 + |removedbotandkwargs| + Attributes: secure_data (:class:`telegram.SecureData`): Credentials for encrypted data nonce (:obj:`str`): Bot-specified nonce @@ -243,6 +248,9 @@ class SecureData(TelegramObject): This object represents the credentials that were used to decrypt the encrypted data. All fields are optional and depend on fields that were requested. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Attributes: personal_details (:class:`telegram.SecureValue`, optional): Credentials for encrypted personal details. @@ -342,6 +350,9 @@ class SecureValue(TelegramObject): This object represents the credentials that were used to decrypt the encrypted value. All fields are optional and depend on the type of field. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Attributes: data (:class:`telegram.DataCredentials`, optional): Credentials for encrypted Telegram Passport data. Available for "personal_details", "passport", "driver_license", @@ -412,7 +423,11 @@ def to_dict(self) -> JSONDict: class _CredentialsBase(TelegramObject): - """Base class for DataCredentials and FileCredentials.""" + """Base class for DataCredentials and FileCredentials. + + .. versionchanged:: 20.0 + |removedbotandkwargs| + """ __slots__ = ("hash", "secret", "file_hash", "data_hash") @@ -431,6 +446,9 @@ class DataCredentials(_CredentialsBase): These credentials can be used to decrypt encrypted data from the data field in EncryptedPassportData. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: data_hash (:obj:`str`): Checksum of encrypted data secret (:obj:`str`): Secret of encrypted data diff --git a/telegram/_passport/data.py b/telegram/_passport/data.py index 8bccc195a10..8bdd38a1155 100644 --- a/telegram/_passport/data.py +++ b/telegram/_passport/data.py @@ -26,6 +26,9 @@ class PersonalDetails(TelegramObject): """ This object represents personal details. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Attributes: first_name (:obj:`str`): First Name. middle_name (:obj:`str`): Optional. First Name. @@ -88,6 +91,9 @@ class ResidentialAddress(TelegramObject): """ This object represents a residential address. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Attributes: street_line1 (:obj:`str`): First line for the address. street_line2 (:obj:`str`): Optional. Second line for the address. @@ -130,6 +136,9 @@ class IdDocumentData(TelegramObject): """ This object represents the data of an identity document. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Attributes: document_no (:obj:`str`): Document number. expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format. diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 74ad493f4da..209c88a87b5 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -43,6 +43,9 @@ class EncryptedPassportElement(TelegramObject): This object is decrypted only when originating from :obj:`telegram.PassportData.decrypted_data`. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 31e5320483c..0a8b6782594 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -38,13 +38,14 @@ class PassportData(TelegramObject): :attr:`decrypted_data` and the payload can be found in :attr:`decrypted_credentials`'s attribute :attr:`telegram.Credentials.nonce`. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index e6aa355c927..345dd02bb58 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -32,10 +32,12 @@ class PassportElementError(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`source` and :attr:`type` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: source (:obj:`str`): Error source. type (:obj:`str`): The section of the user's Telegram Passport which has the error. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: source (:obj:`str`): Error source. @@ -65,6 +67,9 @@ class PassportElementErrorDataField(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`field_name`, :attr:`data_hash` and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of ``"personal_details"``, ``"passport"``, ``"driver_license"``, ``"identity_card"``, @@ -72,7 +77,6 @@ class PassportElementErrorDataField(PassportElementError): field_name (:obj:`str`): Name of the data field which has the error. data_hash (:obj:`str`): Base64-encoded data hash. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of @@ -111,13 +115,15 @@ class PassportElementErrorFile(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. file_hash (:obj:`str`): Base64-encoded file hash. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of @@ -147,13 +153,15 @@ class PassportElementErrorFiles(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hashes`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of @@ -183,13 +191,15 @@ class PassportElementErrorFrontSide(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. file_hash (:obj:`str`): Base64-encoded hash of the file with the front side of the document. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of @@ -219,13 +229,15 @@ class PassportElementErrorReverseSide(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"driver_license"``, ``"identity_card"``. file_hash (:obj:`str`): Base64-encoded hash of the file with the reverse side of the document. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of @@ -255,12 +267,14 @@ class PassportElementErrorSelfie(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. file_hash (:obj:`str`): Base64-encoded hash of the file with the selfie. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of @@ -289,6 +303,9 @@ class PassportElementErrorTranslationFile(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, @@ -296,7 +313,6 @@ class PassportElementErrorTranslationFile(PassportElementError): ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. file_hash (:obj:`str`): Base64-encoded hash of the file. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, @@ -327,6 +343,9 @@ class PassportElementErrorTranslationFiles(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hashes`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, @@ -334,7 +353,6 @@ class PassportElementErrorTranslationFiles(PassportElementError): ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, @@ -365,11 +383,13 @@ class PassportElementErrorUnspecified(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`element_hash`, and :attr:`message` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. element_hash (:obj:`str`): Base64-encoded element hash. message (:obj:`str`): Error message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 083c6e6b739..ecdbe7b32fd 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -36,6 +36,9 @@ class PassportFile(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -45,8 +48,6 @@ class PassportFile(TelegramObject): file_size (:obj:`int`): File size in bytes. file_date (:obj:`int`): Unix time when the file was uploaded. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: file_id (:obj:`str`): Identifier for this file. file_unique_id (:obj:`str`): Unique identifier for this file, which diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index ad42f6b2c79..ff7dded8190 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -32,6 +32,9 @@ class Invoice(TelegramObject): considered equal, if their :attr:`title`, :attr:`description`, :paramref:`start_parameter`, :attr:`currency` and :attr:`total_amount` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: title (:obj:`str`): Product name. description (:obj:`str`): Product description. @@ -44,7 +47,6 @@ class Invoice(TelegramObject): `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: title (:obj:`str`): Product name. diff --git a/telegram/_payment/labeledprice.py b/telegram/_payment/labeledprice.py index 527c06df5b6..fa877be6113 100644 --- a/telegram/_payment/labeledprice.py +++ b/telegram/_payment/labeledprice.py @@ -30,6 +30,9 @@ class LabeledPrice(TelegramObject): .. seealso:: `Paymentbot Example `_ + .. versionchanged:: 20.0 + |removedkwargs| + Args: label (:obj:`str`): Portion label. amount (:obj:`int`): Price of the product in the smallest units of the currency (integer, @@ -38,7 +41,6 @@ class LabeledPrice(TelegramObject): `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: label (:obj:`str`): Portion label. diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index 36f1469a0c2..2b90c98c011 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -35,12 +35,14 @@ class OrderInfo(TelegramObject): considered equal, if their :attr:`name`, :attr:`phone_number`, :attr:`email` and :attr:`shipping_address` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: name (:obj:`str`, optional): User name. phone_number (:obj:`str`, optional): User's phone number. email (:obj:`str`, optional): User email. shipping_address (:class:`telegram.ShippingAddress`, optional): User shipping address. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: name (:obj:`str`): Optional. User name. diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index 76a5878571d..e1e34e42ead 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -39,6 +39,9 @@ class PreCheckoutQuery(TelegramObject): Note: In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. @@ -54,8 +57,6 @@ class PreCheckoutQuery(TelegramObject): user. order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. diff --git a/telegram/_payment/shippingaddress.py b/telegram/_payment/shippingaddress.py index b08e57d6d2a..66d231fc626 100644 --- a/telegram/_payment/shippingaddress.py +++ b/telegram/_payment/shippingaddress.py @@ -29,6 +29,9 @@ class ShippingAddress(TelegramObject): considered equal, if their :attr:`country_code`, :attr:`state`, :attr:`city`, :attr:`street_line1`, :attr:`street_line2` and :attr:`post_code` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: country_code (:obj:`str`): ISO 3166-1 alpha-2 country code. state (:obj:`str`): State, if applicable. @@ -36,7 +39,6 @@ class ShippingAddress(TelegramObject): street_line1 (:obj:`str`): First line for the address. street_line2 (:obj:`str`): Second line for the address. post_code (:obj:`str`): Address post code. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: country_code (:obj:`str`): ISO 3166-1 alpha-2 country code. diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index c9e76642454..bddfae5ffe2 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -35,11 +35,13 @@ class ShippingOption(TelegramObject): .. seealso:: `Paymentbot Example `_ + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Shipping option identifier. title (:obj:`str`): Option title. prices (List[:class:`telegram.LabeledPrice`]): List of price portions. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: id (:obj:`str`): Shipping option identifier. diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index a74ee2d0eb2..a9066f73721 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -40,14 +40,15 @@ class ShippingQuery(TelegramObject): Note: In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. invoice_payload (:obj:`str`): Bot specified invoice payload. shipping_address (:class:`telegram.ShippingAddress`): User specified shipping address. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index 6eac4fe7cd9..651e50cfcab 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -35,6 +35,9 @@ class SuccessfulPayment(TelegramObject): considered equal, if their :attr:`telegram_payment_charge_id` and :attr:`provider_payment_charge_id` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: currency (:obj:`str`): Three-letter ISO 4217 currency code. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not @@ -49,7 +52,6 @@ class SuccessfulPayment(TelegramObject): order_info (:class:`telegram.OrderInfo`, optional): Order info provided by the user. telegram_payment_charge_id (:obj:`str`): Telegram payment identifier. provider_payment_charge_id (:obj:`str`): Provider payment identifier. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: currency (:obj:`str`): Three-letter ISO 4217 currency code. diff --git a/telegram/_poll.py b/telegram/_poll.py index 5a5b0d015e7..8e7b8bdee44 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -41,6 +41,9 @@ class PollOption(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`text` and :attr:`voter_count` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: text (:obj:`str`): Option text, 1-100 characters. voter_count (:obj:`int`): Number of users that voted for this option. @@ -71,6 +74,9 @@ class PollAnswer(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`option_ids` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. @@ -118,6 +124,9 @@ class Poll(TelegramObject): .. seealso:: `Pollbot Example `_ + .. versionchanged:: 20.0 + |removedkwargs| + Args: id (:obj:`str`): Unique poll identifier. question (:obj:`str`): Poll question, 1-300 characters. diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index af07399eaf0..d8ead198bac 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -35,6 +35,9 @@ class ProximityAlertTriggered(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`traveler`, :attr:`watcher` and :attr:`distance` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: traveler (:class:`telegram.User`): User that triggered the alert watcher (:class:`telegram.User`): User that set the alert diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 472b0d20e98..0d92ed82cb8 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -36,6 +36,9 @@ class ReplyKeyboardMarkup(TelegramObject): A user requests to change the bot's language, bot replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard. + .. versionchanged:: 20.0 + |removedkwargs| + Args: keyboard (List[List[:obj:`str` | :class:`telegram.KeyboardButton`]]): Array of button rows, each represented by an Array of :class:`telegram.KeyboardButton` objects. @@ -62,8 +65,6 @@ class ReplyKeyboardMarkup(TelegramObject): .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: keyboard (List[List[:class:`telegram.KeyboardButton` | :obj:`str`]]): Array of button rows. resize_keyboard (:obj:`bool`): Optional. Requests clients to resize the keyboard. @@ -170,7 +171,6 @@ def from_button( field when the reply is active. .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ return cls( [[button]], @@ -222,7 +222,6 @@ def from_row( field when the reply is active. .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ return cls( @@ -275,7 +274,6 @@ def from_column( field when the reply is active. .. versionadded:: 13.7 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. """ button_grid = [[button] for button in button_column] diff --git a/telegram/_replykeyboardremove.py b/telegram/_replykeyboardremove.py index 3e326604f58..74cede1dfce 100644 --- a/telegram/_replykeyboardremove.py +++ b/telegram/_replykeyboardremove.py @@ -38,6 +38,9 @@ class ReplyKeyboardRemove(TelegramObject): User will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use :attr:`telegram.ReplyKeyboardMarkup.one_time_keyboard`. + .. versionchanged:: 20.0 + |removedkwargs| + Args: selective (:obj:`bool`, optional): Use this parameter if you want to remove the keyboard for specific users only. Targets: @@ -46,8 +49,6 @@ class ReplyKeyboardRemove(TelegramObject): 2) If the bot's message is a reply (has `reply_to_message_id`), sender of the original message. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: remove_keyboard (:obj:`True`): Requests clients to remove the custom keyboard. selective (:obj:`bool`): Optional. Use this parameter if you want to remove the keyboard diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index dc454539225..513e0471bc6 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -52,7 +52,6 @@ class TelegramObject: api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| .. versionadded:: 20.0 - Attributes: api_kwargs (Dict[:obj:`str`, any]): |toapikwargsattr| diff --git a/telegram/_update.py b/telegram/_update.py index 5c3d23156a9..105c6c48215 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -46,6 +46,9 @@ class Update(TelegramObject): Note: At most one of the optional parameters can be present in any given update. + .. versionchanged:: 20.0 + |removedkwargs| + Args: update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if @@ -94,8 +97,6 @@ class Update(TelegramObject): receive these updates. .. versionadded:: 13.8 - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: update_id (:obj:`int`): The update's unique identifier. message (:class:`telegram.Message`): Optional. New incoming message. diff --git a/telegram/_user.py b/telegram/_user.py index f88f2dea36e..69f8bee769a 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -63,10 +63,11 @@ class User(TelegramObject): .. versionchanged:: 20.0 - The following are now keyword-only arguments in Bot methods: - ``location``, ``filename``, ``venue``, ``contact``, - ``{read, write, connect, pool}_timeout`` ``api_kwargs``. Use a named argument for those, - and notice that some positional arguments changed position as a result. + * The following are now keyword-only arguments in Bot methods: + ``location``, ``filename``, ``venue``, ``contact``, + ``{read, write, connect, pool}_timeout`` ``api_kwargs``. Use a named argument for those, + and notice that some positional arguments changed position as a result. + * |removedbotandkwargs| Args: id (:obj:`int`): Unique identifier for this user or bot. @@ -89,7 +90,6 @@ class User(TelegramObject): the bot to the attachment menu. .. versionadded:: 20.0 - Attributes: id (:obj:`int`): Unique identifier for this user or bot. is_bot (:obj:`bool`): :obj:`True`, if this user is a bot. diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 8088516b8db..3fb3ce7c083 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -34,6 +34,9 @@ class UserProfilePhotos(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`total_count` and :attr:`photos` are equal. + .. versionchanged:: 20.0 + |removedkwargs| + Args: total_count (:obj:`int`): Total number of profile pictures the target user has. photos (List[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 diff --git a/telegram/_utils/defaultvalue.py b/telegram/_utils/defaultvalue.py index 47545e3b862..c9e7797b489 100644 --- a/telegram/_utils/defaultvalue.py +++ b/telegram/_utils/defaultvalue.py @@ -75,7 +75,6 @@ def f(arg=default_one): Args: value (:class:`object`): The value of the default argument - Attributes: value (:class:`object`): The value of the default argument diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 6da1bc9dd4d..2e1f7c6d18d 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -37,7 +37,8 @@ class VideoChatStarted(TelegramObject): .. versionadded:: 13.4 .. versionchanged:: 20.0 - This class was renamed from ``VoiceChatStarted`` in accordance to Bot API 6.0. + * This class was renamed from ``VoiceChatStarted`` in accordance to Bot API 6.0. + * |removedkwargs| """ __slots__ = () @@ -64,7 +65,6 @@ class VideoChatEnded(TelegramObject): Args: duration (:obj:`int`): Voice chat duration in seconds. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: duration (:obj:`int`): Voice chat duration in seconds. @@ -96,7 +96,6 @@ class VideoChatParticipantsInvited(TelegramObject): Args: users (List[:class:`telegram.User`]): New members that were invited to the video chat. - **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: users (List[:class:`telegram.User`]): New members that were invited to the video chat. @@ -151,8 +150,6 @@ class VideoChatScheduled(TelegramObject): Args: start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the video chat is supposed to be started by a chat administrator - **kwargs (:obj:`dict`): Arbitrary keyword arguments. - Attributes: start_date (:obj:`datetime.datetime`): Point in time (Unix timestamp) when the video chat is supposed to be started by a chat administrator diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 4779e12bcc4..9b7dffb1843 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -40,8 +40,9 @@ class WebhookInfo(TelegramObject): :attr:`last_synchronization_error_date` are equal. .. versionchanged:: 20.0 - :attr:`last_synchronization_error_date` is considered as well when comparing objects of - this type in terms of equality. + * :attr:`last_synchronization_error_date` is considered as well when comparing objects of + this type in terms of equality. + * |removedkwargs| Args: url (:obj:`str`): Webhook URL, may be empty if webhook is not set up. @@ -61,7 +62,6 @@ class WebhookInfo(TelegramObject): that happened when trying to synchronize available updates with Telegram datacenters. .. versionadded:: 20.0 - Attributes: url (:obj:`str`): Webhook URL. has_custom_certificate (:obj:`bool`): If a custom certificate was provided for webhook. diff --git a/telegram/ext/_basepersistence.py b/telegram/ext/_basepersistence.py index 6c2901529c7..5b245572544 100644 --- a/telegram/ext/_basepersistence.py +++ b/telegram/ext/_basepersistence.py @@ -128,7 +128,6 @@ class BasePersistence(Generic[UD, CD, BD], ABC): seconds. .. versionadded:: 20.0 - Attributes: store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this persistence instance. diff --git a/telegram/ext/_callbackcontext.py b/telegram/ext/_callbackcontext.py index e3331f53281..6038ac48371 100644 --- a/telegram/ext/_callbackcontext.py +++ b/telegram/ext/_callbackcontext.py @@ -80,7 +80,6 @@ class CallbackContext(Generic[BT, UD, CD, BD]): to provide :attr:`user_data`. .. versionadded:: 20.0 - Attributes: coroutine (:term:`coroutine function`): Optional. Only present in error handlers if the error was caused by a coroutine run with :meth:`Application.create_task` or a handler diff --git a/telegram/ext/_choseninlineresulthandler.py b/telegram/ext/_choseninlineresulthandler.py index 8251fca9719..3aa596e510a 100644 --- a/telegram/ext/_choseninlineresulthandler.py +++ b/telegram/ext/_choseninlineresulthandler.py @@ -59,7 +59,6 @@ async def callback(update: Update, context: CallbackContext) :attr:`telegram.ext.CallbackContext.matches`. .. versionadded:: 13.6 - Attributes: callback (:term:`coroutine function`): The callback function for this handler. block (:obj:`bool`): Determines whether the return value of the callback should be diff --git a/telegram/ext/_dictpersistence.py b/telegram/ext/_dictpersistence.py index 5460155c46c..e341790f6e7 100644 --- a/telegram/ext/_dictpersistence.py +++ b/telegram/ext/_dictpersistence.py @@ -68,7 +68,6 @@ class DictPersistence(BasePersistence): wait between two consecutive runs of updating the persistence. Defaults to 60 seconds. .. versionadded:: 20.0 - Attributes: store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this persistence instance. diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 785d4be3947..d2852e3f466 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -136,7 +136,6 @@ class ExtBot(Bot, Generic[RLARGS]): limiting the number of requests made by the bot per time interval. .. versionadded:: 20.0 - Attributes: arbitrary_callback_data (:obj:`bool` | :obj:`int`): Whether this bot instance allows to use arbitrary objects as callback data for diff --git a/telegram/ext/_inlinequeryhandler.py b/telegram/ext/_inlinequeryhandler.py index 72aac7da407..891cca13f83 100644 --- a/telegram/ext/_inlinequeryhandler.py +++ b/telegram/ext/_inlinequeryhandler.py @@ -67,7 +67,6 @@ async def callback(update: Update, context: CallbackContext) handle inline queries with the appropriate :attr:`telegram.InlineQuery.chat_type`. .. versionadded:: 13.5 - Attributes: callback (:term:`coroutine function`): The callback function for this handler. pattern (:obj:`str` | :func:`re.Pattern `): Optional. Regex pattern to test diff --git a/telegram/ext/_jobqueue.py b/telegram/ext/_jobqueue.py index b1cfc4bd651..2230505f842 100644 --- a/telegram/ext/_jobqueue.py +++ b/telegram/ext/_jobqueue.py @@ -45,7 +45,6 @@ class JobQueue: `Timerbot Example `_, `Job Queue `_ - Attributes: scheduler (:class:`apscheduler.schedulers.asyncio.AsyncIOScheduler`): The scheduler. @@ -628,7 +627,6 @@ async def callback(context: CallbackContext) user_id (:obj:`int`, optional): User id of the user that this job is associated with. .. versionadded:: 20.0 - Attributes: callback (:term:`coroutine function`): The callback function that should be executed by the new job. diff --git a/telegram/ext/_picklepersistence.py b/telegram/ext/_picklepersistence.py index 82782a8f5a3..a332be6c707 100644 --- a/telegram/ext/_picklepersistence.py +++ b/telegram/ext/_picklepersistence.py @@ -161,7 +161,6 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): wait between two consecutive runs of updating the persistence. Defaults to 60 seconds. .. versionadded:: 20.0 - Attributes: filepath (:obj:`str` | :obj:`pathlib.Path`): The filepath for storing the pickle files. When :attr:`single_file` is :obj:`False` this will be used as a prefix. From 23f6afeab5cd5d475f6dd214949dea01e68999ed Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 18 Sep 2022 18:42:52 +0200 Subject: [PATCH 18/90] Very rough first start on making TO immutable --- telegram/_telegramobject.py | 17 ++++++++++++++++- tests/test_telegramobject.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 513e0471bc6..b003f636ce1 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -59,16 +59,23 @@ class TelegramObject: """ - __slots__ = ("_id_attrs", "_bot", "api_kwargs") + __slots__ = ("_id_attrs", "_bot", "_frozen", "api_kwargs") __INIT_PARAMS: Optional[Set[str]] = None def __init__(self, api_kwargs: JSONDict = None) -> None: + self._frozen: bool = False self._id_attrs: Tuple[object, ...] = () self._bot: Optional["Bot"] = None # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs self.api_kwargs: JSONDict = api_kwargs or {} + def _freeze(self) -> None: + self._frozen = True + + def _unfreeze(self) -> None: + self._frozen = False + def _apply_api_kwargs(self) -> None: """Loops through the api kwargs and for every key that exists as attribute of the object (and is None), it moves the value from `api_kwargs` to the attribute. @@ -88,6 +95,14 @@ def _apply_api_kwargs(self) -> None: if getattr(self, key, True) is None: setattr(self, key, self.api_kwargs.pop(key)) + def __setattr__(self, key: str, value: object) -> None: + # protected attributes can always be set for convenient internal use + if (key == "_frozen") or (not self._frozen) or key.startswith("_"): + super().__setattr__(key, value) + return + + raise AttributeError(f"Attribute {key} of class {self.__class__.__name__} can't be set!") + def __str__(self) -> str: return str(self.to_dict()) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index f91b1d3f2b5..e0f6609b313 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -17,6 +17,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 datetime +import inspect import pickle from copy import deepcopy @@ -25,6 +26,17 @@ from telegram import Chat, Message, PhotoSize, TelegramObject, User +def all_subclasses(cls): + # Gets all subclasses of the specified object, recursively. from + # https://stackoverflow.com/a/3862957/9706202 + return set(cls.__subclasses__()).union( + [s for c in cls.__subclasses__() for s in all_subclasses(c)] + ) + + +TO_SUBCLASSES = sorted(all_subclasses(TelegramObject), key=lambda cls: cls.__name__) + + class TestTelegramObject: class Sub(TelegramObject): def __init__(self, private, normal, b): @@ -197,3 +209,19 @@ def test_deepcopy_subclass_telegram_obj(self, bot): assert d._private == s._private # Can't test for identity since two equal strings is True assert d._bot == s._bot and d._bot is s._bot assert d.normal == s.normal + + @pytest.mark.parametrize("cls", TO_SUBCLASSES, ids=[cls.__name__ for cls in TO_SUBCLASSES]) + def test_subclasses_are_frozen(self, cls): + if cls.__name__.startswith("_"): + return + + # instantiating each subclass would be tedious as some attributes require special init + # args. So we inspect the code instead. + + source_file = inspect.getsourcefile(cls.__init__) + if source_file.endswith("telegramobject.py"): + # classes without their own `__init__` can be ignored + return + + source_lines, first_line = inspect.getsourcelines(cls.__init__) + assert " self._freeze()" in source_lines[-1], f"{cls.__name__} is not frozen correctly" From bf81325729a4e5bdefe9afd7bc90b2fd751517e4 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 18 Sep 2022 18:43:12 +0200 Subject: [PATCH 19/90] Call `_freeze()` in all the `__init__` s --- telegram/_bot.py | 2 ++ telegram/_botcommand.py | 2 ++ telegram/_botcommandscope.py | 16 +++++++++++++++ telegram/_callbackquery.py | 2 ++ telegram/_chat.py | 2 ++ telegram/_chatadministratorrights.py | 2 ++ telegram/_chatinvitelink.py | 2 ++ telegram/_chatjoinrequest.py | 2 ++ telegram/_chatlocation.py | 2 ++ telegram/_chatmember.py | 14 +++++++++++++ telegram/_chatmemberupdated.py | 2 ++ telegram/_chatpermissions.py | 2 ++ telegram/_choseninlineresult.py | 2 ++ telegram/_dice.py | 2 ++ telegram/_files/animation.py | 2 ++ telegram/_files/audio.py | 2 ++ telegram/_files/chatphoto.py | 2 ++ telegram/_files/contact.py | 2 ++ telegram/_files/document.py | 2 ++ telegram/_files/file.py | 2 ++ telegram/_files/inputmedia.py | 12 +++++++++++ telegram/_files/location.py | 2 ++ telegram/_files/photosize.py | 2 ++ telegram/_files/sticker.py | 6 ++++++ telegram/_files/venue.py | 2 ++ telegram/_files/video.py | 2 ++ telegram/_files/videonote.py | 2 ++ telegram/_files/voice.py | 2 ++ telegram/_forcereply.py | 2 ++ telegram/_games/game.py | 2 ++ telegram/_games/gamehighscore.py | 2 ++ telegram/_inline/inlinekeyboardbutton.py | 2 ++ telegram/_inline/inlinekeyboardmarkup.py | 2 ++ telegram/_inline/inlinequery.py | 2 ++ telegram/_inline/inlinequeryresult.py | 2 ++ telegram/_inline/inlinequeryresultarticle.py | 2 ++ telegram/_inline/inlinequeryresultaudio.py | 2 ++ .../_inline/inlinequeryresultcachedaudio.py | 2 ++ .../inlinequeryresultcacheddocument.py | 2 ++ .../_inline/inlinequeryresultcachedgif.py | 2 ++ .../inlinequeryresultcachedmpeg4gif.py | 2 ++ .../_inline/inlinequeryresultcachedphoto.py | 2 ++ .../_inline/inlinequeryresultcachedsticker.py | 2 ++ .../_inline/inlinequeryresultcachedvideo.py | 2 ++ .../_inline/inlinequeryresultcachedvoice.py | 2 ++ telegram/_inline/inlinequeryresultcontact.py | 2 ++ telegram/_inline/inlinequeryresultdocument.py | 2 ++ telegram/_inline/inlinequeryresultgame.py | 2 ++ telegram/_inline/inlinequeryresultgif.py | 2 ++ telegram/_inline/inlinequeryresultlocation.py | 2 ++ telegram/_inline/inlinequeryresultmpeg4gif.py | 2 ++ telegram/_inline/inlinequeryresultphoto.py | 2 ++ telegram/_inline/inlinequeryresultvenue.py | 2 ++ telegram/_inline/inlinequeryresultvideo.py | 2 ++ telegram/_inline/inlinequeryresultvoice.py | 2 ++ .../_inline/inputcontactmessagecontent.py | 2 ++ .../_inline/inputinvoicemessagecontent.py | 2 ++ .../_inline/inputlocationmessagecontent.py | 2 ++ telegram/_inline/inputtextmessagecontent.py | 2 ++ telegram/_inline/inputvenuemessagecontent.py | 2 ++ telegram/_keyboardbutton.py | 2 ++ telegram/_keyboardbuttonpolltype.py | 2 ++ telegram/_loginurl.py | 2 ++ telegram/_menubutton.py | 8 ++++++++ telegram/_message.py | 2 ++ telegram/_messageautodeletetimerchanged.py | 2 ++ telegram/_messageentity.py | 2 ++ telegram/_messageid.py | 2 ++ telegram/_passport/credentials.py | 14 +++++++++++++ telegram/_passport/data.py | 6 ++++++ .../_passport/encryptedpassportelement.py | 2 ++ telegram/_passport/passportdata.py | 2 ++ telegram/_passport/passportelementerrors.py | 20 +++++++++++++++++++ telegram/_passport/passportfile.py | 2 ++ telegram/_payment/invoice.py | 2 ++ telegram/_payment/labeledprice.py | 2 ++ telegram/_payment/orderinfo.py | 2 ++ telegram/_payment/precheckoutquery.py | 2 ++ telegram/_payment/shippingaddress.py | 2 ++ telegram/_payment/shippingoption.py | 2 ++ telegram/_payment/shippingquery.py | 2 ++ telegram/_payment/successfulpayment.py | 2 ++ telegram/_poll.py | 6 ++++++ telegram/_proximityalerttriggered.py | 2 ++ telegram/_replykeyboardmarkup.py | 2 ++ telegram/_replykeyboardremove.py | 2 ++ telegram/_sentwebappmessage.py | 2 ++ telegram/_update.py | 2 ++ telegram/_user.py | 2 ++ telegram/_userprofilephotos.py | 2 ++ telegram/_videochat.py | 8 ++++++++ telegram/_webappdata.py | 2 ++ telegram/_webappinfo.py | 2 ++ telegram/_webhookinfo.py | 2 ++ telegram/ext/_application.py | 2 +- telegram/ext/_extbot.py | 5 ++++- 96 files changed, 283 insertions(+), 2 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 7825865f816..56057db4883 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -228,6 +228,8 @@ def __init__( private_key, password=private_key_password, backend=default_backend() ) + self._freeze() + def __reduce__(self) -> NoReturn: """Called by pickle.dumps(). Serializing bots is unadvisable, so we forbid pickling.""" raise pickle.PicklingError("Bot objects cannot be pickled!") diff --git a/telegram/_botcommand.py b/telegram/_botcommand.py index 91f6263142f..6e4ce2841bf 100644 --- a/telegram/_botcommand.py +++ b/telegram/_botcommand.py @@ -51,3 +51,5 @@ def __init__(self, command: str, description: str, api_kwargs: JSONDict = None): self.description = description self._id_attrs = (self.command, self.description) + + self._freeze() diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 730bff40dfc..bc1070d3030 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -83,6 +83,8 @@ def __init__(self, type: str, api_kwargs: JSONDict = None): self.type = type self._id_attrs = (self.type,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandScope"]: """Converts JSON data to the appropriate :class:`BotCommandScope` object, i.e. takes @@ -135,6 +137,8 @@ class BotCommandScopeDefault(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs) + self._freeze() + class BotCommandScopeAllPrivateChats(BotCommandScope): """Represents the scope of bot commands, covering all private chats. @@ -152,6 +156,8 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs) + self._freeze() + class BotCommandScopeAllGroupChats(BotCommandScope): """Represents the scope of bot commands, covering all group and supergroup chats. @@ -169,6 +175,8 @@ class BotCommandScopeAllGroupChats(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs) + self._freeze() + class BotCommandScopeAllChatAdministrators(BotCommandScope): """Represents the scope of bot commands, covering all group and supergroup chat administrators. @@ -186,6 +194,8 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) + self._freeze() + class BotCommandScopeChat(BotCommandScope): """Represents the scope of bot commands, covering a specific chat. @@ -216,6 +226,8 @@ def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): ) self._id_attrs = (self.type, self.chat_id) + self._freeze() + class BotCommandScopeChatAdministrators(BotCommandScope): """Represents the scope of bot commands, covering all administrators of a specific group or @@ -247,6 +259,8 @@ def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): ) self._id_attrs = (self.type, self.chat_id) + self._freeze() + class BotCommandScopeChatMember(BotCommandScope): """Represents the scope of bot commands, covering a specific member of a group or supergroup @@ -281,3 +295,5 @@ def __init__(self, chat_id: Union[str, int], user_id: int, api_kwargs: JSONDict ) self.user_id = user_id self._id_attrs = (self.type, self.chat_id, self.user_id) + + self._freeze() diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 240904b99c9..cfb421ad56a 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -136,6 +136,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["CallbackQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_chat.py b/telegram/_chat.py index 7d591343acf..81e61405828 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -300,6 +300,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @property def full_name(self) -> Optional[str]: """ diff --git a/telegram/_chatadministratorrights.py b/telegram/_chatadministratorrights.py index cc2f53dfff3..ae6243b08e2 100644 --- a/telegram/_chatadministratorrights.py +++ b/telegram/_chatadministratorrights.py @@ -149,6 +149,8 @@ def __init__( self.can_pin_messages, ) + self._freeze() + @classmethod def all_rights(cls) -> "ChatAdministratorRights": """ diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index 8adf692814d..8b6e17da63d 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -138,6 +138,8 @@ def __init__( self.is_revoked, ) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatInviteLink"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index d9c082d472c..794734dff0f 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -90,6 +90,8 @@ def __init__( self._id_attrs = (self.chat, self.from_user, self.date) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatJoinRequest"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index 638aa6d6674..a84001f9a02 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -61,6 +61,8 @@ def __init__( self._id_attrs = (self.location,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatLocation"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index b23f7268e86..e7c3495d8d2 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -97,6 +97,8 @@ def __init__( self._id_attrs = (self.user, self.status) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -171,6 +173,8 @@ def __init__( self.is_anonymous = is_anonymous self.custom_title = custom_title + self._freeze() + class ChatMemberAdministrator(ChatMember): """ @@ -306,6 +310,8 @@ def __init__( self.can_pin_messages = can_pin_messages self.custom_title = custom_title + self._freeze() + class ChatMemberMember(ChatMember): """ @@ -336,6 +342,8 @@ def __init__( ): super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) + self._freeze() + class ChatMemberRestricted(ChatMember): """ @@ -437,6 +445,8 @@ def __init__( self.can_add_web_page_previews = can_add_web_page_previews self.until_date = until_date + self._freeze() + class ChatMemberLeft(ChatMember): """ @@ -466,6 +476,8 @@ def __init__( ): super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs) + self._freeze() + class ChatMemberBanned(ChatMember): """ @@ -501,3 +513,5 @@ def __init__( ): super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs) self.until_date = until_date + + self._freeze() diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index f69df208233..ebc63164ac6 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -107,6 +107,8 @@ def __init__( self.new_chat_member, ) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMemberUpdated"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_chatpermissions.py b/telegram/_chatpermissions.py index 5e70405623b..4c5c036fbe8 100644 --- a/telegram/_chatpermissions.py +++ b/telegram/_chatpermissions.py @@ -125,6 +125,8 @@ def __init__( self.can_pin_messages, ) + self._freeze() + @classmethod def all_permissions(cls) -> "ChatPermissions": """ diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index 334d80738c8..fa80dc85488 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -88,6 +88,8 @@ def __init__( self._id_attrs = (self.result_id,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChosenInlineResult"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_dice.py b/telegram/_dice.py index 67486e0868e..51c70794b45 100644 --- a/telegram/_dice.py +++ b/telegram/_dice.py @@ -78,6 +78,8 @@ def __init__(self, value: int, emoji: str, api_kwargs: JSONDict = None): self._id_attrs = (self.value, self.emoji) + self._freeze() + DICE: ClassVar[str] = constants.DiceEmoji.DICE # skipcq: PTC-W0052 """:const:`telegram.constants.DiceEmoji.DICE`""" DARTS: ClassVar[str] = constants.DiceEmoji.DARTS diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index 96013821a92..a264b43eca4 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -91,3 +91,5 @@ def __init__( # Optional self.mime_type = mime_type self.file_name = file_name + + self._freeze() diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index 82f36331ba3..fde9741e0ff 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -95,3 +95,5 @@ def __init__( self.title = title self.mime_type = mime_type self.file_name = file_name + + self._freeze() diff --git a/telegram/_files/chatphoto.py b/telegram/_files/chatphoto.py index d7620b526f2..db814e41401 100644 --- a/telegram/_files/chatphoto.py +++ b/telegram/_files/chatphoto.py @@ -92,6 +92,8 @@ def __init__( self.big_file_unique_id, ) + self._freeze() + async def get_small_file( self, *, diff --git a/telegram/_files/contact.py b/telegram/_files/contact.py index e727357ef68..62095867b89 100644 --- a/telegram/_files/contact.py +++ b/telegram/_files/contact.py @@ -68,3 +68,5 @@ def __init__( self.vcard = vcard self._id_attrs = (self.phone_number,) + + self._freeze() diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 24fec68562c..2b511f83c75 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -78,3 +78,5 @@ def __init__( # Optional self.mime_type = mime_type self.file_name = file_name + + self._freeze() diff --git a/telegram/_files/file.py b/telegram/_files/file.py index 49232caae1e..6908c19659c 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -99,6 +99,8 @@ def __init__( self._id_attrs = (self.file_unique_id,) + self._freeze() + async def download( self, custom_path: FilePathInput = None, diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index c5d11de0cac..8407efa1996 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -90,6 +90,8 @@ def __init__( self.caption_entities = caption_entities self.parse_mode = parse_mode + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() @@ -190,6 +192,8 @@ def __init__( self.height = height self.duration = duration + self._freeze() + class InputMediaPhoto(InputMedia): """Represents a photo to be sent. @@ -241,6 +245,8 @@ def __init__( media = parse_file_input(media, PhotoSize, filename=filename, attach=True) super().__init__(InputMediaType.PHOTO, media, caption, caption_entities, parse_mode) + self._freeze() + class InputMediaVideo(InputMedia): """Represents a video to be sent. @@ -338,6 +344,8 @@ def __init__( self.thumb = self._parse_thumb_input(thumb) self.supports_streaming = supports_streaming + self._freeze() + class InputMediaAudio(InputMedia): """Represents an audio file to be treated as music to be sent. @@ -428,6 +436,8 @@ def __init__( self.title = title self.performer = performer + self._freeze() + class InputMediaDocument(InputMedia): """Represents a general file to be sent. @@ -498,3 +508,5 @@ def __init__( super().__init__(InputMediaType.DOCUMENT, media, caption, caption_entities, parse_mode) self.thumb = self._parse_thumb_input(thumb) self.disable_content_type_detection = disable_content_type_detection + + self._freeze() diff --git a/telegram/_files/location.py b/telegram/_files/location.py index b660d156650..b5b50b575e0 100644 --- a/telegram/_files/location.py +++ b/telegram/_files/location.py @@ -90,3 +90,5 @@ def __init__( ) self._id_attrs = (self.longitude, self.latitude) + + self._freeze() diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 6ffbf8349ee..0436920342e 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -73,3 +73,5 @@ def __init__( # Required self.width = width self.height = height + + self._freeze() diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 6ca76a462b1..5d44b6249b9 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -165,6 +165,8 @@ def __init__( self.premium_animation = premium_animation self.custom_emoji_id = custom_emoji_id + self._freeze() + REGULAR: ClassVar[str] = constants.StickerType.REGULAR """:const:`telegram.constants.StickerType.REGULAR`""" MASK: ClassVar[str] = constants.StickerType.MASK @@ -268,6 +270,8 @@ def __init__( self._id_attrs = (self.name,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["StickerSet"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -353,6 +357,8 @@ def __init__( self._id_attrs = (self.point, self.x_shift, self.y_shift, self.scale) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MaskPosition"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index 48a3c3103a7..813a1a7d140 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -99,6 +99,8 @@ def __init__( self._id_attrs = (self.location, self.title) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Venue"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_files/video.py b/telegram/_files/video.py index e5b6d54af09..938372975c4 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -91,3 +91,5 @@ def __init__( # Optional self.mime_type = mime_type self.file_name = file_name + + self._freeze() diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index 9dbd7509f1c..bf780f10e83 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -78,3 +78,5 @@ def __init__( # Required self.length = length self.duration = duration + + self._freeze() diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index d3f257c84cb..9c7968617bc 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -73,3 +73,5 @@ def __init__( self.duration = duration # Optional self.mime_type = mime_type + + self._freeze() diff --git a/telegram/_forcereply.py b/telegram/_forcereply.py index 27efd33a56f..5ecd3b984f1 100644 --- a/telegram/_forcereply.py +++ b/telegram/_forcereply.py @@ -77,3 +77,5 @@ def __init__( self.input_field_placeholder = input_field_placeholder self._id_attrs = (self.selective,) + + self._freeze() diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 815481452a0..37b93d56ef0 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -105,6 +105,8 @@ def __init__( self._id_attrs = (self.title, self.description, self.photo) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Game"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_games/gamehighscore.py b/telegram/_games/gamehighscore.py index be58fd8f39b..03a6c575607 100644 --- a/telegram/_games/gamehighscore.py +++ b/telegram/_games/gamehighscore.py @@ -56,6 +56,8 @@ def __init__(self, position: int, user: User, score: int, api_kwargs: JSONDict = self._id_attrs = (self.position, self.user, self.score) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["GameHighScore"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 6714fdf4a03..8cf5a748b22 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -193,6 +193,8 @@ def __init__( self._id_attrs = () self._set_id_attrs() + self._freeze() + def _set_id_attrs(self) -> None: self._id_attrs = ( self.text, diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 997fa03f53e..c1e4c2dd4b9 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -70,6 +70,8 @@ def __init__( self._id_attrs = (self.inline_keyboard,) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index e25d0de06be..bb5800af9e6 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -105,6 +105,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 12d63949bbf..c7eacb1586f 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -57,6 +57,8 @@ def __init__(self, type: str, id: str, api_kwargs: JSONDict = None): self._id_attrs = (self.id,) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index ed98f7a6159..5f97b69e0f3 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -107,3 +107,5 @@ def __init__( self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 80982328cee..bbbf23ca23b 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -120,3 +120,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 62c97ee3118..99bfdc037b3 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -104,3 +104,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 29d08a3ce56..615aa7b5f3d 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -114,3 +114,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index b60903eb1a2..6462722252d 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -110,3 +110,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index da565c47116..6a9ba794a0e 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -110,3 +110,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 7496d55e042..fda76e1d334 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -115,3 +115,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index 91143b33b36..bdd1b25b57d 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -71,3 +71,5 @@ def __init__( # Optionals self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 26c1c9c9084..2064718be57 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -115,3 +115,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 472f9febcf2..f34097d2211 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -109,3 +109,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index f091321d957..6430bc68fc3 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -110,3 +110,5 @@ def __init__( self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 3ce2d25ba4d..c4ce43b1f7a 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -140,3 +140,5 @@ def __init__( self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 2b59a8734b9..207e1f90dd3 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -60,3 +60,5 @@ def __init__( self.game_short_name = game_short_name self.reply_markup = reply_markup + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 3fd45079c8c..b7c392de4d4 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -141,3 +141,5 @@ def __init__( self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_mime_type = thumb_mime_type + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index 7cef95d0ae1..7a4a642aba9 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -135,3 +135,5 @@ def __init__( self.proximity_alert_radius = ( int(proximity_alert_radius) if proximity_alert_radius else None ) + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index a40326e843e..cd2ba148ef7 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -141,3 +141,5 @@ def __init__( self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_mime_type = thumb_mime_type + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index f3d1166406d..2bdd9a5a62b 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -134,3 +134,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 874b59d15d5..436dc02ed6c 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -137,3 +137,5 @@ def __init__( self.thumb_url = thumb_url self.thumb_width = thumb_width self.thumb_height = thumb_height + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index de04d0e9514..93cc9caacb3 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -152,3 +152,5 @@ def __init__( self.description = description self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index cd4814ef75d..56b82b20bab 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -119,3 +119,5 @@ def __init__( self.caption_entities = caption_entities self.reply_markup = reply_markup self.input_message_content = input_message_content + + self._freeze() diff --git a/telegram/_inline/inputcontactmessagecontent.py b/telegram/_inline/inputcontactmessagecontent.py index 3d9ac9fcbe1..932033747b4 100644 --- a/telegram/_inline/inputcontactmessagecontent.py +++ b/telegram/_inline/inputcontactmessagecontent.py @@ -67,3 +67,5 @@ def __init__( self.vcard = vcard self._id_attrs = (self.phone_number,) + + self._freeze() diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index e4eb6bb8ce1..18c37b3e797 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -216,6 +216,8 @@ def __init__( self.prices, ) + self._freeze() + def __hash__(self) -> int: # we override this as self.prices is a list and not hashable prices = tuple(self.prices) diff --git a/telegram/_inline/inputlocationmessagecontent.py b/telegram/_inline/inputlocationmessagecontent.py index 789b6ecce10..8212f2e87c4 100644 --- a/telegram/_inline/inputlocationmessagecontent.py +++ b/telegram/_inline/inputlocationmessagecontent.py @@ -89,3 +89,5 @@ def __init__( ) self._id_attrs = (self.latitude, self.longitude) + + self._freeze() diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index f115e6027fa..8e14e0fcc60 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -86,6 +86,8 @@ def __init__( self._id_attrs = (self.message_text,) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_inline/inputvenuemessagecontent.py b/telegram/_inline/inputvenuemessagecontent.py index c45a7b88ada..64bcc702103 100644 --- a/telegram/_inline/inputvenuemessagecontent.py +++ b/telegram/_inline/inputvenuemessagecontent.py @@ -103,3 +103,5 @@ def __init__( self.longitude, self.title, ) + + self._freeze() diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index 1b4b187a71d..140fedb274f 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -107,6 +107,8 @@ def __init__( self.web_app, ) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["KeyboardButton"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index ba01939fe92..df618c615e4 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -50,3 +50,5 @@ def __init__( self.type = type self._id_attrs = (self.type,) + + self._freeze() diff --git a/telegram/_loginurl.py b/telegram/_loginurl.py index 560cf3749d1..867f78c1e92 100644 --- a/telegram/_loginurl.py +++ b/telegram/_loginurl.py @@ -90,3 +90,5 @@ def __init__( self.request_write_access = request_write_access self._id_attrs = (self.url,) + + self._freeze() diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index dbd8dc3ea58..08557063af4 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -62,6 +62,8 @@ def __init__( self._id_attrs = (self.type,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"]: """Converts JSON data to the appropriate :class:`MenuButton` object, i.e. takes @@ -111,6 +113,8 @@ class MenuButtonCommands(MenuButton): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.COMMANDS) + self._freeze() + class MenuButtonWebApp(MenuButton): """Represents a menu button, which launches a @@ -145,6 +149,8 @@ def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: JSONDict = None): self._id_attrs = (self.type, self.text, self.web_app) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButtonWebApp"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -176,3 +182,5 @@ class MenuButtonDefault(MenuButton): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.DEFAULT) + + self._freeze() diff --git a/telegram/_message.py b/telegram/_message.py index 6461c5a0663..bf597817f1c 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -568,6 +568,8 @@ def __init__( self._id_attrs = (self.message_id, self.chat) + self._freeze() + @property def chat_id(self) -> int: """:obj:`int`: Shortcut for :attr:`telegram.Chat.id` for :attr:`chat`.""" diff --git a/telegram/_messageautodeletetimerchanged.py b/telegram/_messageautodeletetimerchanged.py index e4d8f6b7094..e03d6e2ece6 100644 --- a/telegram/_messageautodeletetimerchanged.py +++ b/telegram/_messageautodeletetimerchanged.py @@ -56,3 +56,5 @@ def __init__( self.message_auto_delete_time = message_auto_delete_time self._id_attrs = (self.message_auto_delete_time,) + + self._freeze() diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index 438a2157f78..f3a72daae33 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -104,6 +104,8 @@ def __init__( self._id_attrs = (self.type, self.offset, self.length) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MessageEntity"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_messageid.py b/telegram/_messageid.py index a14c3d3420b..89fd8827333 100644 --- a/telegram/_messageid.py +++ b/telegram/_messageid.py @@ -45,3 +45,5 @@ def __init__(self, message_id: int, api_kwargs: JSONDict = None): self.message_id = message_id self._id_attrs = (self.message_id,) + + self._freeze() diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 053c8255c0e..3c6ad0e6f9b 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -157,6 +157,8 @@ def __init__( self._decrypted_secret: Optional[str] = None self._decrypted_data: Optional["Credentials"] = None + self._freeze() + @property def decrypted_secret(self) -> str: """ @@ -230,6 +232,8 @@ def __init__( self.secure_data = secure_data self.nonce = nonce + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Credentials"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -318,6 +322,8 @@ def __init__( self.passport = passport self.personal_details = personal_details + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureData"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -395,6 +401,8 @@ def __init__( self.files = files self.translation = translation + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureValue"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -440,6 +448,8 @@ def __init__(self, hash: str, secret: str, api_kwargs: JSONDict = None): # skip self.file_hash = self.hash self.data_hash = self.hash + self._freeze() + class DataCredentials(_CredentialsBase): """ @@ -463,6 +473,8 @@ class DataCredentials(_CredentialsBase): def __init__(self, data_hash: str, secret: str, api_kwargs: JSONDict = None): super().__init__(data_hash, secret, api_kwargs=api_kwargs) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() @@ -492,6 +504,8 @@ class FileCredentials(_CredentialsBase): def __init__(self, file_hash: str, secret: str, api_kwargs: JSONDict = None): super().__init__(file_hash, secret, api_kwargs=api_kwargs) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_passport/data.py b/telegram/_passport/data.py index 8bdd38a1155..071bc3006b2 100644 --- a/telegram/_passport/data.py +++ b/telegram/_passport/data.py @@ -86,6 +86,8 @@ def __init__( self.last_name_native = last_name_native self.middle_name_native = middle_name_native + self._freeze() + class ResidentialAddress(TelegramObject): """ @@ -131,6 +133,8 @@ def __init__( self.country_code = country_code self.post_code = post_code + self._freeze() + class IdDocumentData(TelegramObject): """ @@ -155,3 +159,5 @@ def __init__( super().__init__(api_kwargs=api_kwargs) self.document_no = document_no self.expiry_date = expiry_date + + self._freeze() diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 209c88a87b5..fef4fabed73 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -168,6 +168,8 @@ def __init__( self.selfie, ) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["EncryptedPassportElement"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 0a8b6782594..be4d0aa9ac9 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -70,6 +70,8 @@ def __init__( self._decrypted_data: Optional[List[EncryptedPassportElement]] = None self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PassportData"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index 345dd02bb58..68bb9d31733 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -57,6 +57,8 @@ def __init__(self, source: str, type: str, message: str, api_kwargs: JSONDict = self._id_attrs = (self.source, self.type) + self._freeze() + class PassportElementErrorDataField(PassportElementError): """ @@ -105,6 +107,8 @@ def __init__( self._id_attrs = (self.source, self.type, self.field_name, self.data_hash, self.message) + self._freeze() + class PassportElementErrorFile(PassportElementError): """ @@ -143,6 +147,8 @@ def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict self._id_attrs = (self.source, self.type, self.file_hash, self.message) + self._freeze() + class PassportElementErrorFiles(PassportElementError): """ @@ -181,6 +187,8 @@ def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDi self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) + self._freeze() + class PassportElementErrorFrontSide(PassportElementError): """ @@ -219,6 +227,8 @@ def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict self._id_attrs = (self.source, self.type, self.file_hash, self.message) + self._freeze() + class PassportElementErrorReverseSide(PassportElementError): """ @@ -257,6 +267,8 @@ def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict self._id_attrs = (self.source, self.type, self.file_hash, self.message) + self._freeze() + class PassportElementErrorSelfie(PassportElementError): """ @@ -293,6 +305,8 @@ def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict self._id_attrs = (self.source, self.type, self.file_hash, self.message) + self._freeze() + class PassportElementErrorTranslationFile(PassportElementError): """ @@ -333,6 +347,8 @@ def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict self._id_attrs = (self.source, self.type, self.file_hash, self.message) + self._freeze() + class PassportElementErrorTranslationFiles(PassportElementError): """ @@ -373,6 +389,8 @@ def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDi self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) + self._freeze() + class PassportElementErrorUnspecified(PassportElementError): """ @@ -406,3 +424,5 @@ def __init__(self, type: str, element_hash: str, message: str, api_kwargs: JSOND self.element_hash = element_hash self._id_attrs = (self.source, self.type, self.element_hash, self.message) + + self._freeze() diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index ecdbe7b32fd..613e8c1f71b 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -89,6 +89,8 @@ def __init__( self._id_attrs = (self.file_unique_id,) + self._freeze() + @classmethod def de_json_decrypted( cls, data: Optional[JSONDict], bot: "Bot", credentials: "FileCredentials" diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index ff7dded8190..7f429d02b6d 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -89,6 +89,8 @@ def __init__( self.total_amount, ) + self._freeze() + MIN_TITLE_LENGTH: ClassVar[int] = constants.InvoiceLimit.MIN_TITLE_LENGTH """:const:`telegram.constants.InvoiceLimit.MIN_TITLE_LENGTH` diff --git a/telegram/_payment/labeledprice.py b/telegram/_payment/labeledprice.py index fa877be6113..50beef66706 100644 --- a/telegram/_payment/labeledprice.py +++ b/telegram/_payment/labeledprice.py @@ -56,3 +56,5 @@ def __init__(self, label: str, amount: int, api_kwargs: JSONDict = None): self.amount = amount self._id_attrs = (self.label, self.amount) + + self._freeze() diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index 2b90c98c011..686e2bea1a9 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -70,6 +70,8 @@ def __init__( self._id_attrs = (self.name, self.phone_number, self.email, self.shipping_address) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["OrderInfo"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index e1e34e42ead..e74cb4865b5 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -102,6 +102,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PreCheckoutQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_payment/shippingaddress.py b/telegram/_payment/shippingaddress.py index 66d231fc626..c71d1dc70c8 100644 --- a/telegram/_payment/shippingaddress.py +++ b/telegram/_payment/shippingaddress.py @@ -85,3 +85,5 @@ def __init__( self.street_line2, self.post_code, ) + + self._freeze() diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index bddfae5ffe2..f15febc84c0 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -67,6 +67,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index a9066f73721..9608406ce94 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -76,6 +76,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ShippingQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index 651e50cfcab..7e1e7570aae 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -97,6 +97,8 @@ def __init__( self._id_attrs = (self.telegram_payment_charge_id, self.provider_payment_charge_id) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SuccessfulPayment"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_poll.py b/telegram/_poll.py index 8e7b8bdee44..e07cec49e17 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -63,6 +63,8 @@ def __init__(self, text: str, voter_count: int, api_kwargs: JSONDict = None): self._id_attrs = (self.text, self.voter_count) + self._freeze() + MAX_LENGTH: ClassVar[int] = constants.PollLimit.OPTION_LENGTH """:const:`telegram.constants.PollLimit.OPTION_LENGTH`""" @@ -102,6 +104,8 @@ def __init__( self._id_attrs = (self.poll_id, self.user, tuple(self.option_ids)) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PollAnswer"]: """See :meth:`telegram.TelegramObject.de_json`.""" @@ -222,6 +226,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Poll"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index d8ead198bac..4483fcc0724 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -60,6 +60,8 @@ def __init__(self, traveler: User, watcher: User, distance: int, api_kwargs: JSO self._id_attrs = (self.traveler, self.watcher, self.distance) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ProximityAlertTriggered"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 0d92ed82cb8..05d4c9567a8 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -121,6 +121,8 @@ def __init__( self._id_attrs = (self.keyboard,) + self._freeze() + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_replykeyboardremove.py b/telegram/_replykeyboardremove.py index 74cede1dfce..7e53c3c4786 100644 --- a/telegram/_replykeyboardremove.py +++ b/telegram/_replykeyboardremove.py @@ -64,3 +64,5 @@ def __init__(self, selective: bool = None, api_kwargs: JSONDict = None): self.remove_keyboard = True # Optionals self.selective = selective + + self._freeze() diff --git a/telegram/_sentwebappmessage.py b/telegram/_sentwebappmessage.py index 4f6c2df8a4e..5ae5b4af40d 100644 --- a/telegram/_sentwebappmessage.py +++ b/telegram/_sentwebappmessage.py @@ -49,3 +49,5 @@ def __init__(self, inline_message_id: str = None, api_kwargs: JSONDict = None): self.inline_message_id = inline_message_id self._id_attrs = (self.inline_message_id,) + + self._freeze() diff --git a/telegram/_update.py b/telegram/_update.py index 105c6c48215..c34c2ccfe6a 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -264,6 +264,8 @@ def __init__( self._id_attrs = (self.update_id,) + self._freeze() + @property def effective_user(self) -> Optional["User"]: """ diff --git a/telegram/_user.py b/telegram/_user.py index 69f8bee769a..22b4bb90c83 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -159,6 +159,8 @@ def __init__( self._id_attrs = (self.id,) + self._freeze() + @property def name(self) -> str: """:obj:`str`: Convenience property. If available, returns the user's :attr:`username` diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 3fb3ce7c083..c40c4e9dfae 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -60,6 +60,8 @@ def __init__( self._id_attrs = (self.total_count, self.photos) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["UserProfilePhotos"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 2e1f7c6d18d..c881b45b9c0 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -49,6 +49,8 @@ def __init__( ): # skipcq: PTC-W0049 super().__init__(api_kwargs=api_kwargs) + self._freeze() + class VideoChatEnded(TelegramObject): """ @@ -82,6 +84,8 @@ def __init__( self.duration = duration self._id_attrs = (self.duration,) + self._freeze() + class VideoChatParticipantsInvited(TelegramObject): """ @@ -113,6 +117,8 @@ def __init__( self.users = users self._id_attrs = (self.users,) + self._freeze() + @classmethod def de_json( cls, data: Optional[JSONDict], bot: "Bot" @@ -168,6 +174,8 @@ def __init__( self._id_attrs = (self.start_date,) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["VideoChatScheduled"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/_webappdata.py b/telegram/_webappdata.py index d5d048ca3d4..3aa31e19207 100644 --- a/telegram/_webappdata.py +++ b/telegram/_webappdata.py @@ -58,3 +58,5 @@ def __init__(self, data: str, button_text: str, api_kwargs: JSONDict = None): self.button_text = button_text self._id_attrs = (self.data, self.button_text) + + self._freeze() diff --git a/telegram/_webappinfo.py b/telegram/_webappinfo.py index 3417ac39409..48b74d70ca9 100644 --- a/telegram/_webappinfo.py +++ b/telegram/_webappinfo.py @@ -52,3 +52,5 @@ def __init__(self, url: str, api_kwargs: JSONDict = None): self.url = url self._id_attrs = (self.url,) + + self._freeze() diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 9b7dffb1843..a519600dcad 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -130,6 +130,8 @@ def __init__( self.last_synchronization_error_date, ) + self._freeze() + @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["WebhookInfo"]: """See :meth:`telegram.TelegramObject.de_json`.""" diff --git a/telegram/ext/_application.py b/telegram/ext/_application.py index ae2b7245130..fa511ec79ac 100644 --- a/telegram/ext/_application.py +++ b/telegram/ext/_application.py @@ -440,7 +440,7 @@ async def _initialize_persistence(self) -> None: raise ValueError("callback_data must be a tuple of length 2") # Mypy doesn't know that persistence.set_bot (see above) already checks that # self.bot is an instance of ExtBot if callback_data should be stored ... - self.bot.callback_data_cache = CallbackDataCache( # type: ignore[attr-defined] + self.bot.callback_data_cache = CallbackDataCache( self.bot, # type: ignore[arg-type] self.bot.callback_data_cache.maxsize, # type: ignore[attr-defined] persistent_data=persistent_data, diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index d2852e3f466..2e291175522 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -203,6 +203,7 @@ def __init__( private_key=private_key, private_key_password=private_key_password, ) + self._unfreeze() self._defaults = defaults self._rate_limiter = rate_limiter @@ -215,6 +216,8 @@ def __init__( self.arbitrary_callback_data = arbitrary_callback_data self.callback_data_cache: CallbackDataCache = CallbackDataCache(bot=self, maxsize=maxsize) + self._freeze() + async def initialize(self) -> None: """See :meth:`telegram.Bot.initialize`. Also initializes the :paramref:`ExtBot.rate_limiter` (if set) @@ -522,7 +525,7 @@ def _effective_inline_results( # different places new_result = copy(result) markup = self._replace_keyboard(result.reply_markup) # type: ignore[attr-defined] - new_result.reply_markup = markup # type: ignore[attr-defined] + new_result.reply_markup = markup results.append(new_result) return results, next_offset From 76a5aba96c03a43bd7bc923919f225cd8d3e1013 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 18 Sep 2022 18:48:03 +0200 Subject: [PATCH 20/90] add `__delattr__` and add basic test --- telegram/_telegramobject.py | 14 +++++++++++++- tests/test_telegramobject.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index b003f636ce1..e4bc9223c7d 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -101,7 +101,19 @@ def __setattr__(self, key: str, value: object) -> None: super().__setattr__(key, value) return - raise AttributeError(f"Attribute {key} of class {self.__class__.__name__} can't be set!") + raise AttributeError( + f"Attribute `{key}` of class `{self.__class__.__name__}` can't be set!" + ) + + def __delattr__(self, key: str) -> None: + # protected attributes can always be set for convenient internal use + if (key == "_frozen") or (not self._frozen) or key.startswith("_"): + super().__delattr__(key) + return + + raise AttributeError( + f"Attribute `{key}` of class `{self.__class__.__name__}` can't be deleted!" + ) def __str__(self) -> str: return str(self.to_dict()) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index e0f6609b313..b4f61af039e 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -225,3 +225,35 @@ def test_subclasses_are_frozen(self, cls): source_lines, first_line = inspect.getsourcelines(cls.__init__) assert " self._freeze()" in source_lines[-1], f"{cls.__name__} is not frozen correctly" + + def test_freeze_unfreeze(self): + class TestSub(TelegramObject): + def __init__(self): + super().__init__() + self._protected = True + self.public = True + self._freeze() + + foo = TestSub() + foo._protected = False + assert foo._protected is False + + with pytest.raises( + AttributeError, match="Attribute `public` of class `TestSub` can't be set!" + ): + foo.public = False + + with pytest.raises( + AttributeError, match="Attribute `public` of class `TestSub` can't be deleted!" + ): + del foo.public + + foo._unfreeze() + foo._protected = True + assert foo._protected is True + foo.public = False + assert foo.public is False + del foo.public + del foo._protected + assert not hasattr(foo, "public") + assert not hasattr(foo, "_protected") From 92c1d877c473dc5be4e4c489153112f2e682ce44 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 18 Sep 2022 19:43:38 +0200 Subject: [PATCH 21/90] Unfreeze after `super().__init__` where necessary --- telegram/_botcommandscope.py | 7 +++++++ telegram/_chatmember.py | 6 ++++++ telegram/_files/_basethumbedmedium.py | 1 + telegram/_files/animation.py | 1 + telegram/_files/audio.py | 1 + telegram/_files/document.py | 1 + telegram/_files/inputmedia.py | 5 +++++ telegram/_files/photosize.py | 1 + telegram/_files/sticker.py | 1 + telegram/_files/video.py | 1 + telegram/_files/videonote.py | 1 + telegram/_files/voice.py | 1 + telegram/_inline/inlinequeryresultarticle.py | 1 + telegram/_inline/inlinequeryresultaudio.py | 1 + telegram/_inline/inlinequeryresultcachedaudio.py | 1 + telegram/_inline/inlinequeryresultcacheddocument.py | 1 + telegram/_inline/inlinequeryresultcachedgif.py | 1 + telegram/_inline/inlinequeryresultcachedmpeg4gif.py | 1 + telegram/_inline/inlinequeryresultcachedphoto.py | 1 + telegram/_inline/inlinequeryresultcachedsticker.py | 1 + telegram/_inline/inlinequeryresultcachedvideo.py | 1 + telegram/_inline/inlinequeryresultcachedvoice.py | 1 + telegram/_inline/inlinequeryresultcontact.py | 1 + telegram/_inline/inlinequeryresultdocument.py | 1 + telegram/_inline/inlinequeryresultgame.py | 1 + telegram/_inline/inlinequeryresultgif.py | 1 + telegram/_inline/inlinequeryresultlocation.py | 1 + telegram/_inline/inlinequeryresultmpeg4gif.py | 1 + telegram/_inline/inlinequeryresultphoto.py | 1 + telegram/_inline/inlinequeryresultvenue.py | 1 + telegram/_inline/inlinequeryresultvideo.py | 1 + telegram/_inline/inlinequeryresultvoice.py | 1 + telegram/_menubutton.py | 3 +++ telegram/_passport/credentials.py | 11 +---------- telegram/_passport/passportelementerrors.py | 11 ++++++++++- 35 files changed, 61 insertions(+), 11 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index bc1070d3030..10137726d38 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -136,6 +136,7 @@ class BotCommandScopeDefault(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs) + self._unfreeze() self._freeze() @@ -155,6 +156,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs) + self._unfreeze() self._freeze() @@ -174,6 +176,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs) + self._unfreeze() self._freeze() @@ -193,6 +196,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) + self._unfreeze() self._freeze() @@ -221,6 +225,7 @@ class BotCommandScopeChat(BotCommandScope): def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs) + self._unfreeze() self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) @@ -254,6 +259,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) + self._unfreeze() self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) @@ -290,6 +296,7 @@ class BotCommandScopeChatMember(BotCommandScope): def __init__(self, chat_id: Union[str, int], user_id: int, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs) + self._unfreeze() self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index e7c3495d8d2..030d42d12d4 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -170,6 +170,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.OWNER, user=user, api_kwargs=api_kwargs) + self._unfreeze() self.is_anonymous = is_anonymous self.custom_title = custom_title @@ -296,6 +297,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.ADMINISTRATOR, user=user, api_kwargs=api_kwargs) + self._unfreeze() self.can_be_edited = can_be_edited self.is_anonymous = is_anonymous self.can_manage_chat = can_manage_chat @@ -341,6 +343,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) + self._unfreeze() self._freeze() @@ -434,6 +437,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.RESTRICTED, user=user, api_kwargs=api_kwargs) + self._unfreeze() self.is_member = is_member self.can_change_info = can_change_info self.can_invite_users = can_invite_users @@ -475,6 +479,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs) + self._unfreeze() self._freeze() @@ -512,6 +517,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs) + self._unfreeze() self.until_date = until_date self._freeze() diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index d20b819e5bb..8bc3d7a1bfb 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -76,6 +76,7 @@ def __init__( file_size=file_size, api_kwargs=api_kwargs, ) + self._unfreeze() self.thumb = thumb @classmethod diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index a264b43eca4..f3e70d793c6 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -84,6 +84,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.width = width self.height = height diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index fde9741e0ff..adec7e9f0b4 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -88,6 +88,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.duration = duration # Optional diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 2b511f83c75..695562630ce 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -75,6 +75,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) + self._unfreeze() # Optional self.mime_type = mime_type self.file_name = file_name diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 8407efa1996..cd102501785 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -187,6 +187,7 @@ def __init__( media = parse_file_input(media, filename=filename, attach=True) super().__init__(InputMediaType.ANIMATION, media, caption, caption_entities, parse_mode) + self._unfreeze() self.thumb = self._parse_thumb_input(thumb) self.width = width self.height = height @@ -244,6 +245,7 @@ def __init__( ): media = parse_file_input(media, PhotoSize, filename=filename, attach=True) super().__init__(InputMediaType.PHOTO, media, caption, caption_entities, parse_mode) + self._unfreeze() self._freeze() @@ -338,6 +340,7 @@ def __init__( media = parse_file_input(media, filename=filename, attach=True) super().__init__(InputMediaType.VIDEO, media, caption, caption_entities, parse_mode) + self._unfreeze() self.width = width self.height = height self.duration = duration @@ -431,6 +434,7 @@ def __init__( media = parse_file_input(media, filename=filename, attach=True) super().__init__(InputMediaType.AUDIO, media, caption, caption_entities, parse_mode) + self._unfreeze() self.thumb = self._parse_thumb_input(thumb) self.duration = duration self.title = title @@ -506,6 +510,7 @@ def __init__( ): media = parse_file_input(media, Document, filename=filename, attach=True) super().__init__(InputMediaType.DOCUMENT, media, caption, caption_entities, parse_mode) + self._unfreeze() self.thumb = self._parse_thumb_input(thumb) self.disable_content_type_detection = disable_content_type_detection diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 0436920342e..58eb493f8ec 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -70,6 +70,7 @@ def __init__( file_size=file_size, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.width = width self.height = height diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 5d44b6249b9..59fe015b0b4 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -152,6 +152,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.width = width self.height = height diff --git a/telegram/_files/video.py b/telegram/_files/video.py index 938372975c4..1d42b17e559 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -84,6 +84,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.width = width self.height = height diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index bf780f10e83..ea5db220a21 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -75,6 +75,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.length = length self.duration = duration diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index 9c7968617bc..bb5dc8c2427 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -69,6 +69,7 @@ def __init__( file_size=file_size, api_kwargs=api_kwargs, ) + self._unfreeze() # Required self.duration = duration # Optional diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index 5f97b69e0f3..0ec642450c8 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -96,6 +96,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.ARTICLE, id, api_kwargs=api_kwargs) + self._unfreeze() self.title = title self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index bbbf23ca23b..8ed19c2ca82 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -109,6 +109,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) + self._unfreeze() self.audio_url = audio_url self.title = title diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 99bfdc037b3..0bfda1d168c 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -96,6 +96,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) + self._unfreeze() self.audio_file_id = audio_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 615aa7b5f3d..88c6fc8bdc2 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -104,6 +104,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) + self._unfreeze() self.title = title self.document_file_id = document_file_id diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 6462722252d..c847377e2a2 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -101,6 +101,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) + self._unfreeze() self.gif_file_id = gif_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index 6a9ba794a0e..fb2d4bce7e8 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -101,6 +101,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) + self._unfreeze() self.mpeg4_file_id = mpeg4_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index fda76e1d334..46efdb56ce2 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -105,6 +105,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) + self._unfreeze() self.photo_file_id = photo_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index bdd1b25b57d..9aef4de005f 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -66,6 +66,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.STICKER, id, api_kwargs=api_kwargs) + self._unfreeze() self.sticker_file_id = sticker_file_id # Optionals diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 2064718be57..f2deeb60b3e 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -105,6 +105,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) + self._unfreeze() self.video_file_id = video_file_id self.title = title diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index f34097d2211..16d2390a5d4 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -100,6 +100,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) + self._unfreeze() self.voice_file_id = voice_file_id self.title = title diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index 6430bc68fc3..c66a538d853 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -99,6 +99,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.CONTACT, id, api_kwargs=api_kwargs) + self._unfreeze() self.phone_number = phone_number self.first_name = first_name diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index c4ce43b1f7a..6e163179e27 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -126,6 +126,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) + self._unfreeze() self.document_url = document_url self.title = title self.mime_type = mime_type diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 207e1f90dd3..9fffae8394c 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -56,6 +56,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.GAME, id, api_kwargs=api_kwargs) + self._unfreeze() self.id = id self.game_short_name = game_short_name diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index b7c392de4d4..fafda19a94a 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -127,6 +127,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) + self._unfreeze() self.gif_url = gif_url self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index 7a4a642aba9..70fd47fb9d8 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -119,6 +119,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.LOCATION, id, api_kwargs=api_kwargs) + self._unfreeze() self.latitude = latitude self.longitude = longitude self.title = title diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index cd2ba148ef7..d64c6ee7c90 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -127,6 +127,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) + self._unfreeze() self.mpeg4_url = mpeg4_url self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 2bdd9a5a62b..e40b46d6623 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -121,6 +121,7 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) + self._unfreeze() self.photo_url = photo_url self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 436dc02ed6c..c866d250c3a 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -122,6 +122,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.VENUE, id, api_kwargs=api_kwargs) + self._unfreeze() self.latitude = latitude self.longitude = longitude self.title = title diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 93cc9caacb3..6bc060615c2 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -137,6 +137,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) + self._unfreeze() self.video_url = video_url self.mime_type = mime_type self.thumb_url = thumb_url diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index 56b82b20bab..53bdefad532 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -109,6 +109,7 @@ def __init__( # Required super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) + self._unfreeze() self.voice_url = voice_url self.title = title diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 08557063af4..4dc75c79de9 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -112,6 +112,7 @@ class MenuButtonCommands(MenuButton): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.COMMANDS) + self._unfreeze() self._freeze() @@ -144,6 +145,7 @@ class MenuButtonWebApp(MenuButton): def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.WEB_APP) + self._unfreeze() self.text = text self.web_app = web_app @@ -182,5 +184,6 @@ class MenuButtonDefault(MenuButton): def __init__(self, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.DEFAULT) + self._unfreeze() self._freeze() diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 3c6ad0e6f9b..675db722575 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -441,6 +441,7 @@ class _CredentialsBase(TelegramObject): def __init__(self, hash: str, secret: str, api_kwargs: JSONDict = None): # skipcq: PYL-W0622 super().__init__(api_kwargs=api_kwargs) + super()._unfreeze() self.hash = hash self.secret = secret @@ -470,11 +471,6 @@ class DataCredentials(_CredentialsBase): __slots__ = () - def __init__(self, data_hash: str, secret: str, api_kwargs: JSONDict = None): - super().__init__(data_hash, secret, api_kwargs=api_kwargs) - - self._freeze() - def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() @@ -501,11 +497,6 @@ class FileCredentials(_CredentialsBase): __slots__ = () - def __init__(self, file_hash: str, secret: str, api_kwargs: JSONDict = None): - super().__init__(file_hash, secret, api_kwargs=api_kwargs) - - self._freeze() - def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index 68bb9d31733..29e7b91f2e3 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -101,7 +101,8 @@ def __init__( api_kwargs: JSONDict = None, ): # Required - super().__init__("data", type, message) + super().__init__("data", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.field_name = field_name self.data_hash = data_hash @@ -143,6 +144,7 @@ class PassportElementErrorFile(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("file", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -183,6 +185,7 @@ class PassportElementErrorFiles(PassportElementError): def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("files", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hashes = file_hashes self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) @@ -223,6 +226,7 @@ class PassportElementErrorFrontSide(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("front_side", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -263,6 +267,7 @@ class PassportElementErrorReverseSide(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("reverse_side", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -301,6 +306,7 @@ class PassportElementErrorSelfie(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("selfie", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -343,6 +349,7 @@ class PassportElementErrorTranslationFile(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("translation_file", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -385,6 +392,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("translation_files", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.file_hashes = file_hashes self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) @@ -421,6 +429,7 @@ class PassportElementErrorUnspecified(PassportElementError): def __init__(self, type: str, element_hash: str, message: str, api_kwargs: JSONDict = None): # Required super().__init__("unspecified", type, message, api_kwargs=api_kwargs) + self._unfreeze() self.element_hash = element_hash self._id_attrs = (self.source, self.type, self.element_hash, self.message) From 43cd729c1cf92d4b3a40af46d3a2803912647e9c Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 20 Sep 2022 09:31:14 +0200 Subject: [PATCH 22/90] Get started on adapting tests --- telegram/_inline/inlinekeyboardbutton.py | 2 ++ tests/conftest.py | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 8cf5a748b22..7b4fd95b2ad 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -232,5 +232,7 @@ def update_callback_data(self, callback_data: Union[str, object]) -> None: Args: callback_data (:class:`object`): The new callback data. """ + self._unfreeze() self.callback_data = callback_data + self._freeze() self._set_id_attrs() diff --git a/tests/conftest.py b/tests/conftest.py index d8ec91ddb48..8807b6d9598 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -140,11 +140,17 @@ async def _request_wrapper( class DictExtBot(ExtBot): - pass + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Makes it easier to work with the bot in tests + self._unfreeze() class DictBot(Bot): - pass + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Makes it easier to work with the bot in tests + self._unfreeze() class DictApplication(Application): From bc9d879b92ab0419102ff7d5b7989d247d6802d4 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 20 Sep 2022 22:19:04 +0200 Subject: [PATCH 23/90] Get a few more tests running --- telegram/_bot.py | 4 ++++ telegram/_telegramobject.py | 20 ++++++++++++++++++-- telegram/ext/_application.py | 2 ++ telegram/ext/_extbot.py | 7 +++++++ tests/test_choseninlineresult.py | 4 +++- tests/test_filters.py | 11 ++++++++++- tests/test_telegramobject.py | 16 +++++++++++++++- 7 files changed, 59 insertions(+), 5 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 56057db4883..cb89829dfe1 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -272,10 +272,14 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: # pylint: disable= for key, val in data.items(): # 1) if isinstance(val, InputMedia): + val._unfreeze() # pylint: disable=protected-access val.parse_mode = DefaultValue.get_value(val.parse_mode) + val._freeze() # pylint: disable=protected-access elif key == "media" and isinstance(val, list): for media in val: + media._unfreeze() # pylint: disable=protected-access media.parse_mode = DefaultValue.get_value(media.parse_mode) + media._freeze() # pylint: disable=protected-access # 2) else: data[key] = DefaultValue.get_value(val) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index e4bc9223c7d..306dccf1036 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -97,7 +97,7 @@ def _apply_api_kwargs(self) -> None: def __setattr__(self, key: str, value: object) -> None: # protected attributes can always be set for convenient internal use - if (key == "_frozen") or (not self._frozen) or key.startswith("_"): + if (key == "_frozen") or (not getattr(self, "_frozen", True)) or key.startswith("_"): super().__setattr__(key, value) return @@ -107,7 +107,7 @@ def __setattr__(self, key: str, value: object) -> None: def __delattr__(self, key: str) -> None: # protected attributes can always be set for convenient internal use - if (key == "_frozen") or (not self._frozen) or key.startswith("_"): + if (key == "_frozen") or (not getattr(self, "_frozen", True)) or key.startswith("_"): super().__delattr__(key) return @@ -141,10 +141,18 @@ def __setstate__(self, state: dict) -> None: This method is used for unpickling. The data, which is in the form a dictionary, is converted back into a class. Should be modified in place. """ + self._unfreeze() for key, val in state.items(): + if key == "_frozen": + # Setting the frozen status to True would prevent the attributes from being set + continue setattr(self, key, val) self._apply_api_kwargs() + # Apply freezing if necessary + if state["_frozen"]: + self._freeze() + def __deepcopy__(self: TO_co, memodict: dict) -> TO_co: """This method deepcopies the object and sets the bot on the newly created copy.""" bot = self._bot # Save bot so we can set it after copying @@ -154,10 +162,18 @@ def __deepcopy__(self: TO_co, memodict: dict) -> TO_co: memodict[id(self)] = result # save the id of the object in the dict attrs = self._get_attrs(include_private=True) # get all its attributes + setattr(result, "_frozen", False) # unfreeze the new object for setting the attributes for k in attrs: # now we set the attributes in the deepcopied object + if k == "_frozen": + # Setting the frozen status to True would prevent the attributes from being set + continue setattr(result, k, deepcopy(getattr(self, k), memodict)) + # Apply freezing if necessary + if self._frozen: + result._freeze() + result.set_bot(bot) # Assign the bots back self.set_bot(bot) return result diff --git a/telegram/ext/_application.py b/telegram/ext/_application.py index fa511ec79ac..0275eac35ed 100644 --- a/telegram/ext/_application.py +++ b/telegram/ext/_application.py @@ -440,11 +440,13 @@ async def _initialize_persistence(self) -> None: raise ValueError("callback_data must be a tuple of length 2") # Mypy doesn't know that persistence.set_bot (see above) already checks that # self.bot is an instance of ExtBot if callback_data should be stored ... + self.bot._unfreeze() # pylint: disable=protected-access self.bot.callback_data_cache = CallbackDataCache( self.bot, # type: ignore[arg-type] self.bot.callback_data_cache.maxsize, # type: ignore[attr-defined] persistent_data=persistent_data, ) + self.bot._freeze() # pylint: disable=protected-access @staticmethod def builder() -> "InitApplicationBuilder": diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 2e291175522..02271327e62 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -355,11 +355,15 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: # 3) elif isinstance(val, InputMedia) and val.parse_mode is DEFAULT_NONE: + val._unfreeze() # pylint: disable=protected-access val.parse_mode = self.defaults.parse_mode if self.defaults else None + val._freeze() # pylint: disable=protected-access elif key == "media" and isinstance(val, list): for media in val: if media.parse_mode is DEFAULT_NONE: + media._unfreeze() # pylint: disable=protected-access media.parse_mode = self.defaults.parse_mode if self.defaults else None + media._freeze() # pylint: disable=protected-access def _replace_keyboard(self, reply_markup: Optional[ReplyMarkup]) -> Optional[ReplyMarkup]: # If the reply_markup is an inline keyboard and we allow arbitrary callback data, let the @@ -536,6 +540,7 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> None: `obj`. Overriding this to call insert the actual desired default values. """ + res._unfreeze() # pylint: disable=protected-access if hasattr(res, "parse_mode") and res.parse_mode is DEFAULT_NONE: res.parse_mode = self.defaults.parse_mode if self.defaults else None if hasattr(res, "input_message_content") and res.input_message_content: @@ -554,6 +559,8 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> None: self.defaults.disable_web_page_preview if self.defaults else None ) + res._freeze() # pylint: disable=protected-access + async def stop_poll( self, chat_id: Union[int, str], diff --git a/tests/test_choseninlineresult.py b/tests/test_choseninlineresult.py index 70c55975aba..428337c0225 100644 --- a/tests/test_choseninlineresult.py +++ b/tests/test_choseninlineresult.py @@ -24,7 +24,9 @@ @pytest.fixture(scope="class") def user(): - return User(1, "First name", False) + user = User(1, "First name", False) + user._unfreeze() + return user @pytest.fixture(scope="class") diff --git a/tests/test_filters.py b/tests/test_filters.py index a3fcb5c3280..05f8a12bd6c 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -39,7 +39,7 @@ @pytest.fixture(scope="function") def update(): - return Update( + update = Update( 0, Message( 0, @@ -52,6 +52,15 @@ def update(): forward_from_chat=Chat(0, "Channel"), ), ) + update._unfreeze() + update.message._unfreeze() + update.message.chat._unfreeze() + update.message.from_user._unfreeze() + update.message.via_bot._unfreeze() + update.message.sender_chat._unfreeze() + update.message.forward_from._unfreeze() + update.message.forward_from_chat._unfreeze() + return update @pytest.fixture(scope="function", params=MessageEntity.ALL_TYPES) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index b4f61af039e..f7f0ee55b66 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -20,6 +20,7 @@ import inspect import pickle from copy import deepcopy +from pathlib import Path import pytest @@ -202,6 +203,17 @@ def test_deepcopy_telegram_obj(self, bot): assert new_msg.from_user == user and new_msg.from_user is not user assert new_msg.photo[0] == photo and new_msg.photo[0] is not photo + # check that deepcopy preserves the freezing status + with pytest.raises( + AttributeError, match="Attribute `text` of class `Message` can't be set!" + ): + new_msg.text = "new text" + + msg._unfreeze() + new_message = deepcopy(msg) + new_message.text = "new text" + assert new_message.text == "new text" + def test_deepcopy_subclass_telegram_obj(self, bot): s = self.Sub("private", "normal", bot) d = deepcopy(s) @@ -219,7 +231,9 @@ def test_subclasses_are_frozen(self, cls): # args. So we inspect the code instead. source_file = inspect.getsourcefile(cls.__init__) - if source_file.endswith("telegramobject.py"): + parents = Path(source_file).parents + is_test_file = Path(__file__).parent.resolve() in parents + if is_test_file or source_file.endswith("telegramobject.py"): # classes without their own `__init__` can be ignored return From 21a75aa044815b22d520025be7601e0c8a2240e3 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 08:58:06 +0200 Subject: [PATCH 24/90] Get startet on review --- telegram/_botcommandscope.py | 1 + telegram/_chat.py | 5 ++- telegram/_chatinvitelink.py | 2 +- telegram/_chatmember.py | 3 +- telegram/_chatmemberupdated.py | 2 +- telegram/_choseninlineresult.py | 2 +- telegram/_files/sticker.py | 4 +-- telegram/_files/venue.py | 2 +- telegram/_games/game.py | 2 +- telegram/_games/gamehighscore.py | 2 +- telegram/_inline/inlinekeyboardbutton.py | 2 +- telegram/_inline/inlinequeryresultarticle.py | 3 ++ telegram/_inline/inlinequeryresultaudio.py | 3 ++ .../_inline/inlinequeryresultcachedaudio.py | 3 ++ .../inlinequeryresultcacheddocument.py | 3 ++ .../_inline/inlinequeryresultcachedgif.py | 3 ++ .../inlinequeryresultcachedmpeg4gif.py | 3 ++ .../_inline/inlinequeryresultcachedphoto.py | 3 ++ .../_inline/inlinequeryresultcachedsticker.py | 3 ++ .../_inline/inlinequeryresultcachedvideo.py | 3 ++ .../_inline/inlinequeryresultcachedvoice.py | 3 ++ telegram/_keyboardbutton.py | 2 +- telegram/_messageentity.py | 2 +- telegram/_payment/orderinfo.py | 2 +- telegram/_payment/successfulpayment.py | 2 +- telegram/_poll.py | 4 +-- telegram/_telegramobject.py | 31 ++++++++++--------- telegram/_update.py | 2 +- telegram/_userprofilephotos.py | 2 +- telegram/_videochat.py | 6 ++-- 30 files changed, 72 insertions(+), 38 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 730bff40dfc..2aab03cb98c 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -143,6 +143,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): .. versionchanged:: 20.0 |removedkwargs| + Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_PRIVATE_CHATS`. """ diff --git a/telegram/_chat.py b/telegram/_chat.py index 7d591343acf..b6e3572cb3c 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -74,7 +74,10 @@ class Chat(TelegramObject): and notice that some positional arguments changed position as a result. .. versionchanged:: 20.0 - |removedbotandkwargs| + * |removedbotandkwargs| + * Removed the attribute ``all_members_are_administrators``. As long as Telegram provides + this field for backwards compatibility, it is available through + :attr:`~telegram.TelegramObject.api_kwargs``. Args: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index 8adf692814d..0e400420908 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -149,7 +149,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatInviteLi data["creator"] = User.de_json(data.get("creator"), bot) data["expire_date"] = from_timestamp(data.get("expire_date", None)) - return cls(**data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index b23f7268e86..40995fe9d3b 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -118,7 +118,8 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"] return _class_mapping[data["status"]].de_json(data=data, bot=bot) data["user"] = User.de_json(data.get("user"), bot) - data["until_date"] = from_timestamp(data.get("until_date", None)) + if "until_date" in data: + data["until_date"] = from_timestamp(data["until_date"]) return super().de_json(data=data, bot=bot) diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index f69df208233..287e7399f53 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -122,7 +122,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMemberUp data["new_chat_member"] = ChatMember.de_json(data.get("new_chat_member"), bot) data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index 334d80738c8..20bad19144f 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -101,4 +101,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChosenInline # Optionals data["location"] = Location.de_json(data.get("location"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 6ca76a462b1..23b6603baca 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -108,7 +108,7 @@ class Sticker(_BaseThumbedMedium): premium animation for the sticker. .. versionadded:: 20.0 - custom_emoji (:obj:`str`): Optional. For custom emoji stickers, unique identifier of the + custom_emoji_id (:obj:`str`): Optional. For custom emoji stickers, unique identifier of the custom emoji. .. versionadded:: 20.0 @@ -361,4 +361,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MaskPosition if data is None: return None - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index 48a3c3103a7..9c1eb9a3f9b 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -109,4 +109,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Venue"]: data["location"] = Location.de_json(data.get("location"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 815481452a0..1edc738f8be 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -117,7 +117,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Game"]: data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot) data["animation"] = Animation.de_json(data.get("animation"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_games/gamehighscore.py b/telegram/_games/gamehighscore.py index be58fd8f39b..3abe9438cb9 100644 --- a/telegram/_games/gamehighscore.py +++ b/telegram/_games/gamehighscore.py @@ -66,4 +66,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["GameHighScor data["user"] = User.de_json(data.get("user"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 6714fdf4a03..07ce1880267 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -218,7 +218,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineKeyboa data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot) data["callback_game"] = CallbackGame.de_json(data.get("callback_game"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) def update_callback_data(self, callback_data: Union[str, object]) -> None: """ diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index ed98f7a6159..30e86e09040 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -34,6 +34,9 @@ class InlineQueryResultArticle(InlineQueryResult): .. seealso:: `Inline Example `_ + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. title (:obj:`str`): Title of the result. diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 80982328cee..2a58c022153 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -37,6 +37,9 @@ class InlineQueryResultAudio(InlineQueryResult): Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the audio. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. audio_url (:obj:`str`): A valid URL for the audio file. diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 62c97ee3118..88c75c56251 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -37,6 +37,9 @@ class InlineQueryResultCachedAudio(InlineQueryResult): file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the audio. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. audio_file_id (:obj:`str`): A valid file identifier for the audio file. diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 29d08a3ce56..4a01ff89191 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -37,6 +37,9 @@ class InlineQueryResultCachedDocument(InlineQueryResult): by the user with an optional caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the file. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. title (:obj:`str`): Title for the result. diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index b60903eb1a2..d6298742913 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -38,6 +38,9 @@ class InlineQueryResultCachedGif(InlineQueryResult): use :attr:`input_message_content` to send a message with specified content instead of the animation. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. gif_file_id (:obj:`str`): A valid file identifier for the GIF file. diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index da565c47116..fb5ffdb801e 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -38,6 +38,9 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): optional caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the animation. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file. diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 7496d55e042..ac18910be66 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -38,6 +38,9 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): :attr:`input_message_content` to send a message with the specified content instead of the photo. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. photo_file_id (:obj:`str`): A valid file identifier of the photo. diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index 91143b33b36..e1d609aa976 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -35,6 +35,9 @@ class InlineQueryResultCachedSticker(InlineQueryResult): be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the sticker. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. sticker_file_id (:obj:`str`): A valid file identifier of the sticker. diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 26c1c9c9084..41ec1ae51f1 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -38,6 +38,9 @@ class InlineQueryResultCachedVideo(InlineQueryResult): :attr:`input_message_content` to send a message with the specified content instead of the video. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. video_file_id (:obj:`str`): A valid file identifier for the video file. diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 472f9febcf2..a57f936407c 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -37,6 +37,9 @@ class InlineQueryResultCachedVoice(InlineQueryResult): message will be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the voice message. + .. versionchanged:: 20.0 + |removedbotandkwargs| + Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. voice_file_id (:obj:`str`): A valid file identifier for the voice message. diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index 1b4b187a71d..da2d65bea9c 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -118,4 +118,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["KeyboardButt data["request_poll"] = KeyboardButtonPollType.de_json(data.get("request_poll"), bot) data["web_app"] = WebAppInfo.de_json(data.get("web_app"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index 438a2157f78..9b6d0ef0b1b 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -114,7 +114,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MessageEntit data["user"] = User.de_json(data.get("user"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) MENTION: ClassVar[str] = constants.MessageEntityType.MENTION """:const:`telegram.constants.MessageEntityType.MENTION`""" diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index 2b90c98c011..55e757b21cc 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -80,4 +80,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["OrderInfo"]: data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index 651e50cfcab..177cb8846c8 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -107,4 +107,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SuccessfulPa data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_poll.py b/telegram/_poll.py index 8e7b8bdee44..0dc2ab08070 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -112,7 +112,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PollAnswer"] data["user"] = User.de_json(data.get("user"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) class Poll(TelegramObject): @@ -234,7 +234,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Poll"]: data["explanation_entities"] = MessageEntity.de_list(data.get("explanation_entities"), bot) data["close_date"] = from_timestamp(data.get("close_date")) - return cls(**data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 6b0634ce156..27a244aabca 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -61,6 +61,8 @@ class TelegramObject: __slots__ = ("_id_attrs", "_bot", "api_kwargs") + # Used to cache the names of the parameters of the __init__ method of the class + # Must be a private attribute to avoid name clashes between subclasses __INIT_PARAMS: Optional[Set[str]] = None def __init__(self, api_kwargs: JSONDict = None) -> None: @@ -209,30 +211,29 @@ def de_json(cls: Type[Tele_co], data: Optional[JSONDict], bot: "Bot") -> Optiona def _de_json( cls: Type[Tele_co], data: Optional[JSONDict], bot: "Bot", api_kwargs: JSONDict = None ) -> Optional[Tele_co]: - if cls.__INIT_PARAMS is None: - signature = inspect.signature(cls) - cls.__INIT_PARAMS = set(signature.parameters.keys()) - if data is None: return None - api_kwargs = api_kwargs or {} - # try-except is significantly faster in case we already have a correct argument set try: obj = cls(**data, api_kwargs=api_kwargs) except TypeError as exc: - if "__init__() got an unexpected keyword argument" in str(exc): - kwargs: JSONDict = {} - for key, value in data.items(): - if key in cls.__INIT_PARAMS: # pylint: disable=unsupported-membership-test - kwargs[key] = value - else: - api_kwargs[key] = value - obj = cls(api_kwargs=api_kwargs, **kwargs) - else: + if "__init__() got an unexpected keyword argument" not in str(exc): raise exc + if cls.__INIT_PARAMS is None: + signature = inspect.signature(cls) + cls.__INIT_PARAMS = set(signature.parameters.keys()) + + api_kwargs = api_kwargs or {} + existing_kwargs: JSONDict = {} + for key, value in data.items(): + if key in cls.__INIT_PARAMS: # pylint: disable=unsupported-membership-test + existing_kwargs[key] = value + else: + api_kwargs[key] = value + obj = cls(api_kwargs=api_kwargs, **existing_kwargs) + obj.set_bot(bot=bot) return obj diff --git a/telegram/_update.py b/telegram/_update.py index 105c6c48215..2c885d20861 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -423,4 +423,4 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Update"]: data["chat_member"] = ChatMemberUpdated.de_json(data.get("chat_member"), bot) data["chat_join_request"] = ChatJoinRequest.de_json(data.get("chat_join_request"), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 3fb3ce7c083..ac5eb741c58 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -70,7 +70,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["UserProfileP data["photos"] = [PhotoSize.de_list(photo, bot) for photo in data["photos"]] - return cls(**data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 2e1f7c6d18d..73240f9f852 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -124,7 +124,7 @@ def de_json( return None data["users"] = User.de_list(data.get("users", []), bot) - return cls(**data) + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" @@ -178,9 +178,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["VideoChatSch data["start_date"] = from_timestamp(data["start_date"]) - obj = cls(**data) - obj.set_bot(bot) - return obj + return super().de_json(data=data, bot=bot) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" From ea3f13daed2a35abf313e1131ebeb1a4c4e56d8c Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 09:32:35 +0200 Subject: [PATCH 25/90] More review & dropping some unneeded `_parse_data` calls --- telegram/_botcommandscope.py | 2 -- telegram/_chatpermissions.py | 3 --- telegram/_files/sticker.py | 10 ---------- telegram/_inline/inlinekeyboardmarkup.py | 2 -- telegram/_menubutton.py | 2 -- tests/test_telegramobject.py | 14 ++++++++++++++ 6 files changed, 14 insertions(+), 19 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 2aab03cb98c..77324cc1596 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -96,8 +96,6 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc The Telegram object. """ - data = cls._parse_data(data) - if not data: return None diff --git a/telegram/_chatpermissions.py b/telegram/_chatpermissions.py index 5e70405623b..21ec8f01c57 100644 --- a/telegram/_chatpermissions.py +++ b/telegram/_chatpermissions.py @@ -35,9 +35,6 @@ class ChatPermissions(TelegramObject): permissions that are set, but also sets all the others to :obj:`False`. However, since not documented, this behaviour may change unbeknown to PTB. - .. versionchanged:: 20.0 - |removedkwargs| - Args: can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to send text messages, contacts, locations and venues. diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 23b6603baca..f785799abb3 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -352,13 +352,3 @@ def __init__( self.scale = scale self._id_attrs = (self.point, self.x_shift, self.y_shift, self.scale) - - @classmethod - def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MaskPosition"]: - """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - - if data is None: - return None - - return super().de_json(data=data, bot=bot) diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 997fa03f53e..943232a6a08 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -83,8 +83,6 @@ def to_dict(self) -> JSONDict: @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineKeyboardMarkup"]: """See :meth:`telegram.TelegramObject.de_json`.""" - data = cls._parse_data(data) - if not data: return None diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index dbd8dc3ea58..808a87f88e1 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -75,8 +75,6 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] The Telegram object. """ - data = cls._parse_data(data) - if not data: return None diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index f91b1d3f2b5..96c58d1fc1a 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -59,6 +59,20 @@ def test_to_json(self, monkeypatch): with pytest.raises(TypeError): telegram_object.to_json() + def test_de_json_api_kwargs(self, bot): + to = TelegramObject.de_json(data={"foo": "bar"}, bot=bot) + assert to.api_kwargs == {"foo": "bar"} + assert to.get_bot() is bot + + def test_de_json_arbitrary_exceptions(self, bot): + class SubClass(TelegramObject): + def __init__(self, **kwargs): + super().__init__(**kwargs) + raise RuntimeError("This is a test") + + with pytest.raises(RuntimeError, match="This is a test"): + SubClass.de_json({}, bot) + def test_to_dict_private_attribute(self): class TelegramObjectSubclass(TelegramObject): __slots__ = ("a", "_b") # Added slots so that the attrs are converted to dict From 3ff5caef04e2dc8736c7e1a84d8298de1934df87 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 10:20:33 +0200 Subject: [PATCH 26/90] more review --- telegram/_telegramobject.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 27a244aabca..0eb1bf7007f 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -116,6 +116,10 @@ def __setstate__(self, state: dict) -> None: This method is used for unpickling. The data, which is in the form a dictionary, is converted back into a class. Should be modified in place. """ + # Make sure that we have a `_bot` attribute. This is necessary, since __getstate__ omits + # this as Bots are not pickable. + setattr(self, "_bot", None) + for key, val in state.items(): setattr(self, key, val) self._apply_api_kwargs() @@ -182,7 +186,7 @@ def _get_attrs( if recursive and data.get("from_user"): data["from"] = data.pop("from_user", None) if remove_bot: - data["_bot"] = None + data.pop("_bot", None) return data @staticmethod From 17bd0da921cf65e369176e6430fe997435b8429f Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 10:59:47 +0200 Subject: [PATCH 27/90] Fix signature caching for subclasses --- telegram/_chatmember.py | 2 +- telegram/_telegramobject.py | 15 +++++++++------ tests/test_voice.py | 1 - 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index 40995fe9d3b..b8c61245cc7 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -115,7 +115,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"] } if cls is ChatMember and data.get("status") in _class_mapping: - return _class_mapping[data["status"]].de_json(data=data, bot=bot) + return _class_mapping[data.pop("status")].de_json(data=data, bot=bot) data["user"] = User.de_json(data.get("user"), bot) if "until_date" in data: diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 0eb1bf7007f..29c74f4e3b3 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -63,7 +63,11 @@ class TelegramObject: # Used to cache the names of the parameters of the __init__ method of the class # Must be a private attribute to avoid name clashes between subclasses - __INIT_PARAMS: Optional[Set[str]] = None + __INIT_PARAMS: Set[str] = set() + # Used to check if __INIT_PARAMS has been set for the current class. Unfortunately, we can't + # just check if `__INIT_PARAMS is None`, since subclasses use the parent class' __INIT_PARAMS + # unless it's overridden + __INIT_PARAMS_CHECK: Optional[Type["TelegramObject"]] = None def __init__(self, api_kwargs: JSONDict = None) -> None: self._id_attrs: Tuple[object, ...] = () @@ -225,17 +229,16 @@ def _de_json( if "__init__() got an unexpected keyword argument" not in str(exc): raise exc - if cls.__INIT_PARAMS is None: + if cls.__INIT_PARAMS_CHECK is not cls: signature = inspect.signature(cls) cls.__INIT_PARAMS = set(signature.parameters.keys()) + cls.__INIT_PARAMS_CHECK = cls api_kwargs = api_kwargs or {} existing_kwargs: JSONDict = {} for key, value in data.items(): - if key in cls.__INIT_PARAMS: # pylint: disable=unsupported-membership-test - existing_kwargs[key] = value - else: - api_kwargs[key] = value + (existing_kwargs if key in cls.__INIT_PARAMS else api_kwargs)[key] = value + obj = cls(api_kwargs=api_kwargs, **existing_kwargs) obj.set_bot(bot=bot) diff --git a/tests/test_voice.py b/tests/test_voice.py index 11ba57f56a0..5d91e869738 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -268,7 +268,6 @@ def test_de_json(self, bot): "file_id": self.voice_file_id, "file_unique_id": self.voice_file_unique_id, "duration": self.duration, - "caption": self.caption, "mime_type": self.mime_type, "file_size": self.file_size, } From d5ac429756281fa50c35f8a775816a8d45b3f835 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:13:39 +0200 Subject: [PATCH 28/90] doc fix --- telegram/_chat.py | 3 ++- telegram/_telegramobject.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index b6e3572cb3c..e4d5e0eb8fc 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -74,10 +74,11 @@ class Chat(TelegramObject): and notice that some positional arguments changed position as a result. .. versionchanged:: 20.0 + * |removedbotandkwargs| * Removed the attribute ``all_members_are_administrators``. As long as Telegram provides this field for backwards compatibility, it is available through - :attr:`~telegram.TelegramObject.api_kwargs``. + :attr:`~telegram.TelegramObject.api_kwargs`. Args: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 29c74f4e3b3..849c81fc138 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -52,6 +52,7 @@ class TelegramObject: api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| .. versionadded:: 20.0 + Attributes: api_kwargs (Dict[:obj:`str`, any]): |toapikwargsattr| From 1896b850dae8a029d944fa2330d1d2d5265923ef Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:24:01 +0200 Subject: [PATCH 29/90] pre-commit --- telegram/ext/_utils/webhookhandler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telegram/ext/_utils/webhookhandler.py b/telegram/ext/_utils/webhookhandler.py index b59c29fe73d..d5d65c268f0 100644 --- a/telegram/ext/_utils/webhookhandler.py +++ b/telegram/ext/_utils/webhookhandler.py @@ -142,7 +142,11 @@ async def post(self) -> None: ) if update: - self._logger.debug("Received Update with ID %d on Webhook", update.update_id) + self._logger.debug( + "Received Update with ID %d on Webhook", + # For some reason pylint thinks update is a general TelegramObject + update.update_id, # pylint: disable=no-member + ) # handle arbitrary callback data, if necessary if isinstance(self.bot, ExtBot): From 059c9a0765945633d0cb7647fa3e76bf3577a9d0 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:25:16 +0200 Subject: [PATCH 30/90] coverage --- tests/test_telegramobject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 96c58d1fc1a..73972c18e5e 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -68,9 +68,9 @@ def test_de_json_arbitrary_exceptions(self, bot): class SubClass(TelegramObject): def __init__(self, **kwargs): super().__init__(**kwargs) - raise RuntimeError("This is a test") + raise TypeError("This is a test") - with pytest.raises(RuntimeError, match="This is a test"): + with pytest.raises(TypeError, match="This is a test"): SubClass.de_json({}, bot) def test_to_dict_private_attribute(self): From 74dd8b622e7cc2687f1f6d1ce6131fd119d247a5 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:31:15 +0200 Subject: [PATCH 31/90] update test_official --- tests/test_official.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_official.py b/tests/test_official.py index 3d5b4e56546..798c4a9bdda 100644 --- a/tests/test_official.py +++ b/tests/test_official.py @@ -32,14 +32,12 @@ IGNORED_PARAMETERS = { "self", "args", - "_kwargs", "read_timeout", "write_timeout", "connect_timeout", "pool_timeout", "bot", "api_kwargs", - "kwargs", } ignored_param_requirements = { # Ignore these since there's convenience params in them (eg. Venue) From 8b8b3860e24a65c49057346b16f95e4b48566ccd Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 11:36:10 +0200 Subject: [PATCH 32/90] Tone down on versionchanged --- docs/substitutions/global.rst | 6 ---- telegram/_botcommand.py | 3 -- telegram/_botcommandscope.py | 24 --------------- telegram/_callbackquery.py | 3 -- telegram/_chat.py | 8 ++--- telegram/_chatinvitelink.py | 1 - telegram/_chatjoinrequest.py | 3 -- telegram/_chatlocation.py | 3 -- telegram/_chatmember.py | 17 ----------- telegram/_chatmemberupdated.py | 3 -- telegram/_choseninlineresult.py | 3 -- telegram/_dice.py | 3 -- telegram/_files/_basemedium.py | 3 -- telegram/_files/_basethumbedmedium.py | 3 -- telegram/_files/animation.py | 3 -- telegram/_files/audio.py | 3 -- telegram/_files/chatphoto.py | 3 -- telegram/_files/contact.py | 3 -- telegram/_files/document.py | 3 -- telegram/_files/file.py | 3 -- telegram/_files/location.py | 3 -- telegram/_files/photosize.py | 3 -- telegram/_files/sticker.py | 10 +------ telegram/_files/venue.py | 3 -- telegram/_files/video.py | 3 -- telegram/_files/videonote.py | 3 -- telegram/_files/voice.py | 3 -- telegram/_forcereply.py | 6 ++-- telegram/_games/game.py | 3 -- telegram/_inline/inlinekeyboardbutton.py | 6 ++-- telegram/_inline/inlinekeyboardmarkup.py | 3 -- telegram/_inline/inlinequery.py | 10 +++---- telegram/_inline/inlinequeryresult.py | 3 -- telegram/_inline/inlinequeryresultarticle.py | 3 -- telegram/_inline/inlinequeryresultaudio.py | 3 -- .../_inline/inlinequeryresultcachedaudio.py | 3 -- .../inlinequeryresultcacheddocument.py | 3 -- .../_inline/inlinequeryresultcachedgif.py | 3 -- .../inlinequeryresultcachedmpeg4gif.py | 3 -- .../_inline/inlinequeryresultcachedphoto.py | 3 -- .../_inline/inlinequeryresultcachedsticker.py | 3 -- .../_inline/inlinequeryresultcachedvideo.py | 3 -- .../_inline/inlinequeryresultcachedvoice.py | 3 -- telegram/_inline/inlinequeryresultcontact.py | 3 -- telegram/_inline/inlinequeryresultdocument.py | 3 -- telegram/_inline/inlinequeryresultgame.py | 3 -- telegram/_inline/inlinequeryresultgif.py | 3 -- telegram/_inline/inlinequeryresultlocation.py | 3 -- telegram/_inline/inlinequeryresultmpeg4gif.py | 3 -- telegram/_inline/inlinequeryresultphoto.py | 3 -- telegram/_inline/inlinequeryresultvenue.py | 3 -- telegram/_inline/inlinequeryresultvideo.py | 3 -- telegram/_inline/inlinequeryresultvoice.py | 3 -- .../_inline/inputcontactmessagecontent.py | 3 -- .../_inline/inputinvoicemessagecontent.py | 3 -- .../_inline/inputlocationmessagecontent.py | 3 -- telegram/_inline/inputtextmessagecontent.py | 3 -- telegram/_inline/inputvenuemessagecontent.py | 3 -- telegram/_keyboardbutton.py | 5 ++-- telegram/_keyboardbuttonpolltype.py | 3 -- telegram/_loginurl.py | 3 -- telegram/_message.py | 1 - telegram/_messageautodeletetimerchanged.py | 3 -- telegram/_messageentity.py | 3 -- telegram/_messageid.py | 3 -- telegram/_passport/credentials.py | 21 +------------ telegram/_passport/data.py | 9 ------ .../_passport/encryptedpassportelement.py | 3 -- telegram/_passport/passportdata.py | 3 -- telegram/_passport/passportelementerrors.py | 30 ------------------- telegram/_passport/passportfile.py | 3 -- telegram/_payment/invoice.py | 3 -- telegram/_payment/labeledprice.py | 3 -- telegram/_payment/orderinfo.py | 3 -- telegram/_payment/precheckoutquery.py | 3 -- telegram/_payment/shippingaddress.py | 3 -- telegram/_payment/shippingoption.py | 3 -- telegram/_payment/shippingquery.py | 3 -- telegram/_payment/successfulpayment.py | 3 -- telegram/_poll.py | 9 ------ telegram/_proximityalerttriggered.py | 3 -- telegram/_replykeyboardmarkup.py | 3 -- telegram/_replykeyboardremove.py | 3 -- telegram/_telegramobject.py | 8 +++-- telegram/_update.py | 3 -- telegram/_user.py | 10 +++---- telegram/_userprofilephotos.py | 3 -- telegram/_videochat.py | 3 +- telegram/_webhookinfo.py | 5 ++-- 89 files changed, 28 insertions(+), 371 deletions(-) diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 9ae8219373b..fc1bb3dda12 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -21,9 +21,3 @@ .. |toapikwargsarg| replace:: Arbitrary keyword arguments. Can be used to store data for which there are no dedicated attributes. |toapikwargsbase| .. |toapikwargsattr| replace:: Optional. Arbitrary keyword arguments. Used to store data for which there are no dedicated attributes. |toapikwargsbase| - -.. |removedbot| replace:: Removed argument and attribute ``bot``. Use :meth:`~telegram.TelegramObject.set_bot` and :meth:`~telegram.TelegramObject.get_bot` instead. - -.. |removedkwargs| replace:: Removed the possibility to pass arbitrary keyword arguments. - -.. |removedbotandkwargs| replace:: |removedbot| |removedkwargs| diff --git a/telegram/_botcommand.py b/telegram/_botcommand.py index 91f6263142f..0aa44c62a78 100644 --- a/telegram/_botcommand.py +++ b/telegram/_botcommand.py @@ -29,9 +29,6 @@ class BotCommand(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`command` and :attr:`description` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: command (:obj:`str`): Text of the command; 1-32 characters. Can contain only lowercase English letters, digits and underscores. diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 77324cc1596..f31a8889458 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -51,9 +51,6 @@ class BotCommandScope(TelegramObject): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): Scope type. @@ -121,9 +118,6 @@ class BotCommandScopeDefault(BotCommandScope): .. _`narrower scope`: https://core.telegram.org/bots/api#determining-list-of-commands .. versionadded:: 13.7 - - .. versionchanged:: 20.0 - |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.DEFAULT`. """ @@ -139,9 +133,6 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_PRIVATE_CHATS`. """ @@ -156,9 +147,6 @@ class BotCommandScopeAllGroupChats(BotCommandScope): """Represents the scope of bot commands, covering all group and supergroup chats. .. versionadded:: 13.7 - - .. versionchanged:: 20.0 - |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_GROUP_CHATS`. """ @@ -173,9 +161,6 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): """Represents the scope of bot commands, covering all group and supergroup chat administrators. .. versionadded:: 13.7 - - .. versionchanged:: 20.0 - |removedkwargs| Attributes: type (:obj:`str`): Scope type :tg-const:`telegram.BotCommandScope.ALL_CHAT_ADMINISTRATORS`. """ @@ -194,9 +179,6 @@ class BotCommandScopeChat(BotCommandScope): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``) @@ -225,9 +207,6 @@ class BotCommandScopeChatAdministrators(BotCommandScope): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``) @@ -256,9 +235,6 @@ class BotCommandScopeChatMember(BotCommandScope): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: chat_id (:obj:`str` | :obj:`int`): Unique identifier for the target chat or username of the target supergroup (in the format ``@supergroupusername``) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 240904b99c9..9736e7c099f 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -65,9 +65,6 @@ class CallbackQuery(TelegramObject): .. versionadded:: 13.6 - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this query. from_user (:class:`telegram.User`): Sender. diff --git a/telegram/_chat.py b/telegram/_chat.py index e4d5e0eb8fc..2bc04c6b898 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -74,11 +74,9 @@ class Chat(TelegramObject): and notice that some positional arguments changed position as a result. .. versionchanged:: 20.0 - - * |removedbotandkwargs| - * Removed the attribute ``all_members_are_administrators``. As long as Telegram provides - this field for backwards compatibility, it is available through - :attr:`~telegram.TelegramObject.api_kwargs`. + Removed the attribute ``all_members_are_administrators``. As long as Telegram provides + this field for backwards compatibility, it is available through + :attr:`~telegram.TelegramObject.api_kwargs`. Args: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index 0e400420908..1ba2cf60a74 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -42,7 +42,6 @@ class ChatInviteLink(TelegramObject): * The argument & attribute :attr:`creates_join_request` is now required to comply with the Bot API. * Comparing objects of this class now also takes :attr:`creates_join_request` into account. - * |removedkwargs| Args: invite_link (:obj:`str`): The invite link. diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index d9c082d472c..15efcd727e3 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -46,9 +46,6 @@ class ChatJoinRequest(TelegramObject): .. versionadded:: 13.8 - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: chat (:class:`telegram.Chat`): Chat to which the request was sent. from_user (:class:`telegram.User`): User that sent the join request. diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index 638aa6d6674..10a752f9f05 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -34,9 +34,6 @@ class ChatLocation(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`location` is equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: location (:class:`telegram.Location`): The location to which the supergroup is connected. Can't be a live location. diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index b8c61245cc7..821f3d5950f 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -54,7 +54,6 @@ class ChatMember(TelegramObject): use :class:`ChatMember` directly. * The constant ``ChatMember.CREATOR`` was replaced by :attr:`~telegram.ChatMember.OWNER` * The constant ``ChatMember.KICKED`` was replaced by :attr:`~telegram.ChatMember.BANNED` - * |removedkwargs| Args: user (:class:`telegram.User`): Information about the user. @@ -140,9 +139,6 @@ class ChatMemberOwner(ChatMember): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: user (:class:`telegram.User`): Information about the user. is_anonymous (:obj:`bool`): :obj:`True`, if the @@ -183,7 +179,6 @@ class ChatMemberAdministrator(ChatMember): * Argument and attribute ``can_manage_voice_chats`` were renamed to :paramref:`can_manage_video_chats` and :attr:`can_manage_video_chats` in accordance to Bot API 6.0. - * |removedkwargs| Args: user (:class:`telegram.User`): Information about the user. @@ -315,9 +310,6 @@ class ChatMemberMember(ChatMember): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: user (:class:`telegram.User`): Information about the user. @@ -345,9 +337,6 @@ class ChatMemberRestricted(ChatMember): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: user (:class:`telegram.User`): Information about the user. is_member (:obj:`bool`): :obj:`True`, if the user is a @@ -446,9 +435,6 @@ class ChatMemberLeft(ChatMember): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: user (:class:`telegram.User`): Information about the user. @@ -475,9 +461,6 @@ class ChatMemberBanned(ChatMember): .. versionadded:: 13.7 - .. versionchanged:: 20.0 - |removedkwargs| - Args: user (:class:`telegram.User`): Information about the user. until_date (:class:`datetime.datetime`): Date when restrictions diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index 287e7399f53..c0e486cf496 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -44,9 +44,6 @@ class ChatMemberUpdated(TelegramObject): Note: In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. - .. versionchanged:: 20.0 - |removedkwargs| - Args: chat (:class:`telegram.Chat`): Chat the user belongs to. from_user (:class:`telegram.User`): Performer of the action, which resulted in the change. diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index 20bad19144f..91acf73b6dc 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -43,9 +43,6 @@ class ChosenInlineResult(TelegramObject): * It is necessary to enable inline feedback via `@Botfather `_ in order to receive these objects in updates. - .. versionchanged:: 20.0 - |removedkwargs| - Args: result_id (:obj:`str`): The unique identifier for the result that was chosen. from_user (:class:`telegram.User`): The user that chose the result. diff --git a/telegram/_dice.py b/telegram/_dice.py index 67486e0868e..bfcc5c78a8d 100644 --- a/telegram/_dice.py +++ b/telegram/_dice.py @@ -55,9 +55,6 @@ class Dice(TelegramObject): /Code-snippets#map-a-slot-machine-dice-value-to-the-corresponding-symbols>`_. However, this behaviour is undocumented and might be changed by Telegram. - .. versionchanged:: 20.0 - |removedkwargs| - Args: value (:obj:`int`): Value of the dice. 1-6 for dice, darts and bowling balls, 1-5 for basketball and football/soccer ball, 1-64 for slot machine. diff --git a/telegram/_files/_basemedium.py b/telegram/_files/_basemedium.py index 994978d000a..420cf29c86c 100644 --- a/telegram/_files/_basemedium.py +++ b/telegram/_files/_basemedium.py @@ -32,9 +32,6 @@ class _BaseMedium(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index d20b819e5bb..6cf88f9691c 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -37,9 +37,6 @@ class _BaseThumbedMedium(_BaseMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index 96013821a92..bc09e46cec6 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -29,9 +29,6 @@ class Animation(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index 82f36331ba3..82f843d5e34 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -29,9 +29,6 @@ class Audio(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download diff --git a/telegram/_files/chatphoto.py b/telegram/_files/chatphoto.py index d7620b526f2..1d026e38897 100644 --- a/telegram/_files/chatphoto.py +++ b/telegram/_files/chatphoto.py @@ -34,9 +34,6 @@ class ChatPhoto(TelegramObject): considered equal, if their :attr:`small_file_unique_id` and :attr:`big_file_unique_id` are equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: small_file_id (:obj:`str`): Unique file identifier of small (160x160) chat photo. This file_id can be used only for photo download and only for as long diff --git a/telegram/_files/contact.py b/telegram/_files/contact.py index e727357ef68..a3d43decb8d 100644 --- a/telegram/_files/contact.py +++ b/telegram/_files/contact.py @@ -28,9 +28,6 @@ class Contact(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`phone_number` is equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: phone_number (:obj:`str`): Contact's phone number. first_name (:obj:`str`): Contact's first name. diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 24fec68562c..24a9226b622 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -30,9 +30,6 @@ class Document(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/file.py b/telegram/_files/file.py index 49232caae1e..a39cbe7405a 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -48,9 +48,6 @@ class File(TelegramObject): * If you obtain an instance of this class from :attr:`telegram.PassportFile.get_file`, then it will automatically be decrypted as it downloads when you call :meth:`download()`. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/location.py b/telegram/_files/location.py index b660d156650..e5830fe24e8 100644 --- a/telegram/_files/location.py +++ b/telegram/_files/location.py @@ -28,9 +28,6 @@ class Location(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`longitude` and :attr:`latitude` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: longitude (:obj:`float`): Longitude as defined by sender. latitude (:obj:`float`): Latitude as defined by sender. diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 6ffbf8349ee..30ca667788c 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -28,9 +28,6 @@ class PhotoSize(_BaseMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index f785799abb3..2e5d1e25a65 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -42,9 +42,6 @@ class Sticker(_BaseThumbedMedium): arguments had to be changed. Use keyword arguments to make sure that the arguments are passed correctly. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. @@ -199,9 +196,7 @@ class StickerSet(TelegramObject): passed correctly. .. versionchanged:: 20.0: - - * The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type` instead. - * |removedkwargs| + The parameter ``contains_masks`` has been removed. Use :paramref:`sticker_type` instead. Args: name (:obj:`str`): Sticker set name. @@ -301,9 +296,6 @@ class MaskPosition(TelegramObject): considered equal, if their :attr:`point`, :attr:`x_shift`, :attr:`y_shift` and, :attr:`scale` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: point (:obj:`str`): The part of the face relative to which the mask should be placed. One of :attr:`FOREHEAD`, :attr:`EYES`, :attr:`MOUTH`, or :attr:`CHIN`. diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index 9c1eb9a3f9b..d879e6195de 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -38,9 +38,6 @@ class Venue(TelegramObject): Foursquare details and Google Pace details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. - .. versionchanged:: 20.0 - |removedkwargs| - Args: location (:class:`telegram.Location`): Venue location. title (:obj:`str`): Name of the venue. diff --git a/telegram/_files/video.py b/telegram/_files/video.py index e5b6d54af09..7a5c9f935db 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -29,9 +29,6 @@ class Video(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index 9dbd7509f1c..a1938c3f2ff 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -29,9 +29,6 @@ class VideoNote(_BaseThumbedMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index d3f257c84cb..1ec0d3c148b 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -28,9 +28,6 @@ class Voice(_BaseMedium): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_forcereply.py b/telegram/_forcereply.py index 27efd33a56f..e07b2a873a4 100644 --- a/telegram/_forcereply.py +++ b/telegram/_forcereply.py @@ -33,10 +33,8 @@ class ForceReply(TelegramObject): considered equal, if their :attr:`selective` is equal. .. versionchanged:: 20.0 - - * The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` - is now always set to :obj:`True` as expected by the Bot API. - * |removedkwargs| + The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` + is now always set to :obj:`True` as expected by the Bot API. Args: selective (:obj:`bool`, optional): Use this parameter if you want to force reply from diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 1edc738f8be..c9d9124e68b 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -39,9 +39,6 @@ class Game(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`title`, :attr:`description` and :attr:`photo` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 07ce1880267..a90971f54f3 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -67,10 +67,8 @@ class InlineKeyboardButton(TelegramObject): :class:`telegram.InlineKeyboardMarkup` .. versionchanged:: 20.0 - - * :attr:`web_app` is considered as well when comparing objects of this type in terms of - equality. - * |removedkwargs| + :attr:`web_app` is considered as well when comparing objects of this type in terms of + equality. Args: text (:obj:`str`): Label text on the button. diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 943232a6a08..09d7e256726 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -39,9 +39,6 @@ class InlineKeyboardMarkup(TelegramObject): .. seealso:: `Inline Keyboard Example 1 `_, `Inline Keyboard Example 2 `_ - .. versionchanged:: 20.0 - |removedkwargs| - Args: inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, each represented by a list of InlineKeyboardButton objects. diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index e25d0de06be..06f654a52c8 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -44,12 +44,10 @@ class InlineQuery(TelegramObject): In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. .. versionchanged:: 20.0 - - * The following are now keyword-only arguments in Bot methods: - ``{read, write, connect, pool}_timeout``, :paramref:`answer.api_kwargs`, - ``auto_pagination``. Use a named argument for those, - and notice that some positional arguments changed position as a result. - * |removedbotandkwargs| + The following are now keyword-only arguments in Bot methods: + ``{read, write, connect, pool}_timeout``, :paramref:`answer.api_kwargs`, + ``auto_pagination``. Use a named argument for those, + and notice that some positional arguments changed position as a result. Args: id (:obj:`str`): Unique identifier for this query. diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 12d63949bbf..325bf210cda 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -33,9 +33,6 @@ class InlineQueryResult(TelegramObject): All URLs passed in inline query results will be available to end users and therefore must be assumed to be *public*. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): Type of the result. id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index 30e86e09040..ed98f7a6159 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -34,9 +34,6 @@ class InlineQueryResultArticle(InlineQueryResult): .. seealso:: `Inline Example `_ - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. title (:obj:`str`): Title of the result. diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 2a58c022153..80982328cee 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -37,9 +37,6 @@ class InlineQueryResultAudio(InlineQueryResult): Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the audio. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. audio_url (:obj:`str`): A valid URL for the audio file. diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 88c75c56251..62c97ee3118 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -37,9 +37,6 @@ class InlineQueryResultCachedAudio(InlineQueryResult): file will be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the audio. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. audio_file_id (:obj:`str`): A valid file identifier for the audio file. diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 4a01ff89191..29d08a3ce56 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -37,9 +37,6 @@ class InlineQueryResultCachedDocument(InlineQueryResult): by the user with an optional caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the file. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. title (:obj:`str`): Title for the result. diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index d6298742913..b60903eb1a2 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -38,9 +38,6 @@ class InlineQueryResultCachedGif(InlineQueryResult): use :attr:`input_message_content` to send a message with specified content instead of the animation. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. gif_file_id (:obj:`str`): A valid file identifier for the GIF file. diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index fb5ffdb801e..da565c47116 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -38,9 +38,6 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): optional caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the animation. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. mpeg4_file_id (:obj:`str`): A valid file identifier for the MP4 file. diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index ac18910be66..7496d55e042 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -38,9 +38,6 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): :attr:`input_message_content` to send a message with the specified content instead of the photo. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. photo_file_id (:obj:`str`): A valid file identifier of the photo. diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index e1d609aa976..91143b33b36 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -35,9 +35,6 @@ class InlineQueryResultCachedSticker(InlineQueryResult): be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the sticker. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. sticker_file_id (:obj:`str`): A valid file identifier of the sticker. diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 41ec1ae51f1..26c1c9c9084 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -38,9 +38,6 @@ class InlineQueryResultCachedVideo(InlineQueryResult): :attr:`input_message_content` to send a message with the specified content instead of the video. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. video_file_id (:obj:`str`): A valid file identifier for the video file. diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index a57f936407c..472f9febcf2 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -37,9 +37,6 @@ class InlineQueryResultCachedVoice(InlineQueryResult): message will be sent by the user. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the voice message. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. voice_file_id (:obj:`str`): A valid file identifier for the voice message. diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index f091321d957..43f75e632db 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -35,9 +35,6 @@ class InlineQueryResultContact(InlineQueryResult): Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the contact. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. phone_number (:obj:`str`): Contact's phone number. diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 3ce2d25ba4d..6180eaeeea2 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -38,9 +38,6 @@ class InlineQueryResultDocument(InlineQueryResult): specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. title (:obj:`str`): Title for the result. diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 2b59a8734b9..688dfeb60b3 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -27,9 +27,6 @@ class InlineQueryResultGame(InlineQueryResult): """Represents a :class:`telegram.Game`. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. game_short_name (:obj:`str`): Short name of the game. diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 3fd45079c8c..d25f67bd1bc 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -37,9 +37,6 @@ class InlineQueryResultGif(InlineQueryResult): the user with optional caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the animation. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. gif_url (:obj:`str`): A valid URL for the GIF file. File size must not exceed 1MB. diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index 7cef95d0ae1..810db71a270 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -35,9 +35,6 @@ class InlineQueryResultLocation(InlineQueryResult): Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the location. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. latitude (:obj:`float`): Location latitude in degrees. diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index a40326e843e..d60eaadaeaa 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -38,9 +38,6 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): use :attr:`input_message_content` to send a message with the specified content instead of the animation. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. mpeg4_url (:obj:`str`): A valid URL for the MP4 file. File size must not exceed 1MB. diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index f3d1166406d..3fb17c02b43 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -37,9 +37,6 @@ class InlineQueryResultPhoto(InlineQueryResult): caption. Alternatively, you can use :attr:`input_message_content` to send a message with the specified content instead of the photo. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. photo_url (:obj:`str`): A valid URL of the photo. Photo must be in JPEG format. Photo size diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 874b59d15d5..21dc57be3f8 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -39,9 +39,6 @@ class InlineQueryResultVenue(InlineQueryResult): Foursquare details and Google Pace details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 Bytes. latitude (:obj:`float`): Latitude of the venue location in degrees. diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index de04d0e9514..1b5b3dd7987 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -42,9 +42,6 @@ class InlineQueryResultVideo(InlineQueryResult): If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must replace its content using :attr:`input_message_content`. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. video_url (:obj:`str`): A valid URL for the embedded video player or video file. diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index cd4814ef75d..4bb4514a0f4 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -38,9 +38,6 @@ class InlineQueryResultVoice(InlineQueryResult): :attr:`input_message_content` to send a message with the specified content instead of the voice message. - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique identifier for this result, 1-64 bytes. voice_url (:obj:`str`): A valid URL for the voice recording. diff --git a/telegram/_inline/inputcontactmessagecontent.py b/telegram/_inline/inputcontactmessagecontent.py index 3d9ac9fcbe1..e1362ee9d99 100644 --- a/telegram/_inline/inputcontactmessagecontent.py +++ b/telegram/_inline/inputcontactmessagecontent.py @@ -28,9 +28,6 @@ class InputContactMessageContent(InputMessageContent): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`phone_number` is equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: phone_number (:obj:`str`): Contact's phone number. first_name (:obj:`str`): Contact's first name. diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index e4eb6bb8ce1..112b1333350 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -38,9 +38,6 @@ class InputInvoiceMessageContent(InputMessageContent): .. versionadded:: 13.5 - .. versionchanged:: 20.0 - |removedkwargs| - Args: title (:obj:`str`): Product name. :tg-const:`telegram.Invoice.MIN_TITLE_LENGTH`- :tg-const:`telegram.Invoice.MAX_TITLE_LENGTH` characters. diff --git a/telegram/_inline/inputlocationmessagecontent.py b/telegram/_inline/inputlocationmessagecontent.py index 789b6ecce10..bb756faeff2 100644 --- a/telegram/_inline/inputlocationmessagecontent.py +++ b/telegram/_inline/inputlocationmessagecontent.py @@ -30,9 +30,6 @@ class InputLocationMessageContent(InputMessageContent): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`latitude` and :attr:`longitude` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: latitude (:obj:`float`): Latitude of the location in degrees. longitude (:obj:`float`): Longitude of the location in degrees. diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index f115e6027fa..23fc76f5255 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -35,9 +35,6 @@ class InputTextMessageContent(InputMessageContent): .. seealso:: `Inline Example `_ - .. versionchanged:: 20.0 - |removedkwargs| - Args: message_text (:obj:`str`): Text of the message to be sent, 1-:tg-const:`telegram.constants.MessageLimit.TEXT_LENGTH` characters after entities diff --git a/telegram/_inline/inputvenuemessagecontent.py b/telegram/_inline/inputvenuemessagecontent.py index c45a7b88ada..fa55e7ee534 100644 --- a/telegram/_inline/inputvenuemessagecontent.py +++ b/telegram/_inline/inputvenuemessagecontent.py @@ -33,9 +33,6 @@ class InputVenueMessageContent(InputMessageContent): Foursquare details and Google Pace details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. - .. versionchanged:: 20.0 - |removedkwargs| - Args: latitude (:obj:`float`): Latitude of the location in degrees. longitude (:obj:`float`): Longitude of the location in degrees. diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index da2d65bea9c..cbae095a321 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -48,9 +48,8 @@ class KeyboardButton(TelegramObject): Older clients will display unsupported message. .. versionchanged:: 20.0 - * :attr:`web_app` is considered as well when comparing objects of this type in terms of - equality. - * |removedkwargs| + :attr:`web_app` is considered as well when comparing objects of this type in terms of + equality. Args: text (:obj:`str`): Text of the button. If none of the optional fields are used, it will be diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index ba01939fe92..78d1b94beae 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -31,9 +31,6 @@ class KeyboardButtonPollType(TelegramObject): .. seealso:: `Pollbot Example `_ - .. versionchanged:: 20.0 - |removedkwargs| - Attributes: type (:obj:`str`): Optional. If :tg-const:`telegram.Poll.QUIZ` is passed, the user will be allowed to create only polls in the quiz mode. If :tg-const:`telegram.Poll.REGULAR` is diff --git a/telegram/_loginurl.py b/telegram/_loginurl.py index 560cf3749d1..1831719fe8e 100644 --- a/telegram/_loginurl.py +++ b/telegram/_loginurl.py @@ -38,9 +38,6 @@ class LoginUrl(TelegramObject): and the integrity of the data as described in `Checking authorization `_ - .. versionchanged:: 20.0 - |removedkwargs| - Args: url (:obj:`str`): An HTTPS URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, diff --git a/telegram/_message.py b/telegram/_message.py index 6461c5a0663..89b8aa5fa2f 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -97,7 +97,6 @@ class Message(TelegramObject): ``{read, write, connect, pool}_timeout``, ``api_kwargs``, ``contact``, ``quote``, ``filename``, ``loaction``, ``venue``. Use a named argument for those, and notice that some positional arguments changed position as a result. - * |removedbotandkwargs| Args: message_id (:obj:`int`): Unique message identifier inside this chat. diff --git a/telegram/_messageautodeletetimerchanged.py b/telegram/_messageautodeletetimerchanged.py index e4d8f6b7094..f83a7b554a0 100644 --- a/telegram/_messageautodeletetimerchanged.py +++ b/telegram/_messageautodeletetimerchanged.py @@ -32,9 +32,6 @@ class MessageAutoDeleteTimerChanged(TelegramObject): .. versionadded:: 13.4 - .. versionchanged:: 20.0 - |removedkwargs| - Args: message_auto_delete_time (:obj:`int`): New auto-delete time for messages in the chat. diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index 9b6d0ef0b1b..f0d1ca946ff 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -38,9 +38,6 @@ class MessageEntity(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`type`, :attr:`offset` and :attr:`length` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): Type of the entity. Can be :attr:`MENTION` (@username), :attr:`HASHTAG`, :attr:`BOT_COMMAND`, diff --git a/telegram/_messageid.py b/telegram/_messageid.py index a14c3d3420b..7ecc4d423cc 100644 --- a/telegram/_messageid.py +++ b/telegram/_messageid.py @@ -28,9 +28,6 @@ class MessageId(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`message_id` is equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: message_id (:obj:`int`): Unique message identifier. diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 053c8255c0e..03d8d9b5bb8 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -112,9 +112,6 @@ class EncryptedCredentials(TelegramObject): This object is decrypted only when originating from :obj:`telegram.PassportData.decrypted_credentials`. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's nonce, data hashes and secrets used for EncryptedPassportElement decryption and @@ -209,9 +206,6 @@ def decrypted_data(self) -> "Credentials": class Credentials(TelegramObject): """ - .. versionchanged:: 20.0 - |removedbotandkwargs| - Attributes: secure_data (:class:`telegram.SecureData`): Credentials for encrypted data nonce (:obj:`str`): Bot-specified nonce @@ -248,9 +242,6 @@ class SecureData(TelegramObject): This object represents the credentials that were used to decrypt the encrypted data. All fields are optional and depend on fields that were requested. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Attributes: personal_details (:class:`telegram.SecureValue`, optional): Credentials for encrypted personal details. @@ -350,9 +341,6 @@ class SecureValue(TelegramObject): This object represents the credentials that were used to decrypt the encrypted value. All fields are optional and depend on the type of field. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Attributes: data (:class:`telegram.DataCredentials`, optional): Credentials for encrypted Telegram Passport data. Available for "personal_details", "passport", "driver_license", @@ -423,11 +411,7 @@ def to_dict(self) -> JSONDict: class _CredentialsBase(TelegramObject): - """Base class for DataCredentials and FileCredentials. - - .. versionchanged:: 20.0 - |removedbotandkwargs| - """ + """Base class for DataCredentials and FileCredentials.""" __slots__ = ("hash", "secret", "file_hash", "data_hash") @@ -446,9 +430,6 @@ class DataCredentials(_CredentialsBase): These credentials can be used to decrypt encrypted data from the data field in EncryptedPassportData. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: data_hash (:obj:`str`): Checksum of encrypted data secret (:obj:`str`): Secret of encrypted data diff --git a/telegram/_passport/data.py b/telegram/_passport/data.py index 8bdd38a1155..8bccc195a10 100644 --- a/telegram/_passport/data.py +++ b/telegram/_passport/data.py @@ -26,9 +26,6 @@ class PersonalDetails(TelegramObject): """ This object represents personal details. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Attributes: first_name (:obj:`str`): First Name. middle_name (:obj:`str`): Optional. First Name. @@ -91,9 +88,6 @@ class ResidentialAddress(TelegramObject): """ This object represents a residential address. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Attributes: street_line1 (:obj:`str`): First line for the address. street_line2 (:obj:`str`): Optional. Second line for the address. @@ -136,9 +130,6 @@ class IdDocumentData(TelegramObject): """ This object represents the data of an identity document. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Attributes: document_no (:obj:`str`): Document number. expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format. diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 209c88a87b5..74ad493f4da 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -43,9 +43,6 @@ class EncryptedPassportElement(TelegramObject): This object is decrypted only when originating from :obj:`telegram.PassportData.decrypted_data`. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 0a8b6782594..61ff457385d 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -38,9 +38,6 @@ class PassportData(TelegramObject): :attr:`decrypted_data` and the payload can be found in :attr:`decrypted_credentials`'s attribute :attr:`telegram.Credentials.nonce`. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index 345dd02bb58..acb743885d9 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -32,9 +32,6 @@ class PassportElementError(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`source` and :attr:`type` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: source (:obj:`str`): Error source. type (:obj:`str`): The section of the user's Telegram Passport which has the error. @@ -67,9 +64,6 @@ class PassportElementErrorDataField(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`field_name`, :attr:`data_hash` and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of ``"personal_details"``, ``"passport"``, ``"driver_license"``, ``"identity_card"``, @@ -115,9 +109,6 @@ class PassportElementErrorFile(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, @@ -153,9 +144,6 @@ class PassportElementErrorFiles(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hashes`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, @@ -191,9 +179,6 @@ class PassportElementErrorFrontSide(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. @@ -229,9 +214,6 @@ class PassportElementErrorReverseSide(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"driver_license"``, ``"identity_card"``. @@ -267,9 +249,6 @@ class PassportElementErrorSelfie(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``. @@ -303,9 +282,6 @@ class PassportElementErrorTranslationFile(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hash`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, @@ -343,9 +319,6 @@ class PassportElementErrorTranslationFiles(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`file_hashes`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, @@ -383,9 +356,6 @@ class PassportElementErrorUnspecified(PassportElementError): considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`, :attr:`element_hash`, and :attr:`message` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. element_hash (:obj:`str`): Base64-encoded element hash. diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index ecdbe7b32fd..857701def01 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -36,9 +36,6 @@ class PassportFile(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`file_unique_id` is equal. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: file_id (:obj:`str`): Identifier for this file, which can be used to download or reuse the file. diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index ff7dded8190..00aa67079f1 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -32,9 +32,6 @@ class Invoice(TelegramObject): considered equal, if their :attr:`title`, :attr:`description`, :paramref:`start_parameter`, :attr:`currency` and :attr:`total_amount` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: title (:obj:`str`): Product name. description (:obj:`str`): Product description. diff --git a/telegram/_payment/labeledprice.py b/telegram/_payment/labeledprice.py index fa877be6113..21544342a2e 100644 --- a/telegram/_payment/labeledprice.py +++ b/telegram/_payment/labeledprice.py @@ -30,9 +30,6 @@ class LabeledPrice(TelegramObject): .. seealso:: `Paymentbot Example `_ - .. versionchanged:: 20.0 - |removedkwargs| - Args: label (:obj:`str`): Portion label. amount (:obj:`int`): Price of the product in the smallest units of the currency (integer, diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index 55e757b21cc..5198401e84b 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -35,9 +35,6 @@ class OrderInfo(TelegramObject): considered equal, if their :attr:`name`, :attr:`phone_number`, :attr:`email` and :attr:`shipping_address` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: name (:obj:`str`, optional): User name. phone_number (:obj:`str`, optional): User's phone number. diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index e1e34e42ead..482f75a060c 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -39,9 +39,6 @@ class PreCheckoutQuery(TelegramObject): Note: In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. diff --git a/telegram/_payment/shippingaddress.py b/telegram/_payment/shippingaddress.py index 66d231fc626..aee05fb214b 100644 --- a/telegram/_payment/shippingaddress.py +++ b/telegram/_payment/shippingaddress.py @@ -29,9 +29,6 @@ class ShippingAddress(TelegramObject): considered equal, if their :attr:`country_code`, :attr:`state`, :attr:`city`, :attr:`street_line1`, :attr:`street_line2` and :attr:`post_code` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: country_code (:obj:`str`): ISO 3166-1 alpha-2 country code. state (:obj:`str`): State, if applicable. diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index bddfae5ffe2..c6a6e5e0016 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -35,9 +35,6 @@ class ShippingOption(TelegramObject): .. seealso:: `Paymentbot Example `_ - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Shipping option identifier. title (:obj:`str`): Option title. diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index a9066f73721..c487c03e0b0 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -40,9 +40,6 @@ class ShippingQuery(TelegramObject): Note: In Python :keyword:`from` is a reserved word use :paramref:`from_user` instead. - .. versionchanged:: 20.0 - |removedbotandkwargs| - Args: id (:obj:`str`): Unique query identifier. from_user (:class:`telegram.User`): User who sent the query. diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index 177cb8846c8..a63338bf7e6 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -35,9 +35,6 @@ class SuccessfulPayment(TelegramObject): considered equal, if their :attr:`telegram_payment_charge_id` and :attr:`provider_payment_charge_id` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: currency (:obj:`str`): Three-letter ISO 4217 currency code. total_amount (:obj:`int`): Total price in the smallest units of the currency (integer, not diff --git a/telegram/_poll.py b/telegram/_poll.py index 0dc2ab08070..4456d708887 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -41,9 +41,6 @@ class PollOption(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`text` and :attr:`voter_count` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: text (:obj:`str`): Option text, 1-100 characters. voter_count (:obj:`int`): Number of users that voted for this option. @@ -74,9 +71,6 @@ class PollAnswer(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`poll_id`, :attr:`user` and :attr:`option_ids` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. @@ -124,9 +118,6 @@ class Poll(TelegramObject): .. seealso:: `Pollbot Example `_ - .. versionchanged:: 20.0 - |removedkwargs| - Args: id (:obj:`str`): Unique poll identifier. question (:obj:`str`): Poll question, 1-300 characters. diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index d8ead198bac..af07399eaf0 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -35,9 +35,6 @@ class ProximityAlertTriggered(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`traveler`, :attr:`watcher` and :attr:`distance` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: traveler (:class:`telegram.User`): User that triggered the alert watcher (:class:`telegram.User`): User that set the alert diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 0d92ed82cb8..c5aafa84df3 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -36,9 +36,6 @@ class ReplyKeyboardMarkup(TelegramObject): A user requests to change the bot's language, bot replies to the request with a keyboard to select the new language. Other users in the group don't see the keyboard. - .. versionchanged:: 20.0 - |removedkwargs| - Args: keyboard (List[List[:obj:`str` | :class:`telegram.KeyboardButton`]]): Array of button rows, each represented by an Array of :class:`telegram.KeyboardButton` objects. diff --git a/telegram/_replykeyboardremove.py b/telegram/_replykeyboardremove.py index 74cede1dfce..27c526ed0f7 100644 --- a/telegram/_replykeyboardremove.py +++ b/telegram/_replykeyboardremove.py @@ -38,9 +38,6 @@ class ReplyKeyboardRemove(TelegramObject): User will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use :attr:`telegram.ReplyKeyboardMarkup.one_time_keyboard`. - .. versionchanged:: 20.0 - |removedkwargs| - Args: selective (:obj:`bool`, optional): Use this parameter if you want to remove the keyboard for specific users only. Targets: diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 849c81fc138..2ea54abee6d 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -45,8 +45,12 @@ class TelegramObject: assert telegram_object.get_bot() is copy.deepcopy(telegram_object).get_bot() .. versionchanged:: 20.0 - ``telegram_object['from']`` will look up the key ``from_user``. This is to account for - special cases like :attr:`Message.from_user` that deviate from the official Bot API. + + * ``telegram_object['from']`` will look up the key ``from_user``. This is to account for + special cases like :attr:`Message.from_user` that deviate from the official Bot API. + * Removed argument and attribute ``bot`` for several subclasses. Use + :meth:`set_bot` and :meth:`get_bot` instead. + * Removed the possibility to pass arbitrary keyword arguments for several subclasses. Arguments: api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| diff --git a/telegram/_update.py b/telegram/_update.py index 2c885d20861..4cd0919dba0 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -46,9 +46,6 @@ class Update(TelegramObject): Note: At most one of the optional parameters can be present in any given update. - .. versionchanged:: 20.0 - |removedkwargs| - Args: update_id (:obj:`int`): The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if diff --git a/telegram/_user.py b/telegram/_user.py index 69f8bee769a..fcb938af0d6 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -62,12 +62,10 @@ class User(TelegramObject): considered equal, if their :attr:`id` is equal. .. versionchanged:: 20.0 - - * The following are now keyword-only arguments in Bot methods: - ``location``, ``filename``, ``venue``, ``contact``, - ``{read, write, connect, pool}_timeout`` ``api_kwargs``. Use a named argument for those, - and notice that some positional arguments changed position as a result. - * |removedbotandkwargs| + The following are now keyword-only arguments in Bot methods: + ``location``, ``filename``, ``venue``, ``contact``, + ``{read, write, connect, pool}_timeout`` ``api_kwargs``. Use a named argument for those, + and notice that some positional arguments changed position as a result. Args: id (:obj:`int`): Unique identifier for this user or bot. diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index ac5eb741c58..8db281761e3 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -34,9 +34,6 @@ class UserProfilePhotos(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`total_count` and :attr:`photos` are equal. - .. versionchanged:: 20.0 - |removedkwargs| - Args: total_count (:obj:`int`): Total number of profile pictures the target user has. photos (List[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 73240f9f852..93a6d65cf73 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -37,8 +37,7 @@ class VideoChatStarted(TelegramObject): .. versionadded:: 13.4 .. versionchanged:: 20.0 - * This class was renamed from ``VoiceChatStarted`` in accordance to Bot API 6.0. - * |removedkwargs| + This class was renamed from ``VoiceChatStarted`` in accordance to Bot API 6.0. """ __slots__ = () diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 9b7dffb1843..ff8570def2f 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -40,9 +40,8 @@ class WebhookInfo(TelegramObject): :attr:`last_synchronization_error_date` are equal. .. versionchanged:: 20.0 - * :attr:`last_synchronization_error_date` is considered as well when comparing objects of - this type in terms of equality. - * |removedkwargs| + :attr:`last_synchronization_error_date` is considered as well when comparing objects of + this type in terms of equality. Args: url (:obj:`str`): Webhook URL, may be empty if webhook is not set up. From 0016e0bb8b2356e121278e28b8fe93df3500fd27 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 13:46:11 +0200 Subject: [PATCH 33/90] Add tests for empty `api_kwargs` --- telegram/_botcommandscope.py | 4 +++- telegram/_menubutton.py | 13 ++++++++----- tests/test_animation.py | 1 + tests/test_audio.py | 2 +- tests/test_botcommand.py | 1 + tests/test_botcommandscope.py | 3 +++ tests/test_callbackquery.py | 1 + tests/test_chatadministratorrights.py | 1 + tests/test_chatinvitelink.py | 2 ++ tests/test_chatjoinrequest.py | 2 ++ tests/test_chatlocation.py | 1 + tests/test_chatmember.py | 2 ++ tests/test_chatmemberupdated.py | 2 ++ tests/test_chatpermissions.py | 1 + tests/test_chatphoto.py | 1 + tests/test_choseninlineresult.py | 2 ++ tests/test_contact.py | 2 ++ tests/test_dice.py | 1 + tests/test_document.py | 1 + tests/test_file.py | 1 + tests/test_game.py | 2 ++ tests/test_gamehighscore.py | 1 + tests/test_inlinekeyboardbutton.py | 1 + tests/test_inlinekeyboardmarkup.py | 1 + tests/test_inlinequery.py | 1 + tests/test_inputinvoicemessagecontent.py | 1 + tests/test_invoice.py | 1 + tests/test_keyboardbutton.py | 1 + tests/test_location.py | 1 + tests/test_menubutton.py | 2 ++ tests/test_message.py | 1 + tests/test_messageautodeletetimerchanged.py | 1 + tests/test_messageentity.py | 1 + tests/test_messageid.py | 1 + tests/test_orderinfo.py | 1 + tests/test_passport.py | 3 +++ tests/test_photo.py | 1 + tests/test_poll.py | 3 +++ tests/test_precheckoutquery.py | 1 + tests/test_proximityalerttriggered.py | 1 + tests/test_sentwebappmessage.py | 1 + tests/test_shippingaddress.py | 1 + tests/test_shippingquery.py | 1 + tests/test_sticker.py | 2 ++ tests/test_successfulpayment.py | 1 + tests/test_update.py | 1 + tests/test_user.py | 3 +++ tests/test_userprofilephotos.py | 1 + tests/test_venue.py | 1 + tests/test_video.py | 1 + tests/test_videochat.py | 4 ++++ tests/test_videonote.py | 1 + tests/test_voice.py | 1 + tests/test_webappdata.py | 1 + tests/test_webappinfo.py | 1 + tests/test_webhookinfo.py | 1 + 56 files changed, 85 insertions(+), 7 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index f31a8889458..f2c9af89604 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -93,6 +93,8 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc The Telegram object. """ + data = cls._parse_data(data) + if not data: return None @@ -107,7 +109,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandSc } if cls is BotCommandScope and data.get("type") in _class_mapping: - return _class_mapping[data["type"]].de_json(data=data, bot=bot) + return _class_mapping[data.pop("type")].de_json(data=data, bot=bot) return super().de_json(data=data, bot=bot) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 808a87f88e1..729ec25c7c6 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -75,6 +75,8 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] The Telegram object. """ + data = cls._parse_data(data) + if not data: return None @@ -85,8 +87,9 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] } if cls is MenuButton and data.get("type") in _class_mapping: - return _class_mapping[data["type"]].de_json(data, bot=bot) - return super().de_json(data=data, bot=bot) + return _class_mapping[data.pop("type")].de_json(data, bot=bot) + out = super().de_json(data=data, bot=bot) + return out COMMANDS: ClassVar[str] = constants.MenuButtonType.COMMANDS """:const:`telegram.constants.MenuButtonType.COMMANDS`""" @@ -107,7 +110,7 @@ class MenuButtonCommands(MenuButton): __slots__ = () def __init__(self, api_kwargs: JSONDict = None): - super().__init__(type=constants.MenuButtonType.COMMANDS) + super().__init__(type=constants.MenuButtonType.COMMANDS, api_kwargs=api_kwargs) class MenuButtonWebApp(MenuButton): @@ -137,7 +140,7 @@ class MenuButtonWebApp(MenuButton): __slots__ = ("text", "web_app") def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: JSONDict = None): - super().__init__(type=constants.MenuButtonType.WEB_APP) + super().__init__(type=constants.MenuButtonType.WEB_APP, api_kwargs=api_kwargs) self.text = text self.web_app = web_app @@ -173,4 +176,4 @@ class MenuButtonDefault(MenuButton): __slots__ = () def __init__(self, api_kwargs: JSONDict = None): - super().__init__(type=constants.MenuButtonType.DEFAULT) + super().__init__(type=constants.MenuButtonType.DEFAULT, api_kwargs=api_kwargs) diff --git a/tests/test_animation.py b/tests/test_animation.py index a1c804b2f5c..0e0d46790ee 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -298,6 +298,7 @@ def test_de_json(self, bot, animation): "file_size": self.file_size, } animation = Animation.de_json(json_dict, bot) + assert animation.api_kwargs == {} assert animation.file_id == self.animation_file_id assert animation.file_unique_id == self.animation_file_unique_id assert animation.file_name == self.file_name diff --git a/tests/test_audio.py b/tests/test_audio.py index 36dc2335a05..eea5a66ab52 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -267,12 +267,12 @@ def test_de_json(self, bot, audio): "performer": self.performer, "title": self.title, "file_name": self.file_name, - "caption": self.caption, "mime_type": self.mime_type, "file_size": self.file_size, "thumb": audio.thumb.to_dict(), } json_audio = Audio.de_json(json_dict, bot) + assert json_audio.api_kwargs == {} assert json_audio.file_id == self.audio_file_id assert json_audio.file_unique_id == self.audio_file_unique_id diff --git a/tests/test_botcommand.py b/tests/test_botcommand.py index c377761c70a..a9fbbb9c5bd 100644 --- a/tests/test_botcommand.py +++ b/tests/test_botcommand.py @@ -39,6 +39,7 @@ def test_slot_behaviour(self, bot_command, mro_slots): def test_de_json(self, bot): json_dict = {"command": self.command, "description": self.description} bot_command = BotCommand.de_json(json_dict, bot) + assert bot_command.api_kwargs == {} assert bot_command.command == self.command assert bot_command.description == self.description diff --git a/tests/test_botcommandscope.py b/tests/test_botcommandscope.py index 7a52c08fe37..2b074b2a197 100644 --- a/tests/test_botcommandscope.py +++ b/tests/test_botcommandscope.py @@ -131,6 +131,9 @@ def test_de_json(self, bot, scope_class_and_type, chat_id): json_dict = {"type": type_, "chat_id": chat_id, "user_id": 42} bot_command_scope = BotCommandScope.de_json(json_dict, bot) + assert set(bot_command_scope.api_kwargs.keys()) == {"chat_id", "user_id"} - set( + cls.__slots__ + ) assert isinstance(bot_command_scope, BotCommandScope) assert isinstance(bot_command_scope, cls) diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index 933ea80eda9..b2a06f7d026 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -90,6 +90,7 @@ def test_de_json(self, bot): "game_short_name": self.game_short_name, } callback_query = CallbackQuery.de_json(json_dict, bot) + assert callback_query.api_kwargs == {} assert callback_query.id == self.id_ assert callback_query.from_user == self.from_user diff --git a/tests/test_chatadministratorrights.py b/tests/test_chatadministratorrights.py index 4ce1a20fda0..16175f252a6 100644 --- a/tests/test_chatadministratorrights.py +++ b/tests/test_chatadministratorrights.py @@ -60,6 +60,7 @@ def test_de_json(self, bot, chat_admin_rights): "is_anonymous": True, } chat_administrator_rights_de = ChatAdministratorRights.de_json(json_dict, bot) + assert chat_administrator_rights_de.api_kwargs == {} assert chat_admin_rights == chat_administrator_rights_de diff --git a/tests/test_chatinvitelink.py b/tests/test_chatinvitelink.py index 2d66a79355c..d1c8de3addd 100644 --- a/tests/test_chatinvitelink.py +++ b/tests/test_chatinvitelink.py @@ -70,6 +70,7 @@ def test_de_json_required_args(self, bot, creator): } invite_link = ChatInviteLink.de_json(json_dict, bot) + assert invite_link.api_kwargs == {} assert invite_link.invite_link == self.link assert invite_link.creator == creator @@ -91,6 +92,7 @@ def test_de_json_all_args(self, bot, creator): } invite_link = ChatInviteLink.de_json(json_dict, bot) + assert invite_link.api_kwargs == {} assert invite_link.invite_link == self.link assert invite_link.creator == creator diff --git a/tests/test_chatjoinrequest.py b/tests/test_chatjoinrequest.py index fe4aa190404..b81eab85270 100644 --- a/tests/test_chatjoinrequest.py +++ b/tests/test_chatjoinrequest.py @@ -70,6 +70,7 @@ def test_de_json(self, bot, time): "date": to_timestamp(time), } chat_join_request = ChatJoinRequest.de_json(json_dict, bot) + assert chat_join_request.api_kwargs == {} assert chat_join_request.chat == self.chat assert chat_join_request.from_user == self.from_user @@ -78,6 +79,7 @@ def test_de_json(self, bot, time): json_dict.update({"bio": self.bio, "invite_link": self.invite_link.to_dict()}) chat_join_request = ChatJoinRequest.de_json(json_dict, bot) + assert chat_join_request.api_kwargs == {} assert chat_join_request.chat == self.chat assert chat_join_request.from_user == self.from_user diff --git a/tests/test_chatlocation.py b/tests/test_chatlocation.py index ade82b3e21a..ea9bd3bc6a8 100644 --- a/tests/test_chatlocation.py +++ b/tests/test_chatlocation.py @@ -43,6 +43,7 @@ def test_de_json(self, bot): "address": self.address, } chat_location = ChatLocation.de_json(json_dict, bot) + assert chat_location.api_kwargs == {} assert chat_location.location == self.location assert chat_location.address == self.address diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py index 9717ed0a0ba..2e6248087d6 100644 --- a/tests/test_chatmember.py +++ b/tests/test_chatmember.py @@ -185,6 +185,7 @@ def test_de_json_required_args(self, bot, chat_member_type): json_dict = make_json_dict(chat_member_type) const_chat_member = ChatMember.de_json(json_dict, bot) + assert const_chat_member.api_kwargs == {} assert isinstance(const_chat_member, ChatMember) assert isinstance(const_chat_member, cls) @@ -194,6 +195,7 @@ def test_de_json_required_args(self, bot, chat_member_type): def test_de_json_all_args(self, bot, chat_member_type): json_dict = make_json_dict(chat_member_type, include_optional_args=True) const_chat_member = ChatMember.de_json(json_dict, bot) + assert const_chat_member.api_kwargs == {} assert isinstance(const_chat_member, ChatMember) assert isinstance(const_chat_member, chat_member_type.__class__) diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index 77ecf58a98c..e62aa60273f 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -102,6 +102,7 @@ def test_de_json_required_args(self, bot, user, chat, old_chat_member, new_chat_ } chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot) + assert chat_member_updated.api_kwargs == {} assert chat_member_updated.chat == chat assert chat_member_updated.from_user == user @@ -124,6 +125,7 @@ def test_de_json_all_args( } chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot) + assert chat_member_updated.api_kwargs == {} assert chat_member_updated.chat == chat assert chat_member_updated.from_user == user diff --git a/tests/test_chatpermissions.py b/tests/test_chatpermissions.py index 1d96d8c892d..64f4da6c269 100644 --- a/tests/test_chatpermissions.py +++ b/tests/test_chatpermissions.py @@ -64,6 +64,7 @@ def test_de_json(self, bot): "can_pin_messages": self.can_pin_messages, } permissions = ChatPermissions.de_json(json_dict, bot) + assert permissions.api_kwargs == {} assert permissions.can_send_messages == self.can_send_messages assert permissions.can_send_media_messages == self.can_send_media_messages diff --git a/tests/test_chatphoto.py b/tests/test_chatphoto.py index 7d01f49fdd2..ff64dcb835d 100644 --- a/tests/test_chatphoto.py +++ b/tests/test_chatphoto.py @@ -115,6 +115,7 @@ def test_de_json(self, bot, chat_photo): "big_file_unique_id": self.chatphoto_big_file_unique_id, } chat_photo = ChatPhoto.de_json(json_dict, bot) + assert chat_photo.api_kwargs == {} assert chat_photo.small_file_id == self.chatphoto_small_file_id assert chat_photo.big_file_id == self.chatphoto_big_file_id assert chat_photo.small_file_unique_id == self.chatphoto_small_file_unique_id diff --git a/tests/test_choseninlineresult.py b/tests/test_choseninlineresult.py index 70c55975aba..884c5bba500 100644 --- a/tests/test_choseninlineresult.py +++ b/tests/test_choseninlineresult.py @@ -45,6 +45,7 @@ def test_slot_behaviour(self, chosen_inline_result, mro_slots): def test_de_json_required(self, bot, user): json_dict = {"result_id": self.result_id, "from": user.to_dict(), "query": self.query} result = ChosenInlineResult.de_json(json_dict, bot) + assert result.api_kwargs == {} assert result.result_id == self.result_id assert result.from_user == user @@ -60,6 +61,7 @@ def test_de_json_all(self, bot, user): "inline_message_id": "a random id", } result = ChosenInlineResult.de_json(json_dict, bot) + assert result.api_kwargs == {} assert result.result_id == self.result_id assert result.from_user == user diff --git a/tests/test_contact.py b/tests/test_contact.py index 0217259f762..36de6492760 100644 --- a/tests/test_contact.py +++ b/tests/test_contact.py @@ -49,6 +49,7 @@ def test_slot_behaviour(self, contact, mro_slots): def test_de_json_required(self, bot): json_dict = {"phone_number": self.phone_number, "first_name": self.first_name} contact = Contact.de_json(json_dict, bot) + assert contact.api_kwargs == {} assert contact.phone_number == self.phone_number assert contact.first_name == self.first_name @@ -61,6 +62,7 @@ def test_de_json_all(self, bot): "user_id": self.user_id, } contact = Contact.de_json(json_dict, bot) + assert contact.api_kwargs == {} assert contact.phone_number == self.phone_number assert contact.first_name == self.first_name diff --git a/tests/test_dice.py b/tests/test_dice.py index 4f5dcc92a1c..39d37d7be40 100644 --- a/tests/test_dice.py +++ b/tests/test_dice.py @@ -39,6 +39,7 @@ def test_slot_behaviour(self, dice, mro_slots): def test_de_json(self, bot, emoji): json_dict = {"value": self.value, "emoji": emoji} dice = Dice.de_json(json_dict, bot) + assert dice.api_kwargs == {} assert dice.value == self.value assert dice.emoji == emoji diff --git a/tests/test_document.py b/tests/test_document.py index 54bd0a7e90a..4a9dbcc8917 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -289,6 +289,7 @@ def test_de_json(self, bot, document): "file_size": self.file_size, } test_document = Document.de_json(json_dict, bot) + assert test_document.api_kwargs == {} assert test_document.file_id == self.document_file_id assert test_document.file_unique_id == self.document_file_unique_id diff --git a/tests/test_file.py b/tests/test_file.py index fd4f3b8cda8..ea11b41342e 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -74,6 +74,7 @@ def test_de_json(self, bot): "file_size": self.file_size, } new_file = File.de_json(json_dict, bot) + assert new_file.api_kwargs == {} assert new_file.file_id == self.file_id assert new_file.file_unique_id == self.file_unique_id diff --git a/tests/test_game.py b/tests/test_game.py index 6ff963fb072..5c7e96814ef 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -57,6 +57,7 @@ def test_de_json_required(self, bot): "photo": [self.photo[0].to_dict()], } game = Game.de_json(json_dict, bot) + assert game.api_kwargs == {} assert game.title == self.title assert game.description == self.description @@ -72,6 +73,7 @@ def test_de_json_all(self, bot): "animation": self.animation.to_dict(), } game = Game.de_json(json_dict, bot) + assert game.api_kwargs == {} assert game.title == self.title assert game.description == self.description diff --git a/tests/test_gamehighscore.py b/tests/test_gamehighscore.py index 968f7f8adc0..43ebbe99312 100644 --- a/tests/test_gamehighscore.py +++ b/tests/test_gamehighscore.py @@ -42,6 +42,7 @@ def test_slot_behaviour(self, game_highscore, mro_slots): def test_de_json(self, bot): json_dict = {"position": self.position, "user": self.user.to_dict(), "score": self.score} highscore = GameHighScore.de_json(json_dict, bot) + assert highscore.api_kwargs == {} assert highscore.position == self.position assert highscore.user == self.user diff --git a/tests/test_inlinekeyboardbutton.py b/tests/test_inlinekeyboardbutton.py index c893426927f..dad079ead7a 100644 --- a/tests/test_inlinekeyboardbutton.py +++ b/tests/test_inlinekeyboardbutton.py @@ -107,6 +107,7 @@ def test_de_json(self, bot): } inline_keyboard_button = InlineKeyboardButton.de_json(json_dict, None) + assert inline_keyboard_button.api_kwargs == {} assert inline_keyboard_button.text == self.text assert inline_keyboard_button.url == self.url assert inline_keyboard_button.callback_data == self.callback_data diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py index a54954bced2..a361a386dda 100644 --- a/tests/test_inlinekeyboardmarkup.py +++ b/tests/test_inlinekeyboardmarkup.py @@ -149,6 +149,7 @@ def test_de_json(self): ] } inline_keyboard_markup = InlineKeyboardMarkup.de_json(json_dict, None) + assert inline_keyboard_markup.api_kwargs == {} assert isinstance(inline_keyboard_markup, InlineKeyboardMarkup) keyboard = inline_keyboard_markup.inline_keyboard diff --git a/tests/test_inlinequery.py b/tests/test_inlinequery.py index 75cb9803a57..18a78734ab1 100644 --- a/tests/test_inlinequery.py +++ b/tests/test_inlinequery.py @@ -57,6 +57,7 @@ def test_de_json(self, bot): "location": self.location.to_dict(), } inline_query_json = InlineQuery.de_json(json_dict, bot) + assert inline_query_json.api_kwargs == {} assert inline_query_json.id == self.id_ assert inline_query_json.from_user == self.from_user diff --git a/tests/test_inputinvoicemessagecontent.py b/tests/test_inputinvoicemessagecontent.py index 66466e3a31a..93a408d57ae 100644 --- a/tests/test_inputinvoicemessagecontent.py +++ b/tests/test_inputinvoicemessagecontent.py @@ -210,6 +210,7 @@ def test_de_json(self, bot): } input_invoice_message_content = InputInvoiceMessageContent.de_json(json_dict, bot=bot) + assert input_invoice_message_content.api_kwargs == {} assert input_invoice_message_content.title == self.title assert input_invoice_message_content.description == self.description diff --git a/tests/test_invoice.py b/tests/test_invoice.py index df71d687b52..5703a09d831 100644 --- a/tests/test_invoice.py +++ b/tests/test_invoice.py @@ -63,6 +63,7 @@ def test_de_json(self, bot): }, bot, ) + assert invoice_json.api_kwargs == {} assert invoice_json.title == self.title assert invoice_json.description == self.description diff --git a/tests/test_keyboardbutton.py b/tests/test_keyboardbutton.py index 950dc30ab44..87673b87412 100644 --- a/tests/test_keyboardbutton.py +++ b/tests/test_keyboardbutton.py @@ -72,6 +72,7 @@ def test_de_json(self, bot): } inline_keyboard_button = KeyboardButton.de_json(json_dict, None) + assert inline_keyboard_button.api_kwargs == {} assert inline_keyboard_button.text == self.text assert inline_keyboard_button.request_location == self.request_location assert inline_keyboard_button.request_contact == self.request_contact diff --git a/tests/test_location.py b/tests/test_location.py index fb25c98c9be..75587d2cd14 100644 --- a/tests/test_location.py +++ b/tests/test_location.py @@ -59,6 +59,7 @@ def test_de_json(self, bot): "proximity_alert_radius": TestLocation.proximity_alert_radius, } location = Location.de_json(json_dict, bot) + assert location.api_kwargs == {} assert location.latitude == self.latitude assert location.longitude == self.longitude diff --git a/tests/test_menubutton.py b/tests/test_menubutton.py index 413116003c0..ae928e81379 100644 --- a/tests/test_menubutton.py +++ b/tests/test_menubutton.py @@ -108,6 +108,7 @@ def test_de_json(self, bot, scope_class_and_type): json_dict = {"type": type_, "text": self.text, "web_app": self.web_app.to_dict()} menu_button = MenuButton.de_json(json_dict, bot) + assert set(menu_button.api_kwargs.keys()) == {"text", "web_app"} - set(cls.__slots__) assert isinstance(menu_button, MenuButton) assert type(menu_button) is cls @@ -120,6 +121,7 @@ def test_de_json(self, bot, scope_class_and_type): def test_de_json_invalid_type(self, bot): json_dict = {"type": "invalid", "text": self.text, "web_app": self.web_app.to_dict()} menu_button = MenuButton.de_json(json_dict, bot) + assert menu_button.api_kwargs == {"text": self.text, "web_app": self.web_app.to_dict()} assert type(menu_button) is MenuButton assert menu_button.type == "invalid" diff --git a/tests/test_message.py b/tests/test_message.py index 49a05366832..c9aca3572fb 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -325,6 +325,7 @@ class TestMessage: def test_all_possibilities_de_json_and_to_dict(self, bot, message_params): new = Message.de_json(message_params.to_dict(), bot) + assert new.api_kwargs == {} assert new.to_dict() == message_params.to_dict() # Checking that none of the attributes are dicts is a best effort approach to ensure that diff --git a/tests/test_messageautodeletetimerchanged.py b/tests/test_messageautodeletetimerchanged.py index b0d09a3ae98..ee8e6c4c06b 100644 --- a/tests/test_messageautodeletetimerchanged.py +++ b/tests/test_messageautodeletetimerchanged.py @@ -31,6 +31,7 @@ def test_slot_behaviour(self, mro_slots): def test_de_json(self): json_dict = {"message_auto_delete_time": self.message_auto_delete_time} madtc = MessageAutoDeleteTimerChanged.de_json(json_dict, None) + assert madtc.api_kwargs == {} assert madtc.message_auto_delete_time == self.message_auto_delete_time diff --git a/tests/test_messageentity.py b/tests/test_messageentity.py index e2565c6308c..116d5263788 100644 --- a/tests/test_messageentity.py +++ b/tests/test_messageentity.py @@ -52,6 +52,7 @@ def test_slot_behaviour(self, message_entity, mro_slots): def test_de_json(self, bot): json_dict = {"type": self.type_, "offset": self.offset, "length": self.length} entity = MessageEntity.de_json(json_dict, bot) + assert entity.api_kwargs == {} assert entity.type == self.type_ assert entity.offset == self.offset diff --git a/tests/test_messageid.py b/tests/test_messageid.py index 0a02e63049e..3ae10efe9eb 100644 --- a/tests/test_messageid.py +++ b/tests/test_messageid.py @@ -36,6 +36,7 @@ def test_slot_behaviour(self, message_id, mro_slots): def test_de_json(self): json_dict = {"message_id": self.m_id} message_id = MessageId.de_json(json_dict, None) + assert message_id.api_kwargs == {} assert message_id.message_id == self.m_id diff --git a/tests/test_orderinfo.py b/tests/test_orderinfo.py index 85d80b5cb3d..ab402d468e5 100644 --- a/tests/test_orderinfo.py +++ b/tests/test_orderinfo.py @@ -50,6 +50,7 @@ def test_de_json(self, bot): "shipping_address": TestOrderInfo.shipping_address.to_dict(), } order_info = OrderInfo.de_json(json_dict, bot) + assert order_info.api_kwargs == {} assert order_info.name == self.name assert order_info.phone_number == self.phone_number diff --git a/tests/test_passport.py b/tests/test_passport.py index 12806f034b7..72ac4412e3f 100644 --- a/tests/test_passport.py +++ b/tests/test_passport.py @@ -414,8 +414,10 @@ def test_all_types(self, passport_data, bot, all_passport_data): }, bot=bot, ) + assert new.api_kwargs == {} new.credentials._decrypted_data = Credentials.de_json(credentials, bot) + assert new.credentials.api_kwargs == {} assert isinstance(new, PassportData) assert new.decrypted_data @@ -507,6 +509,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): def test_de_json_and_to_dict(self, bot): passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot) + assert passport_data.api_kwargs == {} assert passport_data.to_dict() == RAW_PASSPORT_DATA assert passport_data.decrypted_data diff --git a/tests/test_photo.py b/tests/test_photo.py index a22fba0f7a8..ebaff6f3346 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -429,6 +429,7 @@ def test_de_json(self, bot, photo): "file_size": self.file_size, } json_photo = PhotoSize.de_json(json_dict, bot) + assert json_photo.api_kwargs == {} assert json_photo.file_id == photo.file_id assert json_photo.file_unique_id == photo.file_unique_id diff --git a/tests/test_poll.py b/tests/test_poll.py index f305f0c0d0e..0d15a0207f5 100644 --- a/tests/test_poll.py +++ b/tests/test_poll.py @@ -41,6 +41,7 @@ def test_slot_behaviour(self, poll_option, mro_slots): def test_de_json(self): json_dict = {"text": self.text, "voter_count": self.voter_count} poll_option = PollOption.de_json(json_dict, None) + assert poll_option.api_kwargs == {} assert poll_option.text == self.text assert poll_option.voter_count == self.voter_count @@ -91,6 +92,7 @@ def test_de_json(self): "option_ids": self.option_ids, } poll_answer = PollAnswer.de_json(json_dict, None) + assert poll_answer.api_kwargs == {} assert poll_answer.poll_id == self.poll_id assert poll_answer.user == self.user @@ -175,6 +177,7 @@ def test_de_json(self, bot): "close_date": to_timestamp(self.close_date), } poll = Poll.de_json(json_dict, bot) + assert poll.api_kwargs == {} assert poll.id == self.id_ assert poll.question == self.question diff --git a/tests/test_precheckoutquery.py b/tests/test_precheckoutquery.py index 7730752e712..6cb9d547c78 100644 --- a/tests/test_precheckoutquery.py +++ b/tests/test_precheckoutquery.py @@ -64,6 +64,7 @@ def test_de_json(self, bot): "order_info": self.order_info.to_dict(), } pre_checkout_query = PreCheckoutQuery.de_json(json_dict, bot) + assert pre_checkout_query.api_kwargs == {} assert pre_checkout_query.get_bot() is bot assert pre_checkout_query.id == self.id_ diff --git a/tests/test_proximityalerttriggered.py b/tests/test_proximityalerttriggered.py index e10c08e3d9b..0d1fd0ff777 100644 --- a/tests/test_proximityalerttriggered.py +++ b/tests/test_proximityalerttriggered.py @@ -48,6 +48,7 @@ def test_de_json(self, bot): "distance": self.distance, } proximity_alert_triggered = ProximityAlertTriggered.de_json(json_dict, bot) + assert proximity_alert_triggered.api_kwargs == {} assert proximity_alert_triggered.traveler == self.traveler assert proximity_alert_triggered.traveler.first_name == self.traveler.first_name diff --git a/tests/test_sentwebappmessage.py b/tests/test_sentwebappmessage.py index 3bf26b2940f..2d3d93da6f7 100644 --- a/tests/test_sentwebappmessage.py +++ b/tests/test_sentwebappmessage.py @@ -47,6 +47,7 @@ def test_to_dict(self, sent_web_app_message): def test_de_json(self, bot): data = {"inline_message_id": self.inline_message_id} m = SentWebAppMessage.de_json(data, None) + assert m.api_kwargs == {} assert m.inline_message_id == self.inline_message_id def test_equality(self): diff --git a/tests/test_shippingaddress.py b/tests/test_shippingaddress.py index 7222c940f67..1552ebc88dd 100644 --- a/tests/test_shippingaddress.py +++ b/tests/test_shippingaddress.py @@ -57,6 +57,7 @@ def test_de_json(self, bot): "post_code": self.post_code, } shipping_address = ShippingAddress.de_json(json_dict, bot) + assert shipping_address.api_kwargs == {} assert shipping_address.country_code == self.country_code assert shipping_address.state == self.state diff --git a/tests/test_shippingquery.py b/tests/test_shippingquery.py index 5ef4d2f4eb3..01dba47c461 100644 --- a/tests/test_shippingquery.py +++ b/tests/test_shippingquery.py @@ -55,6 +55,7 @@ def test_de_json(self, bot): "shipping_address": TestShippingQuery.shipping_address.to_dict(), } shipping_query = ShippingQuery.de_json(json_dict, bot) + assert shipping_query.api_kwargs == {} assert shipping_query.id == self.id_ assert shipping_query.invoice_payload == self.invoice_payload diff --git a/tests/test_sticker.py b/tests/test_sticker.py index de322fd40cc..3e33cf5220c 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -228,6 +228,7 @@ def test_de_json(self, bot, sticker): "custom_emoji_id": self.custom_emoji_id, } json_sticker = Sticker.de_json(json_dict, bot) + assert json_sticker.api_kwargs == {} assert json_sticker.file_id == self.sticker_file_id assert json_sticker.file_unique_id == self.sticker_file_unique_id @@ -873,6 +874,7 @@ def test_mask_position_de_json(self, bot): "scale": self.scale, } mask_position = MaskPosition.de_json(json_dict, bot) + assert mask_position.api_kwargs == {} assert mask_position.point == self.point assert mask_position.x_shift == self.x_shift diff --git a/tests/test_successfulpayment.py b/tests/test_successfulpayment.py index 1190a170f1d..03cc6e178bc 100644 --- a/tests/test_successfulpayment.py +++ b/tests/test_successfulpayment.py @@ -60,6 +60,7 @@ def test_de_json(self, bot): "provider_payment_charge_id": self.provider_payment_charge_id, } successful_payment = SuccessfulPayment.de_json(json_dict, bot) + assert successful_payment.api_kwargs == {} assert successful_payment.invoice_payload == self.invoice_payload assert successful_payment.shipping_option_id == self.shipping_option_id diff --git a/tests/test_update.py b/tests/test_update.py index f1f3fb7e1b4..2cd76586347 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -114,6 +114,7 @@ def test_de_json(self, bot, paramdict): # Convert the single update 'item' to a dict of that item and apply it to the json_dict json_dict.update({k: v.to_dict() for k, v in paramdict.items()}) update = Update.de_json(json_dict, bot) + assert update.api_kwargs == {} assert update.update_id == self.update_id diff --git a/tests/test_user.py b/tests/test_user.py index fbf85b09d8d..8e3849bf070 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -79,6 +79,7 @@ def test_slot_behaviour(self, user, mro_slots): def test_de_json(self, json_dict, bot): user = User.de_json(json_dict, bot) + assert user.api_kwargs == {} assert user.id == self.id_ assert user.is_bot == self.is_bot @@ -96,6 +97,7 @@ def test_de_json_without_username(self, json_dict, bot): del json_dict["username"] user = User.de_json(json_dict, bot) + assert user.api_kwargs == {} assert user.id == self.id_ assert user.is_bot == self.is_bot @@ -114,6 +116,7 @@ def test_de_json_without_username_and_last_name(self, json_dict, bot): del json_dict["last_name"] user = User.de_json(json_dict, bot) + assert user.api_kwargs == {} assert user.id == self.id_ assert user.is_bot == self.is_bot diff --git a/tests/test_userprofilephotos.py b/tests/test_userprofilephotos.py index 31b7123b288..bd13c223b82 100644 --- a/tests/test_userprofilephotos.py +++ b/tests/test_userprofilephotos.py @@ -41,6 +41,7 @@ def test_slot_behaviour(self, mro_slots): def test_de_json(self, bot): json_dict = {"total_count": 2, "photos": [[y.to_dict() for y in x] for x in self.photos]} user_profile_photos = UserProfilePhotos.de_json(json_dict, bot) + assert user_profile_photos.api_kwargs == {} assert user_profile_photos.total_count == self.total_count assert user_profile_photos.photos == self.photos diff --git a/tests/test_venue.py b/tests/test_venue.py index 86e02d3de49..e1c144cb82e 100644 --- a/tests/test_venue.py +++ b/tests/test_venue.py @@ -62,6 +62,7 @@ def test_de_json(self, bot): "google_place_type": TestVenue.google_place_type, } venue = Venue.de_json(json_dict, bot) + assert venue.api_kwargs == {} assert venue.location == self.location assert venue.title == self.title diff --git a/tests/test_video.py b/tests/test_video.py index 9476fb23552..284bbe7b560 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -317,6 +317,7 @@ def test_de_json(self, bot): "file_name": self.file_name, } json_video = Video.de_json(json_dict, bot) + assert json_video.api_kwargs == {} assert json_video.file_id == self.video_file_id assert json_video.file_unique_id == self.video_file_unique_id diff --git a/tests/test_videochat.py b/tests/test_videochat.py index 695fcd57fa9..d4aed477441 100644 --- a/tests/test_videochat.py +++ b/tests/test_videochat.py @@ -49,6 +49,7 @@ def test_slot_behaviour(self, mro_slots): def test_de_json(self): video_chat_started = VideoChatStarted.de_json({}, None) + assert video_chat_started.api_kwargs == {} assert isinstance(video_chat_started, VideoChatStarted) def test_to_dict(self): @@ -69,6 +70,7 @@ def test_slot_behaviour(self, mro_slots): def test_de_json(self): json_dict = {"duration": self.duration} video_chat_ended = VideoChatEnded.de_json(json_dict, None) + assert video_chat_ended.api_kwargs == {} assert video_chat_ended.duration == self.duration @@ -105,6 +107,7 @@ def test_slot_behaviour(self, mro_slots, user1): def test_de_json(self, user1, user2, bot): json_data = {"users": [user1.to_dict(), user2.to_dict()]} video_chat_participants = VideoChatParticipantsInvited.de_json(json_data, bot) + assert video_chat_participants.api_kwargs == {} assert isinstance(video_chat_participants.users, list) assert video_chat_participants.users[0] == user1 @@ -164,6 +167,7 @@ def test_de_json(self, bot): json_dict = {"start_date": to_timestamp(self.start_date)} video_chat_scheduled = VideoChatScheduled.de_json(json_dict, bot) + assert video_chat_scheduled.api_kwargs == {} assert abs(video_chat_scheduled.start_date - self.start_date) < dtm.timedelta(seconds=1) diff --git a/tests/test_videonote.py b/tests/test_videonote.py index 3691d5e2fd5..d5eb7ac36e1 100644 --- a/tests/test_videonote.py +++ b/tests/test_videonote.py @@ -160,6 +160,7 @@ def test_de_json(self, bot): "file_size": self.file_size, } json_video_note = VideoNote.de_json(json_dict, bot) + assert json_video_note.api_kwargs == {} assert json_video_note.file_id == self.videonote_file_id assert json_video_note.file_unique_id == self.videonote_file_unique_id diff --git a/tests/test_voice.py b/tests/test_voice.py index 5d91e869738..e7e0bf61e7f 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -272,6 +272,7 @@ def test_de_json(self, bot): "file_size": self.file_size, } json_voice = Voice.de_json(json_dict, bot) + assert json_voice.api_kwargs == {} assert json_voice.file_id == self.voice_file_id assert json_voice.file_unique_id == self.voice_file_unique_id diff --git a/tests/test_webappdata.py b/tests/test_webappdata.py index 85e2f939755..618d8e53a58 100644 --- a/tests/test_webappdata.py +++ b/tests/test_webappdata.py @@ -49,6 +49,7 @@ def test_to_dict(self, web_app_data): def test_de_json(self, bot): json_dict = {"data": self.data, "button_text": self.button_text} web_app_data = WebAppData.de_json(json_dict, bot) + assert web_app_data.api_kwargs == {} assert web_app_data.data == self.data assert web_app_data.button_text == self.button_text diff --git a/tests/test_webappinfo.py b/tests/test_webappinfo.py index fda04a8517f..42fb0c48e69 100644 --- a/tests/test_webappinfo.py +++ b/tests/test_webappinfo.py @@ -44,6 +44,7 @@ def test_to_dict(self, web_app_info): def test_de_json(self, bot): json_dict = {"url": self.url} web_app_info = WebAppInfo.de_json(json_dict, bot) + assert web_app_info.api_kwargs == {} assert web_app_info.url == self.url diff --git a/tests/test_webhookinfo.py b/tests/test_webhookinfo.py index 063e0ab8093..55d94e33e7f 100644 --- a/tests/test_webhookinfo.py +++ b/tests/test_webhookinfo.py @@ -81,6 +81,7 @@ def test_de_json(self, bot): "last_synchronization_error_date": self.last_synchronization_error_date, } webhook_info = WebhookInfo.de_json(json_dict, bot) + assert webhook_info.api_kwargs == {} assert webhook_info.url == self.url assert webhook_info.has_custom_certificate == self.has_custom_certificate From 5c99ed288a267f5387c418ff128227777872014b Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 15:08:49 +0200 Subject: [PATCH 34/90] bug fix --- telegram/_menubutton.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 729ec25c7c6..d9195167be5 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -77,7 +77,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] """ data = cls._parse_data(data) - if not data: + if data is None: return None _class_mapping: Dict[str, Type["MenuButton"]] = { From 5175cc740f66b0bc669f5acb2cac822476018998 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 25 Sep 2022 20:06:11 +0200 Subject: [PATCH 35/90] More test adaptions --- telegram/_menubutton.py | 3 +++ tests/test_menubutton.py | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index d9195167be5..3d66b665783 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -80,6 +80,9 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButton"] if data is None: return None + if not data and cls is MenuButton: + return None + _class_mapping: Dict[str, Type["MenuButton"]] = { cls.COMMANDS: MenuButtonCommands, cls.WEB_APP: MenuButtonWebApp, diff --git a/tests/test_menubutton.py b/tests/test_menubutton.py index ae928e81379..ec95ed325b1 100644 --- a/tests/test_menubutton.py +++ b/tests/test_menubutton.py @@ -103,9 +103,6 @@ def test_de_json(self, bot, scope_class_and_type): cls = scope_class_and_type[0] type_ = scope_class_and_type[1] - assert cls.de_json({}, bot) is None - assert cls.de_json(None, bot) is None - json_dict = {"type": type_, "text": self.text, "web_app": self.web_app.to_dict()} menu_button = MenuButton.de_json(json_dict, bot) assert set(menu_button.api_kwargs.keys()) == {"text", "web_app"} - set(cls.__slots__) @@ -118,6 +115,9 @@ def test_de_json(self, bot, scope_class_and_type): if "text" in cls.__slots__: assert menu_button.text == self.text + assert cls.de_json(None, bot) is None + assert MenuButton.de_json({}, bot) is None + def test_de_json_invalid_type(self, bot): json_dict = {"type": "invalid", "text": self.text, "web_app": self.web_app.to_dict()} menu_button = MenuButton.de_json(json_dict, bot) From 3f81766cc34621513269838464826feb081c0469 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 29 Sep 2022 18:29:08 +0200 Subject: [PATCH 36/90] make `api_kwargs` a kw-only argument, pass to super in a few places and remove some redundant inits --- telegram/_botcommand.py | 2 +- telegram/_botcommandscope.py | 16 +++--- telegram/_callbackquery.py | 1 + telegram/_chat.py | 1 + telegram/_chatadministratorrights.py | 1 + telegram/_chatinvitelink.py | 1 + telegram/_chatjoinrequest.py | 1 + telegram/_chatlocation.py | 1 + telegram/_chatmember.py | 7 +++ telegram/_chatmemberupdated.py | 1 + telegram/_chatpermissions.py | 1 + telegram/_choseninlineresult.py | 1 + telegram/_dice.py | 2 +- telegram/_files/_basemedium.py | 7 ++- telegram/_files/_basethumbedmedium.py | 1 + telegram/_files/animation.py | 1 + telegram/_files/audio.py | 1 + telegram/_files/chatphoto.py | 1 + telegram/_files/contact.py | 1 + telegram/_files/document.py | 1 + telegram/_files/file.py | 1 + telegram/_files/inputmedia.py | 56 +++++++++++++++++-- telegram/_files/location.py | 1 + telegram/_files/photosize.py | 1 + telegram/_files/sticker.py | 3 + telegram/_files/venue.py | 1 + telegram/_files/video.py | 1 + telegram/_files/videonote.py | 1 + telegram/_files/voice.py | 1 + telegram/_forcereply.py | 1 + telegram/_games/game.py | 1 + telegram/_games/gamehighscore.py | 2 +- telegram/_inline/inlinekeyboardbutton.py | 1 + telegram/_inline/inlinekeyboardmarkup.py | 1 + telegram/_inline/inlinequery.py | 1 + telegram/_inline/inlinequeryresult.py | 2 +- telegram/_inline/inlinequeryresultarticle.py | 1 + telegram/_inline/inlinequeryresultaudio.py | 1 + .../_inline/inlinequeryresultcachedaudio.py | 1 + .../inlinequeryresultcacheddocument.py | 1 + .../_inline/inlinequeryresultcachedgif.py | 1 + .../inlinequeryresultcachedmpeg4gif.py | 1 + .../_inline/inlinequeryresultcachedphoto.py | 1 + .../_inline/inlinequeryresultcachedsticker.py | 1 + .../_inline/inlinequeryresultcachedvideo.py | 1 + .../_inline/inlinequeryresultcachedvoice.py | 1 + telegram/_inline/inlinequeryresultcontact.py | 1 + telegram/_inline/inlinequeryresultdocument.py | 1 + telegram/_inline/inlinequeryresultgame.py | 1 + telegram/_inline/inlinequeryresultgif.py | 1 + telegram/_inline/inlinequeryresultlocation.py | 1 + telegram/_inline/inlinequeryresultmpeg4gif.py | 1 + telegram/_inline/inlinequeryresultphoto.py | 1 + telegram/_inline/inlinequeryresultvenue.py | 1 + telegram/_inline/inlinequeryresultvideo.py | 1 + telegram/_inline/inlinequeryresultvoice.py | 1 + .../_inline/inputcontactmessagecontent.py | 1 + .../_inline/inputinvoicemessagecontent.py | 1 + .../_inline/inputlocationmessagecontent.py | 1 + telegram/_inline/inputtextmessagecontent.py | 1 + telegram/_inline/inputvenuemessagecontent.py | 1 + telegram/_keyboardbutton.py | 1 + telegram/_keyboardbuttonpolltype.py | 2 +- telegram/_loginurl.py | 1 + telegram/_menubutton.py | 8 +-- telegram/_message.py | 1 + telegram/_messageautodeletetimerchanged.py | 1 + telegram/_messageentity.py | 1 + telegram/_messageid.py | 2 +- telegram/_passport/credentials.py | 14 ++--- telegram/_passport/data.py | 3 + .../_passport/encryptedpassportelement.py | 1 + telegram/_passport/passportdata.py | 1 + telegram/_passport/passportelementerrors.py | 21 +++---- telegram/_passport/passportfile.py | 1 + telegram/_payment/invoice.py | 1 + telegram/_payment/labeledprice.py | 2 +- telegram/_payment/orderinfo.py | 1 + telegram/_payment/precheckoutquery.py | 1 + telegram/_payment/shippingaddress.py | 1 + telegram/_payment/shippingoption.py | 1 + telegram/_payment/shippingquery.py | 1 + telegram/_payment/successfulpayment.py | 1 + telegram/_poll.py | 5 +- telegram/_proximityalerttriggered.py | 4 +- telegram/_replykeyboardmarkup.py | 1 + telegram/_replykeyboardremove.py | 2 +- telegram/_sentwebappmessage.py | 2 +- telegram/_telegramobject.py | 2 +- telegram/_update.py | 1 + telegram/_user.py | 1 + telegram/_userprofilephotos.py | 2 +- telegram/_videochat.py | 9 +-- telegram/_webappdata.py | 2 +- telegram/_webappinfo.py | 2 +- telegram/_webhookinfo.py | 1 + tests/test_telegramobject.py | 51 ++++++++++++++++- 97 files changed, 243 insertions(+), 58 deletions(-) diff --git a/telegram/_botcommand.py b/telegram/_botcommand.py index 0aa44c62a78..489366be446 100644 --- a/telegram/_botcommand.py +++ b/telegram/_botcommand.py @@ -42,7 +42,7 @@ class BotCommand(TelegramObject): __slots__ = ("description", "command") - def __init__(self, command: str, description: str, api_kwargs: JSONDict = None): + def __init__(self, command: str, description: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.command = command self.description = description diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index f2c9af89604..c0686b91dd9 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -75,7 +75,7 @@ class BotCommandScope(TelegramObject): CHAT_MEMBER: ClassVar[str] = constants.BotCommandScopeType.CHAT_MEMBER """:const:`telegram.constants.BotCommandScopeType.CHAT_MEMBER`""" - def __init__(self, type: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.type = type self._id_attrs = (self.type,) @@ -126,7 +126,7 @@ class BotCommandScopeDefault(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: JSONDict = None): + def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs) @@ -141,7 +141,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: JSONDict = None): + def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs) @@ -155,7 +155,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: JSONDict = None): + def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs) @@ -169,7 +169,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): __slots__ = () - def __init__(self, api_kwargs: JSONDict = None): + def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) @@ -192,7 +192,7 @@ class BotCommandScopeChat(BotCommandScope): __slots__ = ("chat_id",) - def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): + def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) @@ -220,7 +220,7 @@ class BotCommandScopeChatAdministrators(BotCommandScope): __slots__ = ("chat_id",) - def __init__(self, chat_id: Union[str, int], api_kwargs: JSONDict = None): + def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) @@ -251,7 +251,7 @@ class BotCommandScopeChatMember(BotCommandScope): __slots__ = ("chat_id", "user_id") - def __init__(self, chat_id: Union[str, int], user_id: int, api_kwargs: JSONDict = None): + def __init__(self, chat_id: Union[str, int], user_id: int, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs) self.chat_id = ( chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 9736e7c099f..7ce3bdbedb3 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -118,6 +118,7 @@ def __init__( data: str = None, inline_message_id: str = None, game_short_name: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chat.py b/telegram/_chat.py index 2bc04c6b898..39bce15e087 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -269,6 +269,7 @@ def __init__( join_to_send_messages: bool = None, join_by_request: bool = None, has_restricted_voice_and_video_messages: bool = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chatadministratorrights.py b/telegram/_chatadministratorrights.py index cc2f53dfff3..185782373ca 100644 --- a/telegram/_chatadministratorrights.py +++ b/telegram/_chatadministratorrights.py @@ -118,6 +118,7 @@ def __init__( can_post_messages: bool = None, can_edit_messages: bool = None, can_pin_messages: bool = None, + *, api_kwargs: JSONDict = None, ) -> None: super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chatinvitelink.py b/telegram/_chatinvitelink.py index 1ba2cf60a74..3701b1d0a8c 100644 --- a/telegram/_chatinvitelink.py +++ b/telegram/_chatinvitelink.py @@ -112,6 +112,7 @@ def __init__( member_limit: int = None, name: str = None, pending_join_request_count: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chatjoinrequest.py b/telegram/_chatjoinrequest.py index 15efcd727e3..4edb03a906e 100644 --- a/telegram/_chatjoinrequest.py +++ b/telegram/_chatjoinrequest.py @@ -73,6 +73,7 @@ def __init__( date: datetime.datetime, bio: str = None, invite_link: ChatInviteLink = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chatlocation.py b/telegram/_chatlocation.py index 10a752f9f05..f3286723559 100644 --- a/telegram/_chatlocation.py +++ b/telegram/_chatlocation.py @@ -50,6 +50,7 @@ def __init__( self, location: Location, address: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index 821f3d5950f..7d8f24ced55 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -87,6 +87,7 @@ def __init__( self, user: User, status: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -162,6 +163,7 @@ def __init__( user: User, is_anonymous: bool, custom_title: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.OWNER, user=user, api_kwargs=api_kwargs) @@ -285,6 +287,7 @@ def __init__( can_edit_messages: bool = None, can_pin_messages: bool = None, custom_title: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.ADMINISTRATOR, user=user, api_kwargs=api_kwargs) @@ -325,6 +328,7 @@ class ChatMemberMember(ChatMember): def __init__( self, user: User, + *, api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) @@ -413,6 +417,7 @@ def __init__( can_send_other_messages: bool, can_add_web_page_previews: bool, until_date: datetime.datetime, + *, api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.RESTRICTED, user=user, api_kwargs=api_kwargs) @@ -449,6 +454,7 @@ class ChatMemberLeft(ChatMember): def __init__( self, user: User, + *, api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs) @@ -481,6 +487,7 @@ def __init__( self, user: User, until_date: datetime.datetime, + *, api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs) diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index c0e486cf496..cceb469c78f 100644 --- a/telegram/_chatmemberupdated.py +++ b/telegram/_chatmemberupdated.py @@ -83,6 +83,7 @@ def __init__( old_chat_member: ChatMember, new_chat_member: ChatMember, invite_link: ChatInviteLink = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_chatpermissions.py b/telegram/_chatpermissions.py index 21ec8f01c57..416e3306a27 100644 --- a/telegram/_chatpermissions.py +++ b/telegram/_chatpermissions.py @@ -98,6 +98,7 @@ def __init__( can_change_info: bool = None, can_invite_users: bool = None, can_pin_messages: bool = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_choseninlineresult.py b/telegram/_choseninlineresult.py index 91acf73b6dc..67d29d501f2 100644 --- a/telegram/_choseninlineresult.py +++ b/telegram/_choseninlineresult.py @@ -71,6 +71,7 @@ def __init__( query: str, location: Location = None, inline_message_id: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_dice.py b/telegram/_dice.py index bfcc5c78a8d..985baa6bd7b 100644 --- a/telegram/_dice.py +++ b/telegram/_dice.py @@ -68,7 +68,7 @@ class Dice(TelegramObject): __slots__ = ("emoji", "value") - def __init__(self, value: int, emoji: str, api_kwargs: JSONDict = None): + def __init__(self, value: int, emoji: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.value = value self.emoji = emoji diff --git a/telegram/_files/_basemedium.py b/telegram/_files/_basemedium.py index 420cf29c86c..a574064befb 100644 --- a/telegram/_files/_basemedium.py +++ b/telegram/_files/_basemedium.py @@ -53,7 +53,12 @@ class _BaseMedium(TelegramObject): __slots__ = ("file_id", "file_size", "file_unique_id") def __init__( - self, file_id: str, file_unique_id: str, file_size: int = None, api_kwargs: JSONDict = None + self, + file_id: str, + file_unique_id: str, + file_size: int = None, + *, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index 6cf88f9691c..4db1d6a608f 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -65,6 +65,7 @@ def __init__( file_unique_id: str, file_size: int = None, thumb: PhotoSize = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index bc09e46cec6..13ada07e15b 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -72,6 +72,7 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index 82f843d5e34..a4a0cc06440 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -76,6 +76,7 @@ def __init__( file_size: int = None, thumb: PhotoSize = None, file_name: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/chatphoto.py b/telegram/_files/chatphoto.py index 1d026e38897..35149731ec3 100644 --- a/telegram/_files/chatphoto.py +++ b/telegram/_files/chatphoto.py @@ -76,6 +76,7 @@ def __init__( small_file_unique_id: str, big_file_id: str, big_file_unique_id: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/contact.py b/telegram/_files/contact.py index a3d43decb8d..a9e306fb49a 100644 --- a/telegram/_files/contact.py +++ b/telegram/_files/contact.py @@ -53,6 +53,7 @@ def __init__( last_name: str = None, user_id: int = None, vcard: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 24a9226b622..e650ebde466 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -63,6 +63,7 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/file.py b/telegram/_files/file.py index a39cbe7405a..1ee3adabce0 100644 --- a/telegram/_files/file.py +++ b/telegram/_files/file.py @@ -81,6 +81,7 @@ def __init__( file_unique_id: str, file_size: int = None, file_path: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 2472c50bf63..1452135b650 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -80,6 +80,7 @@ def __init__( caption: str = None, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -172,6 +173,8 @@ def __init__( duration: int = None, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, + *, + api_kwargs: JSONDict = None, ): if isinstance(media, Animation): width = media.width if width is None else width @@ -183,7 +186,14 @@ def __init__( # things to work in local mode. media = parse_file_input(media, filename=filename, attach=True, local_mode=True) - super().__init__(InputMediaType.ANIMATION, media, caption, caption_entities, parse_mode) + super().__init__( + InputMediaType.ANIMATION, + media, + caption, + caption_entities, + parse_mode, + api_kwargs=api_kwargs, + ) self.thumb = self._parse_thumb_input(thumb) self.width = width self.height = height @@ -234,11 +244,20 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, + *, + api_kwargs: JSONDict = None, ): # We use local_mode=True because we don't have access to the actual setting and want # things to work in local mode. media = parse_file_input(media, PhotoSize, filename=filename, attach=True, local_mode=True) - super().__init__(InputMediaType.PHOTO, media, caption, caption_entities, parse_mode) + super().__init__( + InputMediaType.PHOTO, + media, + caption, + caption_entities, + parse_mode, + api_kwargs=api_kwargs, + ) class InputMediaVideo(InputMedia): @@ -314,6 +333,8 @@ def __init__( thumb: FileInput = None, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, + *, + api_kwargs: JSONDict = None, ): if isinstance(media, Video): @@ -326,7 +347,14 @@ def __init__( # things to work in local mode. media = parse_file_input(media, filename=filename, attach=True, local_mode=True) - super().__init__(InputMediaType.VIDEO, media, caption, caption_entities, parse_mode) + super().__init__( + InputMediaType.VIDEO, + media, + caption, + caption_entities, + parse_mode, + api_kwargs=api_kwargs, + ) self.width = width self.height = height self.duration = duration @@ -401,6 +429,8 @@ def __init__( title: str = None, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, + *, + api_kwargs: JSONDict = None, ): if isinstance(media, Audio): duration = media.duration if duration is None else duration @@ -412,7 +442,14 @@ def __init__( # things to work in local mode. media = parse_file_input(media, filename=filename, attach=True, local_mode=True) - super().__init__(InputMediaType.AUDIO, media, caption, caption_entities, parse_mode) + super().__init__( + InputMediaType.AUDIO, + media, + caption, + caption_entities, + parse_mode, + api_kwargs=api_kwargs, + ) self.thumb = self._parse_thumb_input(thumb) self.duration = duration self.title = title @@ -477,10 +514,19 @@ def __init__( disable_content_type_detection: bool = None, caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, + *, + api_kwargs: JSONDict = None, ): # We use local_mode=True because we don't have access to the actual setting and want # things to work in local mode. media = parse_file_input(media, Document, filename=filename, attach=True, local_mode=True) - super().__init__(InputMediaType.DOCUMENT, media, caption, caption_entities, parse_mode) + super().__init__( + InputMediaType.DOCUMENT, + media, + caption, + caption_entities, + parse_mode, + api_kwargs=api_kwargs, + ) self.thumb = self._parse_thumb_input(thumb) self.disable_content_type_detection = disable_content_type_detection diff --git a/telegram/_files/location.py b/telegram/_files/location.py index e5830fe24e8..4b7066a5e28 100644 --- a/telegram/_files/location.py +++ b/telegram/_files/location.py @@ -71,6 +71,7 @@ def __init__( live_period: int = None, heading: int = None, proximity_alert_radius: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 30ca667788c..86fc391426b 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -59,6 +59,7 @@ def __init__( width: int, height: int, file_size: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 2e5d1e25a65..dfd23b6ab26 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -140,6 +140,7 @@ def __init__( mask_position: "MaskPosition" = None, premium_animation: "File" = None, custom_emoji_id: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__( @@ -249,6 +250,7 @@ def __init__( is_video: bool, sticker_type: str, thumb: PhotoSize = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -335,6 +337,7 @@ def __init__( x_shift: float, y_shift: float, scale: float, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/venue.py b/telegram/_files/venue.py index d879e6195de..a87cfb57342 100644 --- a/telegram/_files/venue.py +++ b/telegram/_files/venue.py @@ -80,6 +80,7 @@ def __init__( foursquare_type: str = None, google_place_id: str = None, google_place_type: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_files/video.py b/telegram/_files/video.py index 7a5c9f935db..c3c632f68b8 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -72,6 +72,7 @@ def __init__( mime_type: str = None, file_size: int = None, file_name: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index a1938c3f2ff..5dd7941a169 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -63,6 +63,7 @@ def __init__( duration: int, thumb: PhotoSize = None, file_size: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index 1ec0d3c148b..22346d3c095 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -58,6 +58,7 @@ def __init__( duration: int, mime_type: str = None, file_size: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__( diff --git a/telegram/_forcereply.py b/telegram/_forcereply.py index e07b2a873a4..aa43b81e59b 100644 --- a/telegram/_forcereply.py +++ b/telegram/_forcereply.py @@ -67,6 +67,7 @@ def __init__( self, selective: bool = None, input_field_placeholder: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_games/game.py b/telegram/_games/game.py index c9d9124e68b..6fc191fda1b 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -88,6 +88,7 @@ def __init__( text: str = None, text_entities: List[MessageEntity] = None, animation: Animation = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_games/gamehighscore.py b/telegram/_games/gamehighscore.py index 3abe9438cb9..ba1f788d75e 100644 --- a/telegram/_games/gamehighscore.py +++ b/telegram/_games/gamehighscore.py @@ -48,7 +48,7 @@ class GameHighScore(TelegramObject): __slots__ = ("position", "user", "score") - def __init__(self, position: int, user: User, score: int, api_kwargs: JSONDict = None): + def __init__(self, position: int, user: User, score: int, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.position = position self.user = user diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index a90971f54f3..145766ce83c 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -173,6 +173,7 @@ def __init__( pay: bool = None, login_url: LoginUrl = None, web_app: WebAppInfo = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 09d7e256726..39b96da96c9 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -54,6 +54,7 @@ class InlineKeyboardMarkup(TelegramObject): def __init__( self, inline_keyboard: List[List[InlineKeyboardButton]], + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index 06f654a52c8..9071d42f3cd 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -88,6 +88,7 @@ def __init__( offset: str, location: Location = None, chat_type: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inlinequeryresult.py b/telegram/_inline/inlinequeryresult.py index 325bf210cda..60da54ad56d 100644 --- a/telegram/_inline/inlinequeryresult.py +++ b/telegram/_inline/inlinequeryresult.py @@ -45,7 +45,7 @@ class InlineQueryResult(TelegramObject): __slots__ = ("type", "id") - def __init__(self, type: str, id: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, id: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Required diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index ed98f7a6159..116065ab14a 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -91,6 +91,7 @@ def __init__( thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 80982328cee..afd7bea4f14 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -104,6 +104,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 62c97ee3118..11b0dc6b1c5 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -92,6 +92,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 29d08a3ce56..23d474c7ff1 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -100,6 +100,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index b60903eb1a2..a7e1408f4b0 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -97,6 +97,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index da565c47116..de9bcd197bf 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -97,6 +97,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 7496d55e042..ea088321e00 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -101,6 +101,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index 91143b33b36..0a99a990595 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -62,6 +62,7 @@ def __init__( sticker_file_id: str, reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 26c1c9c9084..a950d7a2ba4 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -101,6 +101,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 472f9febcf2..d0a96152c0c 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -96,6 +96,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index 43f75e632db..7f13216dec5 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -92,6 +92,7 @@ def __init__( thumb_width: int = None, thumb_height: int = None, vcard: str = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 6180eaeeea2..881190570c2 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -119,6 +119,7 @@ def __init__( thumb_height: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 688dfeb60b3..ec1cd317059 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -49,6 +49,7 @@ def __init__( id: str, # pylint: disable=redefined-builtin game_short_name: str, reply_markup: InlineKeyboardMarkup = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index d25f67bd1bc..d0c36c6e7ec 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -119,6 +119,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index 810db71a270..6fb61140600 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -112,6 +112,7 @@ def __init__( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index d60eaadaeaa..12fc7159bb2 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -119,6 +119,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 3fb17c02b43..6dee5ca5e92 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -114,6 +114,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): # Required diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 21dc57be3f8..4b13f7c97ea 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -114,6 +114,7 @@ def __init__( thumb_height: int = None, google_place_id: str = None, google_place_type: str = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 1b5b3dd7987..ba32119661e 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -129,6 +129,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index 4bb4514a0f4..ff20dbee9a9 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -101,6 +101,7 @@ def __init__( input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): diff --git a/telegram/_inline/inputcontactmessagecontent.py b/telegram/_inline/inputcontactmessagecontent.py index e1362ee9d99..1bd640411bc 100644 --- a/telegram/_inline/inputcontactmessagecontent.py +++ b/telegram/_inline/inputcontactmessagecontent.py @@ -52,6 +52,7 @@ def __init__( first_name: str, last_name: str = None, vcard: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 112b1333350..a39952ed865 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -178,6 +178,7 @@ def __init__( send_phone_number_to_provider: bool = None, send_email_to_provider: bool = None, is_flexible: bool = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inputlocationmessagecontent.py b/telegram/_inline/inputlocationmessagecontent.py index bb756faeff2..df147414b14 100644 --- a/telegram/_inline/inputlocationmessagecontent.py +++ b/telegram/_inline/inputlocationmessagecontent.py @@ -70,6 +70,7 @@ def __init__( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index 23fc76f5255..aac335ca1d0 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -71,6 +71,7 @@ def __init__( parse_mode: ODVInput[str] = DEFAULT_NONE, disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_inline/inputvenuemessagecontent.py b/telegram/_inline/inputvenuemessagecontent.py index fa55e7ee534..b6adeece5f5 100644 --- a/telegram/_inline/inputvenuemessagecontent.py +++ b/telegram/_inline/inputvenuemessagecontent.py @@ -80,6 +80,7 @@ def __init__( foursquare_type: str = None, google_place_id: str = None, google_place_type: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_keyboardbutton.py b/telegram/_keyboardbutton.py index cbae095a321..a0511e17e67 100644 --- a/telegram/_keyboardbutton.py +++ b/telegram/_keyboardbutton.py @@ -87,6 +87,7 @@ def __init__( request_location: bool = None, request_poll: KeyboardButtonPollType = None, web_app: WebAppInfo = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_keyboardbuttonpolltype.py b/telegram/_keyboardbuttonpolltype.py index 78d1b94beae..2cc010f39e7 100644 --- a/telegram/_keyboardbuttonpolltype.py +++ b/telegram/_keyboardbuttonpolltype.py @@ -41,7 +41,7 @@ class KeyboardButtonPollType(TelegramObject): __slots__ = ("type",) def __init__( - self, type: str = None, api_kwargs: JSONDict = None # skipcq: PYL-W0622 + self, type: str = None, *, api_kwargs: JSONDict = None # skipcq: PYL-W0622 ): # pylint: disable=redefined-builtin super().__init__(api_kwargs=api_kwargs) self.type = type diff --git a/telegram/_loginurl.py b/telegram/_loginurl.py index 1831719fe8e..d71d8dfbdb5 100644 --- a/telegram/_loginurl.py +++ b/telegram/_loginurl.py @@ -76,6 +76,7 @@ def __init__( forward_text: bool = None, bot_username: str = None, request_write_access: bool = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 3d66b665783..0ecd6df51d9 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -55,7 +55,7 @@ class MenuButton(TelegramObject): __slots__ = ("type",) def __init__( - self, type: str, api_kwargs: JSONDict = None # skipcq: PYL-W0622 + self, type: str, *, api_kwargs: JSONDict = None # skipcq: PYL-W0622 ): # pylint: disable=redefined-builtin super().__init__(api_kwargs=api_kwargs) self.type = type @@ -112,7 +112,7 @@ class MenuButtonCommands(MenuButton): __slots__ = () - def __init__(self, api_kwargs: JSONDict = None): + def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.COMMANDS, api_kwargs=api_kwargs) @@ -142,7 +142,7 @@ class MenuButtonWebApp(MenuButton): __slots__ = ("text", "web_app") - def __init__(self, text: str, web_app: WebAppInfo, api_kwargs: JSONDict = None): + def __init__(self, text: str, web_app: WebAppInfo, *, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.WEB_APP, api_kwargs=api_kwargs) self.text = text self.web_app = web_app @@ -178,5 +178,5 @@ class MenuButtonDefault(MenuButton): __slots__ = () - def __init__(self, api_kwargs: JSONDict = None): + def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.DEFAULT, api_kwargs=api_kwargs) diff --git a/telegram/_message.py b/telegram/_message.py index 89b8aa5fa2f..cf43b456ac6 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -497,6 +497,7 @@ def __init__( is_automatic_forward: bool = None, has_protected_content: bool = None, web_app_data: WebAppData = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_messageautodeletetimerchanged.py b/telegram/_messageautodeletetimerchanged.py index f83a7b554a0..a6e6e67e6cf 100644 --- a/telegram/_messageautodeletetimerchanged.py +++ b/telegram/_messageautodeletetimerchanged.py @@ -47,6 +47,7 @@ class MessageAutoDeleteTimerChanged(TelegramObject): def __init__( self, message_auto_delete_time: int, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index f0d1ca946ff..9fcbd33c66d 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -86,6 +86,7 @@ def __init__( user: User = None, language: str = None, custom_emoji_id: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_messageid.py b/telegram/_messageid.py index 7ecc4d423cc..42db325bff6 100644 --- a/telegram/_messageid.py +++ b/telegram/_messageid.py @@ -37,7 +37,7 @@ class MessageId(TelegramObject): __slots__ = ("message_id",) - def __init__(self, message_id: int, api_kwargs: JSONDict = None): + def __init__(self, message_id: int, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.message_id = message_id diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 03d8d9b5bb8..89e4dde5ed8 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -141,6 +141,7 @@ def __init__( data: str, hash: str, # skipcq: PYL-W0622 secret: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -217,6 +218,7 @@ def __init__( self, secure_data: "SecureData", nonce: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -292,6 +294,7 @@ def __init__( rental_agreement: "SecureValue" = None, passport_registration: "SecureValue" = None, temporary_registration: "SecureValue" = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -373,6 +376,7 @@ def __init__( selfie: "FileCredentials" = None, files: List["FileCredentials"] = None, translation: List["FileCredentials"] = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -415,7 +419,9 @@ class _CredentialsBase(TelegramObject): __slots__ = ("hash", "secret", "file_hash", "data_hash") - def __init__(self, hash: str, secret: str, api_kwargs: JSONDict = None): # skipcq: PYL-W0622 + def __init__( + self, hash: str, secret: str, *, api_kwargs: JSONDict = None + ): # skipcq: PYL-W0622 super().__init__(api_kwargs=api_kwargs) self.hash = hash self.secret = secret @@ -441,9 +447,6 @@ class DataCredentials(_CredentialsBase): __slots__ = () - def __init__(self, data_hash: str, secret: str, api_kwargs: JSONDict = None): - super().__init__(data_hash, secret, api_kwargs=api_kwargs) - def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() @@ -470,9 +473,6 @@ class FileCredentials(_CredentialsBase): __slots__ = () - def __init__(self, file_hash: str, secret: str, api_kwargs: JSONDict = None): - super().__init__(file_hash, secret, api_kwargs=api_kwargs) - def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() diff --git a/telegram/_passport/data.py b/telegram/_passport/data.py index 8bccc195a10..f256f273dac 100644 --- a/telegram/_passport/data.py +++ b/telegram/_passport/data.py @@ -68,6 +68,7 @@ def __init__( last_name_native: str = None, middle_name: str = None, middle_name_native: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -114,6 +115,7 @@ def __init__( state: str, country_code: str, post_code: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) @@ -141,6 +143,7 @@ def __init__( self, document_no: str, expiry_date: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 74ad493f4da..7ba7f057ab2 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -137,6 +137,7 @@ def __init__( selfie: PassportFile = None, translation: List[PassportFile] = None, credentials: "Credentials" = None, # pylint: disable=unused-argument + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 61ff457385d..d67055d0733 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -57,6 +57,7 @@ def __init__( self, data: List[EncryptedPassportElement], credentials: EncryptedCredentials, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index acb743885d9..d61fc62f545 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -45,7 +45,7 @@ class PassportElementError(TelegramObject): __slots__ = ("message", "source", "type") - def __init__(self, source: str, type: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, source: str, type: str, message: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Required self.source = str(source) @@ -90,10 +90,11 @@ def __init__( field_name: str, data_hash: str, message: str, + *, api_kwargs: JSONDict = None, ): # Required - super().__init__("data", type, message) + super().__init__("data", type, message, api_kwargs=api_kwargs) self.field_name = field_name self.data_hash = data_hash @@ -127,7 +128,7 @@ class PassportElementErrorFile(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("file", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash @@ -162,7 +163,7 @@ class PassportElementErrorFiles(PassportElementError): __slots__ = ("file_hashes",) - def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hashes: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("files", type, message, api_kwargs=api_kwargs) self.file_hashes = file_hashes @@ -197,7 +198,7 @@ class PassportElementErrorFrontSide(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("front_side", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash @@ -232,7 +233,7 @@ class PassportElementErrorReverseSide(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("reverse_side", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash @@ -265,7 +266,7 @@ class PassportElementErrorSelfie(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("selfie", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash @@ -302,7 +303,7 @@ class PassportElementErrorTranslationFile(PassportElementError): __slots__ = ("file_hash",) - def __init__(self, type: str, file_hash: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("translation_file", type, message, api_kwargs=api_kwargs) self.file_hash = file_hash @@ -339,7 +340,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): __slots__ = ("file_hashes",) - def __init__(self, type: str, file_hashes: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, file_hashes: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("translation_files", type, message, api_kwargs=api_kwargs) self.file_hashes = file_hashes @@ -370,7 +371,7 @@ class PassportElementErrorUnspecified(PassportElementError): __slots__ = ("element_hash",) - def __init__(self, type: str, element_hash: str, message: str, api_kwargs: JSONDict = None): + def __init__(self, type: str, element_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("unspecified", type, message, api_kwargs=api_kwargs) self.element_hash = element_hash diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 857701def01..41586483670 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -71,6 +71,7 @@ def __init__( file_date: int, file_size: int, credentials: "FileCredentials" = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/invoice.py b/telegram/_payment/invoice.py index 00aa67079f1..bdc325d1e66 100644 --- a/telegram/_payment/invoice.py +++ b/telegram/_payment/invoice.py @@ -69,6 +69,7 @@ def __init__( start_parameter: str, currency: str, total_amount: int, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/labeledprice.py b/telegram/_payment/labeledprice.py index 21544342a2e..73d1650311a 100644 --- a/telegram/_payment/labeledprice.py +++ b/telegram/_payment/labeledprice.py @@ -47,7 +47,7 @@ class LabeledPrice(TelegramObject): __slots__ = ("label", "amount") - def __init__(self, label: str, amount: int, api_kwargs: JSONDict = None): + def __init__(self, label: str, amount: int, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.label = label self.amount = amount diff --git a/telegram/_payment/orderinfo.py b/telegram/_payment/orderinfo.py index 5198401e84b..c402ad3e5bb 100644 --- a/telegram/_payment/orderinfo.py +++ b/telegram/_payment/orderinfo.py @@ -57,6 +57,7 @@ def __init__( phone_number: str = None, email: str = None, shipping_address: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/precheckoutquery.py b/telegram/_payment/precheckoutquery.py index 482f75a060c..af009beeb6e 100644 --- a/telegram/_payment/precheckoutquery.py +++ b/telegram/_payment/precheckoutquery.py @@ -86,6 +86,7 @@ def __init__( invoice_payload: str, shipping_option_id: str = None, order_info: OrderInfo = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/shippingaddress.py b/telegram/_payment/shippingaddress.py index aee05fb214b..9f1f4d8b897 100644 --- a/telegram/_payment/shippingaddress.py +++ b/telegram/_payment/shippingaddress.py @@ -64,6 +64,7 @@ def __init__( street_line1: str, street_line2: str, post_code: str, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index c6a6e5e0016..36dc8bf3e75 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -54,6 +54,7 @@ def __init__( id: str, # pylint: disable=redefined-builtin title: str, prices: List["LabeledPrice"], + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index c487c03e0b0..09d9892c697 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -63,6 +63,7 @@ def __init__( from_user: User, invoice_payload: str, shipping_address: ShippingAddress, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_payment/successfulpayment.py b/telegram/_payment/successfulpayment.py index a63338bf7e6..cfbd7fd01fd 100644 --- a/telegram/_payment/successfulpayment.py +++ b/telegram/_payment/successfulpayment.py @@ -81,6 +81,7 @@ def __init__( provider_payment_charge_id: str, shipping_option_id: str = None, order_info: OrderInfo = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_poll.py b/telegram/_poll.py index 4456d708887..43c62652c5a 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -53,7 +53,7 @@ class PollOption(TelegramObject): __slots__ = ("voter_count", "text") - def __init__(self, text: str, voter_count: int, api_kwargs: JSONDict = None): + def __init__(self, text: str, voter_count: int, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) self.text = text self.voter_count = voter_count @@ -87,7 +87,7 @@ class PollAnswer(TelegramObject): __slots__ = ("option_ids", "user", "poll_id") def __init__( - self, poll_id: str, user: User, option_ids: List[int], api_kwargs: JSONDict = None + self, poll_id: str, user: User, option_ids: List[int], *, api_kwargs: JSONDict = None ): super().__init__(api_kwargs=api_kwargs) self.poll_id = poll_id @@ -194,6 +194,7 @@ def __init__( explanation_entities: List[MessageEntity] = None, open_period: int = None, close_date: datetime.datetime = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_proximityalerttriggered.py b/telegram/_proximityalerttriggered.py index af07399eaf0..b19d76fa758 100644 --- a/telegram/_proximityalerttriggered.py +++ b/telegram/_proximityalerttriggered.py @@ -49,7 +49,9 @@ class ProximityAlertTriggered(TelegramObject): __slots__ = ("traveler", "distance", "watcher") - def __init__(self, traveler: User, watcher: User, distance: int, api_kwargs: JSONDict = None): + def __init__( + self, traveler: User, watcher: User, distance: int, *, api_kwargs: JSONDict = None + ): super().__init__(api_kwargs=api_kwargs) self.traveler = traveler self.watcher = watcher diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index c5aafa84df3..5e78bd5a1f9 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -90,6 +90,7 @@ def __init__( one_time_keyboard: bool = None, selective: bool = None, input_field_placeholder: str = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_replykeyboardremove.py b/telegram/_replykeyboardremove.py index 27c526ed0f7..57750b2a26f 100644 --- a/telegram/_replykeyboardremove.py +++ b/telegram/_replykeyboardremove.py @@ -55,7 +55,7 @@ class ReplyKeyboardRemove(TelegramObject): __slots__ = ("selective", "remove_keyboard") - def __init__(self, selective: bool = None, api_kwargs: JSONDict = None): + def __init__(self, selective: bool = None, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Required self.remove_keyboard = True diff --git a/telegram/_sentwebappmessage.py b/telegram/_sentwebappmessage.py index 4f6c2df8a4e..4b8e4fbb968 100644 --- a/telegram/_sentwebappmessage.py +++ b/telegram/_sentwebappmessage.py @@ -43,7 +43,7 @@ class SentWebAppMessage(TelegramObject): __slots__ = ("inline_message_id",) - def __init__(self, inline_message_id: str = None, api_kwargs: JSONDict = None): + def __init__(self, inline_message_id: str = None, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Optionals self.inline_message_id = inline_message_id diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 2ea54abee6d..34e9ba08ad9 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -74,7 +74,7 @@ class TelegramObject: # unless it's overridden __INIT_PARAMS_CHECK: Optional[Type["TelegramObject"]] = None - def __init__(self, api_kwargs: JSONDict = None) -> None: + def __init__(self, *, api_kwargs: JSONDict = None) -> None: self._id_attrs: Tuple[object, ...] = () self._bot: Optional["Bot"] = None # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs diff --git a/telegram/_update.py b/telegram/_update.py index 4cd0919dba0..c2db64281e6 100644 --- a/telegram/_update.py +++ b/telegram/_update.py @@ -234,6 +234,7 @@ def __init__( my_chat_member: ChatMemberUpdated = None, chat_member: ChatMemberUpdated = None, chat_join_request: ChatJoinRequest = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_user.py b/telegram/_user.py index fcb938af0d6..52633d9b379 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -138,6 +138,7 @@ def __init__( supports_inline_queries: bool = None, is_premium: bool = None, added_to_attachment_menu: bool = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 8db281761e3..7a513690330 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -48,7 +48,7 @@ class UserProfilePhotos(TelegramObject): __slots__ = ("photos", "total_count") def __init__( - self, total_count: int, photos: List[List[PhotoSize]], api_kwargs: JSONDict = None + self, total_count: int, photos: List[List[PhotoSize]], *, api_kwargs: JSONDict = None ): super().__init__(api_kwargs=api_kwargs) # Required diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 93a6d65cf73..afd7507510a 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -42,12 +42,6 @@ class VideoChatStarted(TelegramObject): __slots__ = () - def __init__( - self, - api_kwargs: JSONDict = None, - ): # skipcq: PTC-W0049 - super().__init__(api_kwargs=api_kwargs) - class VideoChatEnded(TelegramObject): """ @@ -75,6 +69,7 @@ class VideoChatEnded(TelegramObject): def __init__( self, duration: int, + *, api_kwargs: JSONDict = None, ) -> None: super().__init__(api_kwargs=api_kwargs) @@ -106,6 +101,7 @@ class VideoChatParticipantsInvited(TelegramObject): def __init__( self, users: List[User], + *, api_kwargs: JSONDict = None, ) -> None: super().__init__(api_kwargs=api_kwargs) @@ -160,6 +156,7 @@ class VideoChatScheduled(TelegramObject): def __init__( self, start_date: dtm.datetime, + *, api_kwargs: JSONDict = None, ) -> None: super().__init__(api_kwargs=api_kwargs) diff --git a/telegram/_webappdata.py b/telegram/_webappdata.py index d5d048ca3d4..361c2607988 100644 --- a/telegram/_webappdata.py +++ b/telegram/_webappdata.py @@ -51,7 +51,7 @@ class WebAppData(TelegramObject): __slots__ = ("data", "button_text") - def __init__(self, data: str, button_text: str, api_kwargs: JSONDict = None): + def __init__(self, data: str, button_text: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Required self.data = data diff --git a/telegram/_webappinfo.py b/telegram/_webappinfo.py index 3417ac39409..581ef099a9c 100644 --- a/telegram/_webappinfo.py +++ b/telegram/_webappinfo.py @@ -46,7 +46,7 @@ class WebAppInfo(TelegramObject): __slots__ = ("url",) - def __init__(self, url: str, api_kwargs: JSONDict = None): + def __init__(self, url: str, *, api_kwargs: JSONDict = None): super().__init__(api_kwargs=api_kwargs) # Required self.url = url diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index ff8570def2f..3bb58d78f57 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -101,6 +101,7 @@ def __init__( allowed_updates: List[str] = None, ip_address: str = None, last_synchronization_error_date: int = None, + *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 73972c18e5e..35596be1146 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -17,12 +17,28 @@ # 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 datetime +import inspect import pickle from copy import deepcopy +from pathlib import Path import pytest -from telegram import Chat, Message, PhotoSize, TelegramObject, User +from telegram import Bot, Chat, Message, PhotoSize, TelegramObject, User + + +def all_subclasses(cls): + # Gets all subclasses of the specified object, recursively. from + # https://stackoverflow.com/a/3862957/9706202 + # also includes the class itself + return ( + set(cls.__subclasses__()) + .union([s for c in cls.__subclasses__() for s in all_subclasses(c)]) + .union({cls}) + ) + + +TO_SUBCLASSES = sorted(all_subclasses(TelegramObject), key=lambda cls: cls.__name__) class TestTelegramObject: @@ -64,6 +80,39 @@ def test_de_json_api_kwargs(self, bot): assert to.api_kwargs == {"foo": "bar"} assert to.get_bot() is bot + @pytest.mark.parametrize("cls", TO_SUBCLASSES, ids=[cls.__name__ for cls in TO_SUBCLASSES]) + def test_subclasses_have_api_kwargs(self, cls): + """Checks that all subclasses of TelegramObject have an api_kwargs argument that is + kw-only. Also, tries to check that this argument is passed to super - by checking that + the `__init__` contains `api_kwargs=api_kwargs` + """ + if issubclass(cls, Bot): + # Bot doesn't have api_kwargs, because it's not defined by TG + return + + # only relevant for subclasses that have their own init + if inspect.getsourcefile(cls.__init__) != inspect.getsourcefile(cls): + return + + # Ignore classes in the test directory + source_file = Path(inspect.getsourcefile(cls)) + parents = source_file.parents + is_test_file = Path(__file__).parent.resolve() in parents + if is_test_file: + return + + # check the signature first + signature = inspect.signature(cls) + assert signature.parameters.get("api_kwargs").kind == inspect.Parameter.KEYWORD_ONLY + + # Now check for `api_kwargs=api_kwargs` in the source code of `__init__` + if cls is TelegramObject: + # TelegramObject doesn't have a super class + return + assert "api_kwargs=api_kwargs" in inspect.getsource( + cls.__init__ + ), f"{cls.__name__} doesn't seem to pass `api_kwargs` to `super().__init__`" + def test_de_json_arbitrary_exceptions(self, bot): class SubClass(TelegramObject): def __init__(self, **kwargs): From cf6475b655351c7021704bdb80e6b402b12f1ce4 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 29 Sep 2022 19:23:12 +0200 Subject: [PATCH 37/90] tippity tappity, fixing tests --- telegram/_passport/credentials.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 89e4dde5ed8..2c655c628ae 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -447,6 +447,9 @@ class DataCredentials(_CredentialsBase): __slots__ = () + def __init__(self, data_hash: str, secret: str, *, api_kwargs: JSONDict = None): + super().__init__(hash=data_hash, secret=secret, api_kwargs=api_kwargs) + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() @@ -473,6 +476,9 @@ class FileCredentials(_CredentialsBase): __slots__ = () + def __init__(self, file_hash: str, secret: str, *, api_kwargs: JSONDict = None): + super().__init__(hash=file_hash, secret=secret, api_kwargs=api_kwargs) + def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() From b5b2cf098b604d8b94a7586399a200519aec89dc Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 7 Oct 2022 20:26:28 +0200 Subject: [PATCH 38/90] Make api_kwargs read-only and fix some merge errors --- telegram/_botcommandscope.py | 4 +++ telegram/_chatmember.py | 2 ++ telegram/_files/inputmedia.py | 2 ++ telegram/_menubutton.py | 2 ++ telegram/_passport/credentials.py | 2 ++ telegram/_telegramobject.py | 49 ++++++++++++++++++++++++++----- tests/test_telegramobject.py | 15 +++++++++- 7 files changed, 67 insertions(+), 9 deletions(-) diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 5f57892ab56..7f37340724d 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -130,6 +130,7 @@ class BotCommandScopeDefault(BotCommandScope): def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.DEFAULT, api_kwargs=api_kwargs) + self._freeze() class BotCommandScopeAllPrivateChats(BotCommandScope): @@ -145,6 +146,7 @@ class BotCommandScopeAllPrivateChats(BotCommandScope): def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_PRIVATE_CHATS, api_kwargs=api_kwargs) + self._freeze() class BotCommandScopeAllGroupChats(BotCommandScope): @@ -159,6 +161,7 @@ class BotCommandScopeAllGroupChats(BotCommandScope): def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_GROUP_CHATS, api_kwargs=api_kwargs) + self._freeze() class BotCommandScopeAllChatAdministrators(BotCommandScope): @@ -173,6 +176,7 @@ class BotCommandScopeAllChatAdministrators(BotCommandScope): def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.ALL_CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) + self._freeze() class BotCommandScopeChat(BotCommandScope): diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index d7e56bbe1b9..ec5d0892ff8 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -340,6 +340,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.MEMBER, user=user, api_kwargs=api_kwargs) + self._freeze() class ChatMemberRestricted(ChatMember): @@ -469,6 +470,7 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.LEFT, user=user, api_kwargs=api_kwargs) + self._freeze() class ChatMemberBanned(ChatMember): diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 4a3a47fbb19..7b4ccf6239a 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -264,6 +264,8 @@ def __init__( api_kwargs=api_kwargs, ) + self._freeze() + class InputMediaVideo(InputMedia): """Represents a video to be sent. diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 5e2c3cbd026..66fc68edeb2 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -116,6 +116,7 @@ class MenuButtonCommands(MenuButton): def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.COMMANDS, api_kwargs=api_kwargs) + self._freeze() class MenuButtonWebApp(MenuButton): @@ -185,3 +186,4 @@ class MenuButtonDefault(MenuButton): def __init__(self, *, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.DEFAULT, api_kwargs=api_kwargs) + self._freeze() diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 216ab946e9d..5ceec8c589d 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -460,6 +460,7 @@ class DataCredentials(_CredentialsBase): def __init__(self, data_hash: str, secret: str, *, api_kwargs: JSONDict = None): super().__init__(hash=data_hash, secret=secret, api_kwargs=api_kwargs) + self._freeze() def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" @@ -489,6 +490,7 @@ class FileCredentials(_CredentialsBase): def __init__(self, file_hash: str, secret: str, *, api_kwargs: JSONDict = None): super().__init__(hash=file_hash, secret=secret, api_kwargs=api_kwargs) + self._freeze() def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 7673fe8079c..d00d009e316 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -20,7 +20,20 @@ import inspect import json from copy import deepcopy -from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Type, TypeVar, Union +from types import MappingProxyType +from typing import ( + TYPE_CHECKING, + Any, + Dict, + List, + Mapping, + Optional, + Set, + Tuple, + Type, + TypeVar, + Union, +) from telegram._utils.types import JSONDict from telegram._utils.warnings import warn @@ -58,7 +71,7 @@ class TelegramObject: .. versionadded:: 20.0 Attributes: - api_kwargs (Dict[:obj:`str`, any]): |toapikwargsattr| + api_kwargs (:obj:`types.MappingProxyType` [:obj:`str`, any]): |toapikwargsattr| .. versionadded:: 20.0 @@ -79,7 +92,7 @@ def __init__(self, *, api_kwargs: JSONDict = None) -> None: self._id_attrs: Tuple[object, ...] = () self._bot: Optional["Bot"] = None # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs - self.api_kwargs: JSONDict = api_kwargs or {} + self.api_kwargs: Mapping[str, Any] = MappingProxyType(api_kwargs or {}) def _freeze(self) -> None: self._frozen = True @@ -87,9 +100,10 @@ def _freeze(self) -> None: def _unfreeze(self) -> None: self._frozen = False - def _apply_api_kwargs(self) -> None: + def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None: """Loops through the api kwargs and for every key that exists as attribute of the object (and is None), it moves the value from `api_kwargs` to the attribute. + *Edits `api_kwargs` in pace!* This method is currently only called in the unpickling process, i.e. not on "normal" init. This is because @@ -102,9 +116,9 @@ def _apply_api_kwargs(self) -> None: then you can pass everything as proper argument. """ # we convert to list to ensure that the list doesn't change length while we loop - for key in list(self.api_kwargs.keys()): + for key in list(api_kwargs.keys()): if getattr(self, key, True) is None: - setattr(self, key, self.api_kwargs.pop(key)) + setattr(self, key, api_kwargs.pop(key)) def __setattr__(self, key: str, value: object) -> None: # protected attributes can always be set for convenient internal use @@ -145,7 +159,11 @@ def __getstate__(self) -> Dict[str, Union[str, object]]: This method is used for pickling. We remove the bot attribute of the object since those are not pickable. """ - return self._get_attrs(include_private=True, recursive=False, remove_bot=True) + out = self._get_attrs(include_private=True, recursive=False, remove_bot=True) + # MappingProxyType is not pickable, so we convert it to a dict and revert in + # __setstate__ + out["api_kwargs"] = dict(self.api_kwargs) + return out def __setstate__(self, state: dict) -> None: """ @@ -162,8 +180,17 @@ def __setstate__(self, state: dict) -> None: if key == "_frozen": # Setting the frozen status to True would prevent the attributes from being set continue + if key == "api_kwargs": + # See below + continue setattr(self, key, val) - self._apply_api_kwargs() + + # For api_kwargs we first apply any kwargs that are already attributes of the object + # 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. + api_kwargs = state["api_kwargs"] + self._apply_api_kwargs(api_kwargs) + setattr(self, "api_kwargs", MappingProxyType(api_kwargs)) # Apply freezing if necessary if state["_frozen"]: @@ -184,6 +211,12 @@ def __deepcopy__(self: Tele_co, memodict: dict) -> Tele_co: if k == "_frozen": # Setting the frozen status to True would prevent the attributes from being set continue + if k == "api_kwargs": + # Need to copy api_kwargs manually, since it's a MappingProxyType is not + # pickable and deepcopy uses the pickle interface + setattr(result, k, MappingProxyType(deepcopy(dict(self.api_kwargs), memodict))) + continue + setattr(result, k, deepcopy(getattr(self, k), memodict)) # Apply freezing if necessary diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 1dd23a1cdb0..b022f515356 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -21,6 +21,7 @@ import pickle from copy import deepcopy from pathlib import Path +from types import MappingProxyType import pytest @@ -80,6 +81,15 @@ def test_de_json_api_kwargs(self, bot): assert to.api_kwargs == {"foo": "bar"} assert to.get_bot() is bot + def test_api_kwargs_read_only(self): + tg_object = TelegramObject(api_kwargs={"foo": "bar"}) + tg_object._freeze() + assert isinstance(tg_object.api_kwargs, MappingProxyType) + with pytest.raises(TypeError): + tg_object.api_kwargs["foo"] = "baz" + with pytest.raises(AttributeError, match="can't be set"): + tg_object.api_kwargs = {"foo": "baz"} + @pytest.mark.parametrize("cls", TO_SUBCLASSES, ids=[cls.__name__ for cls in TO_SUBCLASSES]) def test_subclasses_have_api_kwargs(self, cls): """Checks that all subclasses of TelegramObject have an api_kwargs argument that is @@ -237,7 +247,9 @@ def test_deepcopy_telegram_obj(self, bot): date = datetime.datetime.now() photo = PhotoSize("file_id", "unique", 21, 21) photo.set_bot(bot) - msg = Message(1, date, chat, from_user=user, text="foobar", photo=[photo]) + msg = Message( + 1, date, chat, from_user=user, text="foobar", photo=[photo], api_kwargs={"foo": "bar"} + ) msg.set_bot(bot) new_msg = deepcopy(msg) @@ -252,6 +264,7 @@ def test_deepcopy_telegram_obj(self, bot): assert new_msg.chat == chat and new_msg.chat is not chat assert new_msg.from_user == user and new_msg.from_user is not user assert new_msg.photo[0] == photo and new_msg.photo[0] is not photo + assert new_msg.api_kwargs == {"foo": "bar"} and new_msg.api_kwargs is not msg.api_kwargs # check that deepcopy preserves the freezing status with pytest.raises( From d0194e32b7909716efbf8353c8ead574d01c8cf1 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:14:44 +0100 Subject: [PATCH 39/90] Try adapting to #3311 --- telegram/_bot.py | 6 ++++++ telegram/ext/_extbot.py | 7 +++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 12ca10dd9ca..9501a557819 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -2879,23 +2879,29 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if hasattr(res, "parse_mode"): res = copy(res) copied = True + res._unfreeze() # pylint: disable=protected-access res.parse_mode = DefaultValue.get_value(res.parse_mode) + res._freeze() # pylint: disable=protected-access if hasattr(res, "input_message_content") and res.input_message_content: if hasattr(res.input_message_content, "parse_mode"): if not copied: res = copy(res) copied = True res.input_message_content = copy(res.input_message_content) + res.input_message_content._unfreeze() # pylint: disable=protected-access res.input_message_content.parse_mode = DefaultValue.get_value( res.input_message_content.parse_mode ) + res.input_message_content._freeze() # pylint: disable=protected-access if hasattr(res.input_message_content, "disable_web_page_preview"): if not copied: res = copy(res) res.input_message_content = copy(res.input_message_content) + res.input_message_content._unfreeze() # pylint: disable=protected-access res.input_message_content.disable_web_page_preview = DefaultValue.get_value( res.input_message_content.disable_web_page_preview ) + res.input_message_content._freeze() # pylint: disable=protected-access return res diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 81ff2a75112..27f8a0a2a62 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -572,6 +572,7 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ res._unfreeze() # pylint: disable=protected-access copied = True res.parse_mode = self.defaults.parse_mode if self.defaults else None + res._freeze() # pylint: disable=protected-access if hasattr(res, "input_message_content") and res.input_message_content: if ( hasattr(res.input_message_content, "parse_mode") @@ -580,20 +581,22 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if not copied: res = copy(res) copied = True + res.input_message_content._unfreeze() # pylint: disable=protected-access res.input_message_content.parse_mode = ( self.defaults.parse_mode if self.defaults else None ) + res.input_message_content._freeze() # pylint: disable=protected-access if ( hasattr(res.input_message_content, "disable_web_page_preview") and res.input_message_content.disable_web_page_preview is DEFAULT_NONE ): if not copied: res = copy(res) + res.input_message_content._unfreeze() # pylint: disable=protected-access res.input_message_content.disable_web_page_preview = ( self.defaults.disable_web_page_preview if self.defaults else None ) - - res._freeze() # pylint: disable=protected-access + res.input_message_content._freeze() # pylint: disable=protected-access return res From 7d44114ecdac24b161ed3f65c3415678442f2440 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 31 Oct 2022 14:17:55 +0100 Subject: [PATCH 40/90] Remove an unnecessary unfreeze --- telegram/_inline/inlinekeyboardbutton.py | 2 +- telegram/ext/_application.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index 46f5cfc5e46..3f859dcdc77 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -233,5 +233,5 @@ def update_callback_data(self, callback_data: Union[str, object]) -> None: """ self._unfreeze() self.callback_data = callback_data - self._freeze() self._set_id_attrs() + self._freeze() diff --git a/telegram/ext/_application.py b/telegram/ext/_application.py index c9d1209538b..e4a77190cf0 100644 --- a/telegram/ext/_application.py +++ b/telegram/ext/_application.py @@ -437,7 +437,6 @@ async def _initialize_persistence(self) -> None: if persistent_data is not None: if not isinstance(persistent_data, tuple) or len(persistent_data) != 2: raise ValueError("callback_data must be a tuple of length 2") - self.bot._unfreeze() # pylint: disable=protected-access # Mypy doesn't know that persistence.set_bot (see above) already checks that # self.bot is an instance of ExtBot if callback_data should be stored ... @@ -445,8 +444,6 @@ async def _initialize_persistence(self) -> None: persistent_data ) - self.bot._freeze() # pylint: disable=protected-access - @staticmethod def builder() -> "InitApplicationBuilder": """Convenience method. Returns a new :class:`telegram.ext.ApplicationBuilder`. From ba73b3d6420473d551f91ece29d3456e3b61251d Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:27:38 +0100 Subject: [PATCH 41/90] First draft of a script that converts all list-style class attributes to tuples --- immutabilize.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 immutabilize.py diff --git a/immutabilize.py b/immutabilize.py new file mode 100644 index 00000000000..858f9a23a34 --- /dev/null +++ b/immutabilize.py @@ -0,0 +1,85 @@ +# Loops through all TG classes and converts list-type attributes to tuples. +import inspect +import re +from pathlib import Path + +import telegram + +# loop through all classes in the `telegram` module +classes = inspect.getmembers(telegram, inspect.isclass) +for name, cls in classes: + print("Processing class", name) + # first adjust the __init__ of the class + params = inspect.signature(cls.__init__).parameters + params_to_change = set() + for param in params.values(): + if "List" in str(param.annotation): + print(" Converting list-type parameter", param.name, "to Collection") + params_to_change.add(param.name) + + if not params_to_change: + continue + + class_source_file = Path(inspect.getfile(cls)) + + _, class_start_line = inspect.getsourcelines(cls) + class_source = inspect.getsource(cls) + class_source_lines = class_source.splitlines() + class_length = len(class_source_lines) + + init_source_lines, init_start_line = inspect.getsourcelines(cls.__init__) + init_length = len(init_source_lines) + init_source = inspect.getsource(cls.__init__) + + args_start_line = -1 + attributes_start_line = -1 + # Search "Args:" block in the docstring + for i, line in enumerate(class_source_lines): + if line.strip().startswith("Args:"): + args_start_line = i + if line.strip().startswith("Attributes:"): + attributes_start_line = i + if class_start_line + i == init_start_line: + break + + # In the "Args:" block replace "List[" by "Collection[" + for i in range(args_start_line + 1, attributes_start_line): + for param in params_to_change: + class_source_lines[i] = class_source_lines[i].replace( + f"{param} (List[", f"{param} (Collection[" + ) + + # In the "Attributes:" block replace "List[" by "Tuple[" + for i in range(attributes_start_line + 1, class_length): + for param in params_to_change: + class_source_lines[i] = class_source_lines[i].replace( + f"{param} (List[", f"{param} (Tuple[" + ) + + # Adjust type annotations in the __init__ and converts to tuples before assigning to + # attributes + for param in params_to_change: + init_source = init_source.replace(param + ": List", param + ": Collection") + init_source = re.sub( + rf"self\.{param} = ([^ ]*)\n", rf"self.{param} = tuple(\1)\n", init_source + ) + init_source = re.sub( + rf"self\.{param} = (.*) or \[\]\n", rf"self.{param} = tuple(\1 or ())\n", init_source + ) + + file_contents = class_source_file.read_text(encoding="utf-8") + file_contents = file_contents.splitlines() + + # Insert new __init__ + file_contents[ + init_start_line - 1 : init_start_line + init_length - 1 + ] = init_source.splitlines() + + # Insert new docstring + file_contents[class_start_line - 1 : init_start_line - 2] = class_source_lines[ + : init_start_line - class_start_line - 1 + ] + + file_contents = "\n".join(file_contents) + "\n" + + class_source_file.write_text(file_contents, encoding="utf-8") From 8745329f306815bd4383e99487cd2e0f7176c6e1 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 7 Nov 2022 22:02:07 +0100 Subject: [PATCH 42/90] Sequence, not Collection --- immutabilize.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/immutabilize.py b/immutabilize.py index 858f9a23a34..358a43f57c1 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -14,7 +14,7 @@ params_to_change = set() for param in params.values(): if "List" in str(param.annotation): - print(" Converting list-type parameter", param.name, "to Collection") + print(" Converting list-type parameter", param.name, "to Sequence") params_to_change.add(param.name) if not params_to_change: @@ -42,11 +42,11 @@ if class_start_line + i == init_start_line: break - # In the "Args:" block replace "List[" by "Collection[" + # In the "Args:" block replace "List[" by "Sequence[" for i in range(args_start_line + 1, attributes_start_line): for param in params_to_change: class_source_lines[i] = class_source_lines[i].replace( - f"{param} (List[", f"{param} (Collection[" + f"{param} (List[", f"{param} (Sequence[" ) # In the "Attributes:" block replace "List[" by "Tuple[" @@ -59,7 +59,7 @@ # Adjust type annotations in the __init__ and converts to tuples before assigning to # attributes for param in params_to_change: - init_source = init_source.replace(param + ": List", param + ": Collection") + init_source = init_source.replace(param + ": List", param + ": Sequence") init_source = re.sub( rf"self\.{param} = ([^ ]*)\n", rf"self.{param} = tuple(\1)\n", init_source ) From 0ea335f8ced64fd33f4ff3c83ee73f8851e02290 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 7 Nov 2022 22:34:02 +0100 Subject: [PATCH 43/90] Add versionchanged directives through transition script --- docs/substitutions/global.rst | 4 ++++ immutabilize.py | 36 +++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index fc1bb3dda12..5028bfc533c 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -21,3 +21,7 @@ .. |toapikwargsarg| replace:: Arbitrary keyword arguments. Can be used to store data for which there are no dedicated attributes. |toapikwargsbase| .. |toapikwargsattr| replace:: Optional. Arbitrary keyword arguments. Used to store data for which there are no dedicated attributes. |toapikwargsbase| + +.. |squenceclassargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list. The input is converted to a tuple. + +.. |tupleclassattrs| replace:: This attribute is now an immutable tuple. diff --git a/immutabilize.py b/immutabilize.py index 358a43f57c1..98933b47e89 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -44,17 +44,49 @@ # In the "Args:" block replace "List[" by "Sequence[" for i in range(args_start_line + 1, attributes_start_line): + whitespaces = -1 for param in params_to_change: + if f"{param} (List[" not in class_source_lines[i]: + continue + class_source_lines[i] = class_source_lines[i].replace( f"{param} (List[", f"{param} (Sequence[" ) + whitespaces = re.match(r" +", class_source_lines[i]).end() + 4 + j = i + 1 + while class_source_lines[j] and ( + re.match(r"\s+", class_source_lines[j]).end() >= whitespaces + ): + j = j + 1 + + class_source_lines[ + j - 1 + ] += f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '} |squenceclassargs|" + if class_source_lines[j]: + class_source_lines[j - 1] += "\n" - # In the "Attributes:" block replace "List[" by "Tuple[" + # In the "Attributes:" block replace "List[" by "Sequence[" for i in range(attributes_start_line + 1, class_length): + whitespaces = -1 for param in params_to_change: + if f"{param} (List[" not in class_source_lines[i]: + continue + class_source_lines[i] = class_source_lines[i].replace( - f"{param} (List[", f"{param} (Tuple[" + f"{param} (List[", f"{param} (Sequence[" ) + whitespaces = re.match(r" +", class_source_lines[i]).end() + 4 + j = i + 1 + while class_source_lines[j] and ( + re.match(r"\s+", class_source_lines[j]).end() >= whitespaces + ): + j = j + 1 + + class_source_lines[ + j - 1 + ] += f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '} |tupleclassattrs|" + if class_source_lines[j]: + class_source_lines[j - 1] += "\n" # Adjust type annotations in the __init__ and converts to tuples before assigning to # attributes From 7ca6b94f3ffb33ac8108d2cedfcd3dceff6567a2 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 7 Nov 2022 22:54:45 +0100 Subject: [PATCH 44/90] try handling files with multiple classes --- immutabilize.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/immutabilize.py b/immutabilize.py index 98933b47e89..2e049a3a833 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -1,13 +1,42 @@ # Loops through all TG classes and converts list-type attributes to tuples. +import importlib import inspect +import os import re +import types from pathlib import Path +from importlib import reload import telegram + +def reload_package(package): + assert hasattr(package, "__package__") + fn = package.__file__ + fn_dir = os.path.dirname(fn) + os.sep + module_visit = {fn} + del fn + + def reload_recursive_ex(module): + importlib.reload(module) + + for module_child in vars(module).values(): + if isinstance(module_child, types.ModuleType): + fn_child = getattr(module_child, "__file__", None) + if (fn_child is not None) and fn_child.startswith(fn_dir): + if fn_child not in module_visit: + # print("reloading:", fn_child, "from", module) + module_visit.add(fn_child) + reload_recursive_ex(module_child) + + return reload_recursive_ex(package) + + # loop through all classes in the `telegram` module classes = inspect.getmembers(telegram, inspect.isclass) -for name, cls in classes: +for name, _ in classes: + cls = getattr(telegram, name) + print("Processing class", name) # first adjust the __init__ of the class params = inspect.signature(cls.__init__).parameters @@ -112,6 +141,14 @@ : init_start_line - class_start_line - 1 ] + i = 0 + while file_contents[i].startswith("#"): + i += 1 + file_contents[i] += "\nfrom typing import Sequence" + file_contents = "\n".join(file_contents) + "\n" class_source_file.write_text(file_contents, encoding="utf-8") + + # so that the changes are reflected in the module + reload_package(telegram) From eca7129d47c5cd1f49899e3c9f189e46bc2756e0 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:10:18 +0100 Subject: [PATCH 45/90] Just rerun the script if a file contains multiple classes --- immutabilize.py | 73 ++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/immutabilize.py b/immutabilize.py index 2e049a3a833..f8957f9ac47 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -1,41 +1,29 @@ # Loops through all TG classes and converts list-type attributes to tuples. -import importlib import inspect -import os +import pickle import re -import types from pathlib import Path -from importlib import reload -import telegram - - -def reload_package(package): - assert hasattr(package, "__package__") - fn = package.__file__ - fn_dir = os.path.dirname(fn) + os.sep - module_visit = {fn} - del fn +import isort - def reload_recursive_ex(module): - importlib.reload(module) +import telegram - for module_child in vars(module).values(): - if isinstance(module_child, types.ModuleType): - fn_child = getattr(module_child, "__file__", None) - if (fn_child is not None) and fn_child.startswith(fn_dir): - if fn_child not in module_visit: - # print("reloading:", fn_child, "from", module) - module_visit.add(fn_child) - reload_recursive_ex(module_child) +changed_files = set() +processed_classes = set() - return reload_recursive_ex(package) +try: + processed_classes = pickle.load(open("processed_classes.pickle", "rb")) +except Exception: + print("Could not load pickle file") + processed_classes = set() +unprocessed_classes = set() # loop through all classes in the `telegram` module classes = inspect.getmembers(telegram, inspect.isclass) -for name, _ in classes: - cls = getattr(telegram, name) +for name, cls in classes: + if cls in processed_classes: + continue print("Processing class", name) # first adjust the __init__ of the class @@ -50,6 +38,12 @@ def reload_recursive_ex(module): continue class_source_file = Path(inspect.getfile(cls)) + if class_source_file not in changed_files: + changed_files.add(class_source_file) + processed_classes.add(cls) + else: + unprocessed_classes.add(cls) + continue _, class_start_line = inspect.getsourcelines(cls) class_source = inspect.getsource(cls) @@ -88,9 +82,10 @@ def reload_recursive_ex(module): ): j = j + 1 - class_source_lines[ - j - 1 - ] += f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '} |squenceclassargs|" + class_source_lines[j - 1] += ( + f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" + " |squenceclassargs| " + ) if class_source_lines[j]: class_source_lines[j - 1] += "\n" @@ -111,9 +106,10 @@ def reload_recursive_ex(module): ): j = j + 1 - class_source_lines[ - j - 1 - ] += f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '} |tupleclassattrs|" + class_source_lines[j - 1] += ( + f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" + " |tupleclassattrs|" + ) if class_source_lines[j]: class_source_lines[j - 1] += "\n" @@ -148,7 +144,16 @@ def reload_recursive_ex(module): file_contents = "\n".join(file_contents) + "\n" + # Sort imports + file_contents = isort.code(file_contents) + class_source_file.write_text(file_contents, encoding="utf-8") - # so that the changes are reflected in the module - reload_package(telegram) +if unprocessed_classes: + print( + "Rerun the script to finish the conversion. I can't handle files that contain multiple " + "classes. The following classes were not processed:" + f"{', '.join([cls.__name__ for cls in unprocessed_classes])}" + ) + +pickle.dump(processed_classes, open("processed_classes.pickle", "wb")) From b3ee612c28cac40afe0c98ce388a6600cd97d084 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:25:54 +0100 Subject: [PATCH 46/90] Convert Union annotations --- immutabilize.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/immutabilize.py b/immutabilize.py index f8957f9ac47..7dd7c19bcfa 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -117,6 +117,11 @@ # attributes for param in params_to_change: init_source = init_source.replace(param + ": List", param + ": Sequence") + init_source = re.sub( + rf"{param}: Union\[List\[(\w+)\], Tuple\[\w+, \.\.\.\]\]", + rf"{param}: Sequence[\1]", + init_source, + ) init_source = re.sub( rf"self\.{param} = ([^ ]*)\n", rf"self.{param} = tuple(\1)\n", init_source ) From 27941cde36510288a8cdd36ac1df9dba7dd2be5c Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:28:11 +0100 Subject: [PATCH 47/90] Improve handling of optionals in init --- immutabilize.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/immutabilize.py b/immutabilize.py index 7dd7c19bcfa..d5a48ab72dd 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -126,7 +126,9 @@ rf"self\.{param} = ([^ ]*)\n", rf"self.{param} = tuple(\1)\n", init_source ) init_source = re.sub( - rf"self\.{param} = (.*) or \[\]\n", rf"self.{param} = tuple(\1 or ())\n", init_source + rf"self\.{param} = (.*) or \[\]\n", + rf"self.{param} = tuple(\1) if \1 else ()\n", + init_source, ) file_contents = class_source_file.read_text(encoding="utf-8") From a3bee068e9df31f0966afce2cd8cbad49fa834a9 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:40:35 +0100 Subject: [PATCH 48/90] More script tweaking --- immutabilize.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/immutabilize.py b/immutabilize.py index d5a48ab72dd..9766c659c18 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -28,11 +28,12 @@ print("Processing class", name) # first adjust the __init__ of the class params = inspect.signature(cls.__init__).parameters - params_to_change = set() + params_to_change = dict() for param in params.values(): if "List" in str(param.annotation): print(" Converting list-type parameter", param.name, "to Sequence") - params_to_change.add(param.name) + params_to_change[param.name] = param.default + print(" ", param.name, "default value:", repr(param.default)) if not params_to_change: continue @@ -84,7 +85,7 @@ class_source_lines[j - 1] += ( f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" - " |squenceclassargs| " + " |squenceclassargs|" ) if class_source_lines[j]: class_source_lines[j - 1] += "\n" @@ -115,7 +116,7 @@ # Adjust type annotations in the __init__ and converts to tuples before assigning to # attributes - for param in params_to_change: + for param, default_value in params_to_change.items(): init_source = init_source.replace(param + ": List", param + ": Sequence") init_source = re.sub( rf"{param}: Union\[List\[(\w+)\], Tuple\[\w+, \.\.\.\]\]", @@ -123,8 +124,20 @@ init_source, ) init_source = re.sub( - rf"self\.{param} = ([^ ]*)\n", rf"self.{param} = tuple(\1)\n", init_source + rf"{param}: Union\[Tuple\[\w+, \.\.\.\], List\[(\w+)\]\]", + rf"{param}: Sequence[\1]", + init_source, ) + if default_value is None: + init_source = re.sub( + rf"self\.{param} = (\S*)\n", + rf"self.{param} = tuple(\1) if \1 else None\n", + init_source, + ) + else: + init_source = re.sub( + rf"self\.{param} = (\S*)\n", rf"self.{param} = tuple(\1)\n", init_source + ) init_source = re.sub( rf"self\.{param} = (.*) or \[\]\n", rf"self.{param} = tuple(\1) if \1 else ()\n", From 852e4ec0aa4830aafd8903fed921513b073d3cbf Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 8 Nov 2022 08:48:36 +0100 Subject: [PATCH 49/90] Apply transition script --- telegram/_files/inputmedia.py | 86 ++++++++++++++----- telegram/_files/sticker.py | 19 ++-- telegram/_games/game.py | 35 +++++--- telegram/_inline/inlinekeyboardmarkup.py | 17 ++-- telegram/_inline/inlinequeryresultaudio.py | 19 ++-- .../_inline/inlinequeryresultcachedaudio.py | 19 ++-- .../inlinequeryresultcacheddocument.py | 19 ++-- .../_inline/inlinequeryresultcachedgif.py | 19 ++-- .../inlinequeryresultcachedmpeg4gif.py | 19 ++-- .../_inline/inlinequeryresultcachedphoto.py | 19 ++-- .../_inline/inlinequeryresultcachedvideo.py | 19 ++-- .../_inline/inlinequeryresultcachedvoice.py | 19 ++-- telegram/_inline/inlinequeryresultdocument.py | 19 ++-- telegram/_inline/inlinequeryresultgif.py | 19 ++-- telegram/_inline/inlinequeryresultmpeg4gif.py | 19 ++-- telegram/_inline/inlinequeryresultphoto.py | 19 ++-- telegram/_inline/inlinequeryresultvideo.py | 19 ++-- telegram/_inline/inlinequeryresultvoice.py | 19 ++-- .../_inline/inputinvoicemessagecontent.py | 37 +++++--- telegram/_inline/inputtextmessagecontent.py | 19 ++-- telegram/_message.py | 82 +++++++++++++----- telegram/_passport/credentials.py | 21 +++-- .../_passport/encryptedpassportelement.py | 32 +++++-- telegram/_passport/passportdata.py | 19 ++-- telegram/_payment/shippingoption.py | 17 ++-- telegram/_poll.py | 48 ++++++++--- telegram/_userprofilephotos.py | 17 ++-- telegram/_videochat.py | 17 ++-- telegram/_webhookinfo.py | 19 ++-- 29 files changed, 528 insertions(+), 223 deletions(-) diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 209b1c828fb..1a6e149da45 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -17,7 +17,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/]. """Base class for Telegram InputMedia Objects.""" -from typing import List, Optional, Tuple, Union +from typing import Optional, Sequence, Union from telegram._files.animation import Animation from telegram._files.audio import Audio @@ -55,9 +55,13 @@ class InputMedia(TelegramObject): caption (:obj:`str`, optional): Caption of the media to be sent, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. @@ -67,8 +71,12 @@ class InputMedia(TelegramObject): media (:obj:`str` | :class:`telegram.InputFile`): Media to send. caption (:obj:`str`): Optional. Caption of the media to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. + + .. versionchanged:: 20.0 + |tupleclassattrs| + """ __slots__ = ("caption", "caption_entities", "media", "parse_mode", "type") @@ -78,7 +86,7 @@ def __init__( media_type: str, media: Union[str, InputFile, MediaType], caption: str = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, + caption_entities: Sequence[MessageEntity] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, *, api_kwargs: JSONDict = None, @@ -87,7 +95,7 @@ def __init__( self.type = media_type self.media = media self.caption = caption - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.parse_mode = parse_mode self._freeze() @@ -141,9 +149,13 @@ class InputMediaAnimation(InputMedia): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + width (:obj:`int`, optional): Animation width. height (:obj:`int`, optional): Animation height. duration (:obj:`int`, optional): Animation duration in seconds. @@ -153,8 +165,12 @@ class InputMediaAnimation(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Animation to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. + + .. versionchanged:: 20.0 + |tupleclassattrs| + thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. width (:obj:`int`): Optional. Animation width. height (:obj:`int`): Optional. Animation height. @@ -173,7 +189,7 @@ def __init__( width: int = None, height: int = None, duration: int = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, + caption_entities: Sequence[MessageEntity] = None, filename: str = None, *, api_kwargs: JSONDict = None, @@ -226,18 +242,24 @@ class InputMediaPhoto(InputMedia): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + .. versionchanged:: 20.0 + |squenceclassargs| + Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.PHOTO`. media (:obj:`str` | :class:`telegram.InputFile`): Photo to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. + .. versionchanged:: 20.0 + |tupleclassattrs| + """ __slots__ = () @@ -247,7 +269,7 @@ def __init__( media: Union[FileInput, PhotoSize], caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, + caption_entities: Sequence[MessageEntity] = None, filename: str = None, *, api_kwargs: JSONDict = None, @@ -296,9 +318,13 @@ class InputMediaVideo(InputMedia): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + width (:obj:`int`, optional): Video width. height (:obj:`int`, optional): Video height. duration (:obj:`int`, optional): Video duration in seconds. @@ -315,8 +341,12 @@ class InputMediaVideo(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Video file to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. + + .. versionchanged:: 20.0 + |tupleclassattrs| + width (:obj:`int`): Optional. Video width. height (:obj:`int`): Optional. Video height. duration (:obj:`int`): Optional. Video duration in seconds. @@ -338,7 +368,7 @@ def __init__( supports_streaming: bool = None, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb: FileInput = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, + caption_entities: Sequence[MessageEntity] = None, filename: str = None, *, api_kwargs: JSONDict = None, @@ -398,9 +428,13 @@ class InputMediaAudio(InputMedia): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + duration (:obj:`int`): Duration of the audio in seconds as defined by sender. performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio tags. @@ -416,8 +450,12 @@ class InputMediaAudio(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. + + .. versionchanged:: 20.0 + |tupleclassattrs| + duration (:obj:`int`): Duration of the audio in seconds. performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio tags. @@ -437,7 +475,7 @@ def __init__( duration: int = None, performer: str = None, title: str = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, + caption_entities: Sequence[MessageEntity] = None, filename: str = None, *, api_kwargs: JSONDict = None, @@ -490,9 +528,13 @@ class InputMediaDocument(InputMedia): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ optional): |thumbdocstringnopath| @@ -507,8 +549,12 @@ class InputMediaDocument(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): File to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. + + .. versionchanged:: 20.0 + |tupleclassattrs| + thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. disable_content_type_detection (:obj:`bool`): Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always true, if @@ -525,7 +571,7 @@ def __init__( caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, disable_content_type_detection: bool = None, - caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, + caption_entities: Sequence[MessageEntity] = None, filename: str = None, *, api_kwargs: JSONDict = None, diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 962c5f8452e..35bdee86a56 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -17,8 +17,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/]. """This module contains objects that represent stickers.""" - -from typing import TYPE_CHECKING, ClassVar, List, Optional +from typing import TYPE_CHECKING, ClassVar, Optional, Sequence from telegram import constants from telegram._files._basethumbedmedium import _BaseThumbedMedium @@ -209,7 +208,11 @@ class StickerSet(TelegramObject): is_video (:obj:`bool`): :obj:`True`, if the sticker set contains video stickers. .. versionadded:: 13.11 - stickers (List[:class:`telegram.Sticker`]): List of all set stickers. + stickers (Sequence[:class:`telegram.Sticker`]): List of all set stickers. + + .. versionchanged:: 20.0 + |squenceclassargs| + sticker_type (:obj:`str`): Type of stickers in the set, currently one of :attr:`telegram.Sticker.REGULAR`, :attr:`telegram.Sticker.MASK`, :attr:`telegram.Sticker.CUSTOM_EMOJI`. @@ -225,7 +228,11 @@ class StickerSet(TelegramObject): is_video (:obj:`bool`): :obj:`True`, if the sticker set contains video stickers. .. versionadded:: 13.11 - stickers (List[:class:`telegram.Sticker`]): List of all set stickers. + stickers (Sequence[:class:`telegram.Sticker`]): List of all set stickers. + + .. versionchanged:: 20.0 + |tupleclassattrs| + sticker_type (:obj:`str`): Type of stickers in the set. .. versionadded:: 20.0 @@ -249,7 +256,7 @@ def __init__( name: str, title: str, is_animated: bool, - stickers: List[Sticker], + stickers: Sequence[Sticker], is_video: bool, sticker_type: str, thumb: PhotoSize = None, @@ -261,7 +268,7 @@ def __init__( self.title = title self.is_animated = is_animated self.is_video = is_video - self.stickers = stickers + self.stickers = tuple(stickers) self.sticker_type = sticker_type # Optional self.thumb = thumb diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 9668d9342cb..955cb9a6939 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -17,9 +17,8 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Game.""" - import sys -from typing import TYPE_CHECKING, Dict, List, Optional +from typing import TYPE_CHECKING, Dict, List, Optional, Sequence from telegram._files.animation import Animation from telegram._files.photosize import PhotoSize @@ -42,30 +41,46 @@ class Game(TelegramObject): Args: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. - photo (List[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message + photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message in chats. + + .. versionchanged:: 20.0 + |squenceclassargs| + text (:obj:`str`, optional): Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited using :meth:`telegram.Bot.edit_message_text`. 0-:tg-const:`telegram.constants.MessageLimit.TEXT_LENGTH` characters. - text_entities (List[:class:`telegram.MessageEntity`], optional): Special entities that + text_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities that appear in text, such as usernames, URLs, bot commands, etc. + + .. versionchanged:: 20.0 + |squenceclassargs| + animation (:class:`telegram.Animation`, optional): Animation that will be displayed in the game message in chats. Upload via `BotFather `_. Attributes: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. - photo (List[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message + photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message in chats. + + .. versionchanged:: 20.0 + |tupleclassattrs| + text (:obj:`str`): Optional. Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited using :meth:`telegram.Bot.edit_message_text`. - text_entities (List[:class:`telegram.MessageEntity`]): Special entities that + text_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities that appear in text, such as usernames, URLs, bot commands, etc. This list is empty if the message does not contain text entities. + + .. versionchanged:: 20.0 + |tupleclassattrs| + animation (:class:`telegram.Animation`): Optional. Animation that will be displayed in the game message in chats. Upload via `BotFather `_. @@ -84,9 +99,9 @@ def __init__( self, title: str, description: str, - photo: List[PhotoSize], + photo: Sequence[PhotoSize], text: str = None, - text_entities: List[MessageEntity] = None, + text_entities: Sequence[MessageEntity] = None, animation: Animation = None, *, api_kwargs: JSONDict = None, @@ -95,10 +110,10 @@ def __init__( # Required self.title = title self.description = description - self.photo = photo + self.photo = tuple(photo) # Optionals self.text = text - self.text_entities = text_entities or [] + self.text_entities = tuple(text_entities) if text_entities else () self.animation = animation self._id_attrs = (self.title, self.description, self.photo) diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index bac08db812a..4fb987ab03a 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -17,8 +17,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/]. """This module contains an object that represents a Telegram InlineKeyboardMarkup.""" - -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, List, Optional, Sequence from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton from telegram._telegramobject import TelegramObject @@ -40,20 +39,26 @@ class InlineKeyboardMarkup(TelegramObject): `Inline Keyboard Example 2 `_ Args: - inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, + inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, each represented by a list of InlineKeyboardButton objects. + .. versionchanged:: 20.0 + |squenceclassargs| + Attributes: - inline_keyboard (List[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, + inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, each represented by a list of InlineKeyboardButton objects. + .. versionchanged:: 20.0 + |tupleclassattrs| + """ __slots__ = ("inline_keyboard",) def __init__( self, - inline_keyboard: List[List[InlineKeyboardButton]], + inline_keyboard: Sequence[List[InlineKeyboardButton]], *, api_kwargs: JSONDict = None, ): @@ -64,7 +69,7 @@ def __init__( "list of InlineKeyboardButtons" ) # Required - self.inline_keyboard = inline_keyboard + self.inline_keyboard = tuple(inline_keyboard) self._id_attrs = (self.inline_keyboard,) diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index eb0663aa824..302368c57d0 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultAudio.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -49,9 +48,13 @@ class InlineQueryResultAudio(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -70,9 +73,13 @@ class InlineQueryResultAudio(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -103,7 +110,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -119,7 +126,7 @@ def __init__( self.audio_duration = audio_duration self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 4a98752d2fa..9f7fed5b836 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -46,9 +45,13 @@ class InlineQueryResultCachedAudio(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -64,9 +67,13 @@ class InlineQueryResultCachedAudio(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -91,7 +98,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -103,7 +110,7 @@ def __init__( # Optionals self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index b1aebc6e163..5ab7b8c4ec9 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -48,9 +47,13 @@ class InlineQueryResultCachedDocument(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption.. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -68,9 +71,13 @@ class InlineQueryResultCachedDocument(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption.. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -99,7 +106,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -113,7 +120,7 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 53cce1c89e3..7e949730b4a 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultCachedGif.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -48,9 +47,13 @@ class InlineQueryResultCachedGif(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -67,9 +70,13 @@ class InlineQueryResultCachedGif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -96,7 +103,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -109,7 +116,7 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index fd4fca34289..ffd194c735d 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -48,9 +47,13 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -67,9 +70,13 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -96,7 +103,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -109,7 +116,7 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 917e5e2a233..ca28c74ffef 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -49,9 +48,13 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -69,9 +72,13 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -100,7 +107,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -114,7 +121,7 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 25dc7cf6603..da8077fa548 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -49,9 +48,13 @@ class InlineQueryResultCachedVideo(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -69,9 +72,13 @@ class InlineQueryResultCachedVideo(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -100,7 +107,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -114,7 +121,7 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 51c73ff8dbd..000c3f8e9d8 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -47,9 +46,13 @@ class InlineQueryResultCachedVoice(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -66,9 +69,13 @@ class InlineQueryResultCachedVoice(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -95,7 +102,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -108,7 +115,7 @@ def __init__( # Optionals self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index b1da49fb56c..c0941ddcbf1 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultDocument""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -47,9 +46,13 @@ class InlineQueryResultDocument(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + document_url (:obj:`str`): A valid URL for the file. mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" or "application/zip". @@ -72,9 +75,13 @@ class InlineQueryResultDocument(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + document_url (:obj:`str`): A valid URL for the file. mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" or "application/zip". @@ -118,7 +125,7 @@ def __init__( thumb_width: int = None, thumb_height: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -132,7 +139,7 @@ def __init__( # Optionals self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.description = description self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 45bfe823fec..04dad8ac075 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultGif.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -54,9 +53,13 @@ class InlineQueryResultGif(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -79,9 +82,13 @@ class InlineQueryResultGif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -118,7 +125,7 @@ def __init__( gif_duration: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -136,7 +143,7 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_mime_type = thumb_mime_type diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 1be1374d177..879ef4ebf42 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -54,9 +53,13 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -79,9 +82,13 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -118,7 +125,7 @@ def __init__( mpeg4_duration: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -136,7 +143,7 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_mime_type = thumb_mime_type diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index c31e885f385..5a02f13b246 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -52,9 +51,13 @@ class InlineQueryResultPhoto(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the @@ -76,9 +79,13 @@ class InlineQueryResultPhoto(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -113,7 +120,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -130,7 +137,7 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 05cf20afee8..767eb4ccf8c 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultVideo.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -54,9 +53,13 @@ class InlineQueryResultVideo(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + video_width (:obj:`int`, optional): Video width. video_height (:obj:`int`, optional): Video height. video_duration (:obj:`int`, optional): Video duration in seconds. @@ -81,9 +84,13 @@ class InlineQueryResultVideo(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + video_width (:obj:`int`): Optional. Video width. video_height (:obj:`int`): Optional. Video height. video_duration (:obj:`int`): Optional. Video duration in seconds. @@ -128,7 +135,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -144,7 +151,7 @@ def __init__( # Optional self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.video_width = video_width self.video_height = video_height self.video_duration = video_duration diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index d97e43c31c7..71c3305f8dd 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InlineQueryResultVoice.""" - -from typing import TYPE_CHECKING, List, Tuple, Union +from typing import TYPE_CHECKING, Sequence from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -48,9 +47,13 @@ class InlineQueryResultVoice(InlineQueryResult): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`], optional): List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + voice_duration (:obj:`int`, optional): Recording duration in seconds. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. @@ -68,9 +71,13 @@ class InlineQueryResultVoice(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + voice_duration (:obj:`int`): Optional. Recording duration in seconds. reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. @@ -100,7 +107,7 @@ def __init__( reply_markup: InlineKeyboardMarkup = None, input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + caption_entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -115,7 +122,7 @@ def __init__( self.voice_duration = voice_duration self.caption = caption self.parse_mode = parse_mode - self.caption_entities = caption_entities + self.caption_entities = tuple(caption_entities) if caption_entities else None self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index d2c789f47a2..b79b333ce52 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -17,8 +17,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/]. """This module contains a class that represents a Telegram InputInvoiceMessageContent.""" - -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional, Sequence from telegram._inline.inputmessagecontent import InputMessageContent from telegram._payment.labeledprice import LabeledPrice @@ -52,20 +51,28 @@ class InputInvoiceMessageContent(InputMessageContent): `@Botfather `_. currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on `currencies `_ - prices (List[:class:`telegram.LabeledPrice`]): Price breakdown, a list of + prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + + .. versionchanged:: 20.0 + |squenceclassargs| + max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the *smallest* units of the currency (integer, **not** float/double). For example, for a maximum tip of US$ 1.45 pass ``max_tip_amount = 145``. See the ``exp`` parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to ``0``. - suggested_tip_amounts (List[:obj:`int`], optional): An array of suggested + suggested_tip_amounts (Sequence[:obj:`int`], optional): An array of suggested amounts of tip in the *smallest* units of the currency (integer, **not** float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed :attr:`max_tip_amount`. + + .. versionchanged:: 20.0 + |squenceclassargs| + provider_data (:obj:`str`, optional): An object for data about the invoice, which will be shared with the payment provider. A detailed description of the required fields should be provided by the payment provider. @@ -104,12 +111,20 @@ class InputInvoiceMessageContent(InputMessageContent): `@Botfather `_. currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on `currencies `_ - prices (List[:class:`telegram.LabeledPrice`]): Price breakdown, a list of + prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components. + + .. versionchanged:: 20.0 + |tupleclassattrs| + max_tip_amount (:obj:`int`): Optional. The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). - suggested_tip_amounts (List[:obj:`int`]): Optional. An array of suggested + suggested_tip_amounts (Sequence[:obj:`int`]): Optional. An array of suggested amounts of tip in the smallest units of the currency (integer, not float/double). + + .. versionchanged:: 20.0 + |tupleclassattrs| + provider_data (:obj:`str`): Optional. An object for data about the invoice, which will be shared with the payment provider. photo_url (:obj:`str`): Optional. URL of the product photo for the invoice. @@ -163,9 +178,9 @@ def __init__( payload: str, provider_token: str, currency: str, - prices: List[LabeledPrice], + prices: Sequence[LabeledPrice], max_tip_amount: int = None, - suggested_tip_amounts: List[int] = None, + suggested_tip_amounts: Sequence[int] = None, provider_data: str = None, photo_url: str = None, photo_size: int = None, @@ -188,10 +203,12 @@ def __init__( self.payload = payload self.provider_token = provider_token self.currency = currency - self.prices = prices + self.prices = tuple(prices) # Optionals self.max_tip_amount = max_tip_amount - self.suggested_tip_amounts = suggested_tip_amounts + self.suggested_tip_amounts = ( + tuple(suggested_tip_amounts) if suggested_tip_amounts else None + ) self.provider_data = provider_data self.photo_url = photo_url self.photo_size = photo_size diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index cbb88faf64f..f67c1547093 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -17,8 +17,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/]. """This module contains the classes that represent Telegram InputTextMessageContent.""" - -from typing import List, Tuple, Union +from typing import Sequence from telegram._inline.inputmessagecontent import InputMessageContent from telegram._messageentity import MessageEntity @@ -42,9 +41,13 @@ class InputTextMessageContent(InputMessageContent): parse_mode (:obj:`str`, optional): Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - entities (List[:class:`telegram.MessageEntity`], optional): List of special + entities (Sequence[:class:`telegram.MessageEntity`], optional): List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |squenceclassargs| + disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the sent message. @@ -55,9 +58,13 @@ class InputTextMessageContent(InputMessageContent): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - entities (List[:class:`telegram.MessageEntity`]): Optional. List of special + entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in the sent message. @@ -70,7 +77,7 @@ def __init__( message_text: str, parse_mode: ODVInput[str] = DEFAULT_NONE, disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, + entities: Sequence[MessageEntity] = None, *, api_kwargs: JSONDict = None, ): @@ -79,7 +86,7 @@ def __init__( self.message_text = message_text # Optionals self.parse_mode = parse_mode - self.entities = entities + self.entities = tuple(entities) if entities else None self.disable_web_page_preview = disable_web_page_preview self._id_attrs = (self.message_text,) diff --git a/telegram/_message.py b/telegram/_message.py index ea97c56dea8..75d41dd042b 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -21,7 +21,7 @@ import datetime import sys from html import escape -from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, Union from telegram._chat import Chat from telegram._dice import Dice @@ -138,13 +138,21 @@ class Message(TelegramObject): text (:obj:`str`, optional): For text messages, the actual UTF-8 text of the message, 0-:tg-const:`telegram.constants.MessageLimit.TEXT_LENGTH` characters. - entities (List[:class:`telegram.MessageEntity`], optional): For text messages, special + entities (Sequence[:class:`telegram.MessageEntity`], optional): For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text. See :attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly. - caption_entities (List[:class:`telegram.MessageEntity`], optional): For messages with a + + .. versionchanged:: 20.0 + |squenceclassargs| + + caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` methods for how to use properly. + + .. versionchanged:: 20.0 + |squenceclassargs| + audio (:class:`telegram.Audio`, optional): Message is an audio file, information about the file. document (:class:`telegram.Document`, optional): Message is a general file, information @@ -153,8 +161,12 @@ class Message(TelegramObject): about the animation. For backward compatibility, when this field is set, the document field will also be set. game (:class:`telegram.Game`, optional): Message is a game, information about the game. - photo (List[:class:`telegram.PhotoSize`], optional): Message is a photo, available + photo (Sequence[:class:`telegram.PhotoSize`], optional): Message is a photo, available sizes of the photo. + + .. versionchanged:: 20.0 + |squenceclassargs| + sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information about the sticker. video (:class:`telegram.Video`, optional): Message is a video, information about the video. @@ -162,9 +174,13 @@ class Message(TelegramObject): the file. video_note (:class:`telegram.VideoNote`, optional): Message is a video note, information about the video message. - new_chat_members (List[:class:`telegram.User`], optional): New members that were added to + new_chat_members (Sequence[:class:`telegram.User`], optional): New members that were added to the group or supergroup and information about them (the bot itself may be one of these members). + + .. versionchanged:: 20.0 + |squenceclassargs| + caption (:obj:`str`, optional): Caption for the animation, audio, document, photo, video or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. contact (:class:`telegram.Contact`, optional): Message is a shared contact, information @@ -177,8 +193,12 @@ class Message(TelegramObject): left_chat_member (:class:`telegram.User`, optional): A member was removed from the group, information about them (this member may be the bot itself). new_chat_title (:obj:`str`, optional): A chat title was changed to this value. - new_chat_photo (List[:class:`telegram.PhotoSize`], optional): A chat photo was changed to + new_chat_photo (Sequence[:class:`telegram.PhotoSize`], optional): A chat photo was changed to this value. + + .. versionchanged:: 20.0 + |squenceclassargs| + delete_chat_photo (:obj:`bool`, optional): Service message: The chat photo was deleted. group_chat_created (:obj:`bool`, optional): Service message: The group has been created. supergroup_chat_created (:obj:`bool`, optional): Service message: The supergroup has been @@ -279,29 +299,45 @@ class Message(TelegramObject): media_group_id (:obj:`str`): Optional. The unique identifier of a media message group this message belongs to. text (:obj:`str`): Optional. The actual UTF-8 text of the message. - entities (List[:class:`telegram.MessageEntity`]): Special entities like + entities (Sequence[:class:`telegram.MessageEntity`]): Special entities like usernames, URLs, bot commands, etc. that appear in the text. See :attr:`Message.parse_entity` and :attr:`parse_entities` methods for how to use properly. This list is empty if the message does not contain entities. - caption_entities (List[:class:`telegram.MessageEntity`]): Special entities like + + .. versionchanged:: 20.0 + |tupleclassattrs| + + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities like usernames, URLs, bot commands, etc. that appear in the caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` methods for how to use properly. This list is empty if the message does not contain caption entities. + + .. versionchanged:: 20.0 + |tupleclassattrs| + audio (:class:`telegram.Audio`): Optional. Information about the file. document (:class:`telegram.Document`): Optional. Information about the file. animation (:class:`telegram.Animation`) Optional. Information about the file. For backward compatibility, when this field is set, the document field will also be set. game (:class:`telegram.Game`): Optional. Information about the game. - photo (List[:class:`telegram.PhotoSize`]): Available sizes of the photo. + photo (Sequence[:class:`telegram.PhotoSize`]): Available sizes of the photo. This list is empty if the message does not contain a photo. + + .. versionchanged:: 20.0 + |tupleclassattrs| + sticker (:class:`telegram.Sticker`): Optional. Information about the sticker. video (:class:`telegram.Video`): Optional. Information about the video. voice (:class:`telegram.Voice`): Optional. Information about the file. video_note (:class:`telegram.VideoNote`): Optional. Information about the video message. - new_chat_members (List[:class:`telegram.User`]): Information about new members to + new_chat_members (Sequence[:class:`telegram.User`]): Information about new members to the chat. The bot itself may be one of these members. This list is empty if the message does not contain new chat members. + + .. versionchanged:: 20.0 + |tupleclassattrs| + caption (:obj:`str`): Optional. Caption for the document, photo or video, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. @@ -311,8 +347,12 @@ class Message(TelegramObject): left_chat_member (:class:`telegram.User`): Optional. Information about the user that left the group. (this member may be the bot itself). new_chat_title (:obj:`str`): Optional. A chat title was changed to this value. - new_chat_photo (List[:class:`telegram.PhotoSize`]): A chat photo was changed to + new_chat_photo (Sequence[:class:`telegram.PhotoSize`]): A chat photo was changed to this value. This list is empty if the message does not contain a new chat photo. + + .. versionchanged:: 20.0 + |tupleclassattrs| + delete_chat_photo (:obj:`bool`): Optional. The chat photo was deleted. group_chat_created (:obj:`bool`): Optional. The group has been created. supergroup_chat_created (:obj:`bool`): Optional. The supergroup has been created. @@ -449,24 +489,24 @@ def __init__( reply_to_message: "Message" = None, edit_date: datetime.datetime = None, text: str = None, - entities: List["MessageEntity"] = None, - caption_entities: List["MessageEntity"] = None, + entities: Sequence["MessageEntity"] = None, + caption_entities: Sequence["MessageEntity"] = None, audio: Audio = None, document: Document = None, game: Game = None, - photo: List[PhotoSize] = None, + photo: Sequence[PhotoSize] = None, sticker: Sticker = None, video: Video = None, voice: Voice = None, video_note: VideoNote = None, - new_chat_members: List[User] = None, + new_chat_members: Sequence[User] = None, caption: str = None, contact: Contact = None, location: Location = None, venue: Venue = None, left_chat_member: User = None, new_chat_title: str = None, - new_chat_photo: List[PhotoSize] = None, + new_chat_photo: Sequence[PhotoSize] = None, delete_chat_photo: bool = None, group_chat_created: bool = None, supergroup_chat_created: bool = None, @@ -517,12 +557,12 @@ def __init__( self.edit_date = edit_date self.has_protected_content = has_protected_content self.text = text - self.entities = entities or [] - self.caption_entities = caption_entities or [] + self.entities = tuple(entities) if entities else () + self.caption_entities = tuple(caption_entities) if caption_entities else () self.audio = audio self.game = game self.document = document - self.photo = photo or [] + self.photo = tuple(photo) if photo else () self.sticker = sticker self.video = video self.voice = voice @@ -531,10 +571,10 @@ def __init__( self.contact = contact self.location = location self.venue = venue - self.new_chat_members = new_chat_members or [] + self.new_chat_members = tuple(new_chat_members) if new_chat_members else () self.left_chat_member = left_chat_member self.new_chat_title = new_chat_title - self.new_chat_photo = new_chat_photo or [] + self.new_chat_photo = tuple(new_chat_photo) if new_chat_photo else () self.delete_chat_photo = bool(delete_chat_photo) self.group_chat_created = bool(group_chat_created) self.supergroup_chat_created = bool(supergroup_chat_created) diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 64553117320..424cd378d6b 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -19,7 +19,7 @@ # pylint: disable=missing-module-docstring, redefined-builtin import json from base64 import b64decode -from typing import TYPE_CHECKING, List, Optional, no_type_check +from typing import TYPE_CHECKING, Optional, Sequence, no_type_check try: from cryptography.hazmat.backends import default_backend @@ -362,14 +362,21 @@ class SecureValue(TelegramObject): selfie (:class:`telegram.FileCredentials`, optional): Credentials for encrypted selfie of the user with a document. Can be available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (List[:class:`telegram.FileCredentials`], optional): Credentials for an + translation (Sequence[:class:`telegram.FileCredentials`], optional): Credentials for an encrypted translation of the document. Available for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration". - files (List[:class:`telegram.FileCredentials`], optional): Credentials for encrypted + + .. versionchanged:: 20.0 + |tupleclassattrs| + + files (Sequence[:class:`telegram.FileCredentials`], optional): Credentials for encrypted files. Available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. + .. versionchanged:: 20.0 + |tupleclassattrs| + """ __slots__ = ("data", "front_side", "reverse_side", "selfie", "files", "translation") @@ -380,8 +387,8 @@ def __init__( front_side: "FileCredentials" = None, reverse_side: "FileCredentials" = None, selfie: "FileCredentials" = None, - files: List["FileCredentials"] = None, - translation: List["FileCredentials"] = None, + files: Sequence["FileCredentials"] = None, + translation: Sequence["FileCredentials"] = None, *, api_kwargs: JSONDict = None, ): @@ -390,8 +397,8 @@ def __init__( self.front_side = front_side self.reverse_side = reverse_side self.selfie = selfie - self.files = files - self.translation = translation + self.files = tuple(files) if files else None + self.translation = tuple(translation) if translation else None self._freeze() diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index dbc760cb35b..f171addf3d4 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram EncryptedPassportElement.""" from base64 import b64decode -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional, Sequence from telegram._passport.credentials import decrypt_json from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress @@ -58,9 +58,13 @@ class EncryptedPassportElement(TelegramObject): "phone_number" type. email (:obj:`str`, optional): User's verified email address, available only for "email" type. - files (List[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted files + files (Sequence[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. + + .. versionchanged:: 20.0 + |squenceclassargs| + front_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the front side of the document, provided by the user. Available for "passport", "driver_license", "identity_card" and "internal_passport". @@ -70,12 +74,15 @@ class EncryptedPassportElement(TelegramObject): selfie (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the selfie of the user holding a document, provided by the user; available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (List[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted + translation (Sequence[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted files with translated versions of documents provided by the user. Available if requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. + .. versionchanged:: 20.0 + |squenceclassargs| + Attributes: type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", @@ -91,9 +98,13 @@ class EncryptedPassportElement(TelegramObject): "phone_number" type. email (:obj:`str`): Optional. User's verified email address, available only for "email" type. - files (List[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files + files (Sequence[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. + + .. versionchanged:: 20.0 + |tupleclassattrs| + front_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the front side of the document, provided by the user. Available for "passport", "driver_license", "identity_card" and "internal_passport". @@ -103,12 +114,15 @@ class EncryptedPassportElement(TelegramObject): selfie (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the selfie of the user holding a document, provided by the user; available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (List[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted + translation (Sequence[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files with translated versions of documents provided by the user. Available if requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. + .. versionchanged:: 20.0 + |tupleclassattrs| + """ __slots__ = ( @@ -131,11 +145,11 @@ def __init__( data: PersonalDetails = None, phone_number: str = None, email: str = None, - files: List[PassportFile] = None, + files: Sequence[PassportFile] = None, front_side: PassportFile = None, reverse_side: PassportFile = None, selfie: PassportFile = None, - translation: List[PassportFile] = None, + translation: Sequence[PassportFile] = None, credentials: "Credentials" = None, # pylint: disable=unused-argument *, api_kwargs: JSONDict = None, @@ -148,11 +162,11 @@ def __init__( self.data = data self.phone_number = phone_number self.email = email - self.files = files + self.files = tuple(files) if files else None self.front_side = front_side self.reverse_side = reverse_side self.selfie = selfie - self.translation = translation + self.translation = tuple(translation) if translation else None self.hash = hash self._id_attrs = ( diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index f01243ec953..00fa267415a 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -17,8 +17,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/]. """Contains information about Telegram Passport data shared with the bot by the user.""" - -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, List, Optional, Sequence from telegram._passport.credentials import EncryptedCredentials from telegram._passport.encryptedpassportelement import EncryptedPassportElement @@ -39,13 +38,21 @@ class PassportData(TelegramObject): attribute :attr:`telegram.Credentials.nonce`. Args: - data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information + data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. + + .. versionchanged:: 20.0 + |squenceclassargs| + credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. Attributes: - data (List[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information + data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. + + .. versionchanged:: 20.0 + |tupleclassattrs| + credentials (:class:`telegram.EncryptedCredentials`): Encrypted credentials. @@ -55,14 +62,14 @@ class PassportData(TelegramObject): def __init__( self, - data: List[EncryptedPassportElement], + data: Sequence[EncryptedPassportElement], credentials: EncryptedCredentials, *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) - self.data = data + self.data = tuple(data) self.credentials = credentials self._decrypted_data: Optional[List[EncryptedPassportElement]] = None diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index 83851ba48e9..f10a47c4750 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -17,8 +17,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/]. """This module contains an object that represents a Telegram ShippingOption.""" - -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING, Sequence from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict @@ -38,12 +37,18 @@ class ShippingOption(TelegramObject): Args: id (:obj:`str`): Shipping option identifier. title (:obj:`str`): Option title. - prices (List[:class:`telegram.LabeledPrice`]): List of price portions. + prices (Sequence[:class:`telegram.LabeledPrice`]): List of price portions. + + .. versionchanged:: 20.0 + |squenceclassargs| Attributes: id (:obj:`str`): Shipping option identifier. title (:obj:`str`): Option title. - prices (List[:class:`telegram.LabeledPrice`]): List of price portions. + prices (Sequence[:class:`telegram.LabeledPrice`]): List of price portions. + + .. versionchanged:: 20.0 + |tupleclassattrs| """ @@ -53,7 +58,7 @@ def __init__( self, id: str, # pylint: disable=redefined-builtin title: str, - prices: List["LabeledPrice"], + prices: Sequence["LabeledPrice"], *, api_kwargs: JSONDict = None, ): @@ -61,7 +66,7 @@ def __init__( self.id = id # pylint: disable=invalid-name self.title = title - self.prices = prices + self.prices = tuple(prices) self._id_attrs = (self.id,) diff --git a/telegram/_poll.py b/telegram/_poll.py index 214d7b6715c..27cb1f77706 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -17,10 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Poll.""" - import datetime import sys -from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional +from typing import TYPE_CHECKING, ClassVar, Dict, List, Optional, Sequence from telegram import constants from telegram._messageentity import MessageEntity @@ -76,25 +75,31 @@ class PollAnswer(TelegramObject): Args: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (List[:obj:`int`]): 0-based identifiers of answer options, chosen by the user. + option_ids (Sequence[:obj:`int`]): 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote. + .. versionchanged:: 20.0 + |squenceclassargs| + Attributes: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (List[:obj:`int`]): Identifiers of answer options, chosen by the user. + option_ids (Sequence[:obj:`int`]): Identifiers of answer options, chosen by the user. + + .. versionchanged:: 20.0 + |tupleclassattrs| """ __slots__ = ("option_ids", "user", "poll_id") def __init__( - self, poll_id: str, user: User, option_ids: List[int], *, api_kwargs: JSONDict = None + self, poll_id: str, user: User, option_ids: Sequence[int], *, api_kwargs: JSONDict = None ): super().__init__(api_kwargs=api_kwargs) self.poll_id = poll_id self.user = user - self.option_ids = option_ids + self.option_ids = tuple(option_ids) self._id_attrs = (self.poll_id, self.user, tuple(self.option_ids)) @@ -125,7 +130,11 @@ class Poll(TelegramObject): Args: id (:obj:`str`): Unique poll identifier. question (:obj:`str`): Poll question, 1-300 characters. - options (List[:class:`PollOption`]): List of poll options. + options (Sequence[:class:`PollOption`]): List of poll options. + + .. versionchanged:: 20.0 + |squenceclassargs| + is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. @@ -135,8 +144,12 @@ class Poll(TelegramObject): forwarded) by the bot or to the private chat with the bot. explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters. - explanation_entities (List[:class:`telegram.MessageEntity`], optional): Special entities + explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. + + .. versionchanged:: 20.0 + |squenceclassargs| + open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active after creation. close_date (:obj:`datetime.datetime`, optional): Point in time (Unix timestamp) when the @@ -145,7 +158,11 @@ class Poll(TelegramObject): Attributes: id (:obj:`str`): Unique poll identifier. question (:obj:`str`): Poll question, 1-300 characters. - options (List[:class:`PollOption`]): List of poll options. + options (Sequence[:class:`PollOption`]): List of poll options. + + .. versionchanged:: 20.0 + |tupleclassattrs| + total_voter_count (:obj:`int`): Total number of users that voted in the poll. is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. @@ -154,10 +171,13 @@ class Poll(TelegramObject): correct_option_id (:obj:`int`): Optional. Identifier of the correct answer option. explanation (:obj:`str`): Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll. - explanation_entities (List[:class:`telegram.MessageEntity`]): Special entities + explanation_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. This list is empty if the message does not contain explanation entities. + .. versionchanged:: 20.0 + |tupleclassattrs| + .. versionchanged:: 20.0 This attribute is now always a (possibly empty) list and never :obj:`None`. open_period (:obj:`int`): Optional. Amount of time in seconds the poll will be active @@ -187,7 +207,7 @@ def __init__( self, id: str, # pylint: disable=redefined-builtin question: str, - options: List[PollOption], + options: Sequence[PollOption], total_voter_count: int, is_closed: bool, is_anonymous: bool, @@ -195,7 +215,7 @@ def __init__( allows_multiple_answers: bool, correct_option_id: int = None, explanation: str = None, - explanation_entities: List[MessageEntity] = None, + explanation_entities: Sequence[MessageEntity] = None, open_period: int = None, close_date: datetime.datetime = None, *, @@ -204,7 +224,7 @@ def __init__( super().__init__(api_kwargs=api_kwargs) self.id = id # pylint: disable=invalid-name self.question = question - self.options = options + self.options = tuple(options) self.total_voter_count = total_voter_count self.is_closed = is_closed self.is_anonymous = is_anonymous @@ -212,7 +232,7 @@ def __init__( self.allows_multiple_answers = allows_multiple_answers self.correct_option_id = correct_option_id self.explanation = explanation - self.explanation_entities = explanation_entities or [] + self.explanation_entities = tuple(explanation_entities) if explanation_entities else () self.open_period = open_period self.close_date = close_date diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 98b8bc1ac95..e56a449b5d8 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -17,8 +17,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/]. """This module contains an object that represents a Telegram UserProfilePhotos.""" - -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, List, Optional, Sequence from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject @@ -36,24 +35,30 @@ class UserProfilePhotos(TelegramObject): Args: total_count (:obj:`int`): Total number of profile pictures the target user has. - photos (List[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 + photos (Sequence[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 sizes each). + .. versionchanged:: 20.0 + |squenceclassargs| + Attributes: total_count (:obj:`int`): Total number of profile pictures. - photos (List[List[:class:`telegram.PhotoSize`]]): Requested profile pictures. + photos (Sequence[List[:class:`telegram.PhotoSize`]]): Requested profile pictures. + + .. versionchanged:: 20.0 + |tupleclassattrs| """ __slots__ = ("photos", "total_count") def __init__( - self, total_count: int, photos: List[List[PhotoSize]], *, api_kwargs: JSONDict = None + self, total_count: int, photos: Sequence[List[PhotoSize]], *, api_kwargs: JSONDict = None ): super().__init__(api_kwargs=api_kwargs) # Required self.total_count = total_count - self.photos = photos + self.photos = tuple(photos) self._id_attrs = (self.total_count, self.photos) diff --git a/telegram/_videochat.py b/telegram/_videochat.py index f5e3fe89d67..9984d06c743 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -17,9 +17,8 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects related to Telegram video chats.""" - import datetime as dtm -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional, Sequence from telegram._telegramobject import TelegramObject from telegram._user import User @@ -91,10 +90,16 @@ class VideoChatParticipantsInvited(TelegramObject): This class was renamed from ``VoiceChatParticipantsInvited`` in accordance to Bot API 6.0. Args: - users (List[:class:`telegram.User`]): New members that were invited to the video chat. + users (Sequence[:class:`telegram.User`]): New members that were invited to the video chat. + + .. versionchanged:: 20.0 + |squenceclassargs| Attributes: - users (List[:class:`telegram.User`]): New members that were invited to the video chat. + users (Sequence[:class:`telegram.User`]): New members that were invited to the video chat. + + .. versionchanged:: 20.0 + |tupleclassattrs| """ @@ -102,12 +107,12 @@ class VideoChatParticipantsInvited(TelegramObject): def __init__( self, - users: List[User], + users: Sequence[User], *, api_kwargs: JSONDict = None, ) -> None: super().__init__(api_kwargs=api_kwargs) - self.users = users + self.users = tuple(users) self._id_attrs = (self.users,) self._freeze() diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 47971517d7e..640c2a58689 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -17,8 +17,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/]. """This module contains an object that represents a Telegram WebhookInfo.""" - -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, Optional, Sequence from telegram._telegramobject import TelegramObject from telegram._utils.datetime import from_timestamp @@ -55,8 +54,12 @@ class WebhookInfo(TelegramObject): most recent error that happened when trying to deliver an update via webhook. max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery. - allowed_updates (List[:obj:`str`], optional): A list of update types the bot is subscribed + allowed_updates (Sequence[:obj:`str`], optional): A list of update types the bot is subscribed to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. + + .. versionchanged:: 20.0 + |squenceclassargs| + last_synchronization_error_date (:obj:`int`, optional): Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters. @@ -70,8 +73,12 @@ class WebhookInfo(TelegramObject): last_error_message (:obj:`str`): Optional. Error message in human-readable format. max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS connections. - allowed_updates (List[:obj:`str`]): Optional. A list of update types the bot is subscribed + allowed_updates (Sequence[:obj:`str`]): Optional. A list of update types the bot is subscribed to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. + + .. versionchanged:: 20.0 + |tupleclassattrs| + last_synchronization_error_date (:obj:`int`): Optional. Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters. @@ -98,7 +105,7 @@ def __init__( last_error_date: int = None, last_error_message: str = None, max_connections: int = None, - allowed_updates: List[str] = None, + allowed_updates: Sequence[str] = None, ip_address: str = None, last_synchronization_error_date: int = None, *, @@ -115,7 +122,7 @@ def __init__( self.last_error_date = last_error_date self.last_error_message = last_error_message self.max_connections = max_connections - self.allowed_updates = allowed_updates + self.allowed_updates = tuple(allowed_updates) if allowed_updates else None self.last_synchronization_error_date = last_synchronization_error_date self._id_attrs = ( From a7cb5475bb2d8bdc715e25c293ebf3904c71e1b1 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 9 Nov 2022 22:35:45 +0100 Subject: [PATCH 50/90] make pre-commit happier --- telegram/_bot.py | 6 +++++- telegram/_games/game.py | 8 ++++---- telegram/_inline/inlinekeyboardmarkup.py | 8 ++++---- telegram/_message.py | 10 +++++----- telegram/_passport/encryptedpassportelement.py | 12 ++++++++---- telegram/_passport/passportdata.py | 10 ++++++---- telegram/_poll.py | 9 +++++---- telegram/_userprofilephotos.py | 4 ++-- telegram/_webhookinfo.py | 10 ++++++---- 9 files changed, 45 insertions(+), 32 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 05c6dececff..27298b6bcfe 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -2092,10 +2092,14 @@ async def send_media_group( # Copy first item (to avoid mutation of original object), apply group caption to it. # This will lead to the group being shown with this caption. item_to_get_caption = copy.copy(media[0]) + item_to_get_caption._unfreeze() # pylint: disable=protected-access item_to_get_caption.caption = caption if parse_mode is not DEFAULT_NONE: item_to_get_caption.parse_mode = parse_mode - item_to_get_caption.caption_entities = caption_entities + item_to_get_caption.caption_entities = ( + tuple(caption_entities) if caption_entities else None + ) + item_to_get_caption._freeze() # pylint: disable=protected-access # copy the list (just the references) to avoid mutating the original list media = media[:] diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 955cb9a6939..3748389b02b 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -41,8 +41,8 @@ class Game(TelegramObject): Args: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. - photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message - in chats. + photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game + message in chats. .. versionchanged:: 20.0 |squenceclassargs| @@ -64,8 +64,8 @@ class Game(TelegramObject): Attributes: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. - photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message - in chats. + photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game + message in chats. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 4fb987ab03a..6f45f34c2ad 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -39,15 +39,15 @@ class InlineKeyboardMarkup(TelegramObject): `Inline Keyboard Example 2 `_ Args: - inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, - each represented by a list of InlineKeyboardButton objects. + inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button + rows, each represented by a list of InlineKeyboardButton objects. .. versionchanged:: 20.0 |squenceclassargs| Attributes: - inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button rows, - each represented by a list of InlineKeyboardButton objects. + inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button + rows, each represented by a list of InlineKeyboardButton objects. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_message.py b/telegram/_message.py index 75d41dd042b..5ad029475a4 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -174,9 +174,9 @@ class Message(TelegramObject): the file. video_note (:class:`telegram.VideoNote`, optional): Message is a video note, information about the video message. - new_chat_members (Sequence[:class:`telegram.User`], optional): New members that were added to - the group or supergroup and information about them (the bot itself may be one of these - members). + new_chat_members (Sequence[:class:`telegram.User`], optional): New members that were added + to the group or supergroup and information about them (the bot itself may be one of + these members). .. versionchanged:: 20.0 |squenceclassargs| @@ -193,8 +193,8 @@ class Message(TelegramObject): left_chat_member (:class:`telegram.User`, optional): A member was removed from the group, information about them (this member may be the bot itself). new_chat_title (:obj:`str`, optional): A chat title was changed to this value. - new_chat_photo (Sequence[:class:`telegram.PhotoSize`], optional): A chat photo was changed to - this value. + new_chat_photo (Sequence[:class:`telegram.PhotoSize`], optional): A chat photo was changed + to this value. .. versionchanged:: 20.0 |squenceclassargs| diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index f171addf3d4..146bb2d3b35 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -58,7 +58,8 @@ class EncryptedPassportElement(TelegramObject): "phone_number" type. email (:obj:`str`, optional): User's verified email address, available only for "email" type. - files (Sequence[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted files + files (Sequence[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted + files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. @@ -74,7 +75,8 @@ class EncryptedPassportElement(TelegramObject): selfie (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the selfie of the user holding a document, provided by the user; available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (Sequence[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted + translation (Sequence[:class:`telegram.PassportFile`], optional): Array of + encrypted/decrypted files with translated versions of documents provided by the user. Available if requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and @@ -98,7 +100,8 @@ class EncryptedPassportElement(TelegramObject): "phone_number" type. email (:obj:`str`): Optional. User's verified email address, available only for "email" type. - files (Sequence[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files + files (Sequence[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted + files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. @@ -114,7 +117,8 @@ class EncryptedPassportElement(TelegramObject): selfie (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the selfie of the user holding a document, provided by the user; available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (Sequence[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted + translation (Sequence[:class:`telegram.PassportFile`]): Optional. Array of + encrypted/decrypted files with translated versions of documents provided by the user. Available if requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 00fa267415a..80589cd546b 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -38,8 +38,9 @@ class PassportData(TelegramObject): attribute :attr:`telegram.Credentials.nonce`. Args: - data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information - about documents and other Telegram Passport elements that was shared with the bot. + data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted + information about documents and other Telegram Passport elements that was shared with + the bot. .. versionchanged:: 20.0 |squenceclassargs| @@ -47,8 +48,9 @@ class PassportData(TelegramObject): credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. Attributes: - data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information - about documents and other Telegram Passport elements that was shared with the bot. + data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted + information about documents and other Telegram Passport elements that was shared with + the bot. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_poll.py b/telegram/_poll.py index 27cb1f77706..c6efd1c26ee 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -75,8 +75,8 @@ class PollAnswer(TelegramObject): Args: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (Sequence[:obj:`int`]): 0-based identifiers of answer options, chosen by the user. - May be empty if the user retracted their vote. + option_ids (Sequence[:obj:`int`]): 0-based identifiers of answer options, chosen by the + user. May be empty if the user retracted their vote. .. versionchanged:: 20.0 |squenceclassargs| @@ -144,8 +144,9 @@ class Poll(TelegramObject): forwarded) by the bot or to the private chat with the bot. explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters. - explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special entities - like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. + explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special + entities like usernames, URLs, bot commands, etc. that appear in the + :attr:`explanation`. .. versionchanged:: 20.0 |squenceclassargs| diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index e56a449b5d8..ad8bd02b982 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -35,8 +35,8 @@ class UserProfilePhotos(TelegramObject): Args: total_count (:obj:`int`): Total number of profile pictures the target user has. - photos (Sequence[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 - sizes each). + photos (Sequence[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to + 4 sizes each). .. versionchanged:: 20.0 |squenceclassargs| diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 640c2a58689..53753d611cf 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -54,8 +54,9 @@ class WebhookInfo(TelegramObject): most recent error that happened when trying to deliver an update via webhook. max_connections (:obj:`int`, optional): Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery. - allowed_updates (Sequence[:obj:`str`], optional): A list of update types the bot is subscribed - to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. + allowed_updates (Sequence[:obj:`str`], optional): A list of update types the bot is + subscribed to. Defaults to all update types, except + :attr:`telegram.Update.chat_member`. .. versionchanged:: 20.0 |squenceclassargs| @@ -73,8 +74,9 @@ class WebhookInfo(TelegramObject): last_error_message (:obj:`str`): Optional. Error message in human-readable format. max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS connections. - allowed_updates (Sequence[:obj:`str`]): Optional. A list of update types the bot is subscribed - to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. + allowed_updates (Sequence[:obj:`str`]): Optional. A list of update types the bot is + subscribed to. Defaults to all update types, except + :attr:`telegram.Update.chat_member`. .. versionchanged:: 20.0 |tupleclassattrs| From a7ff9211c412eeabbe4bc71ae20d59aa10483041 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:46:27 +0100 Subject: [PATCH 51/90] Get a number of tests working --- telegram/_bot.py | 6 +++++ telegram/ext/_callbackdatacache.py | 2 ++ tests/test_animation.py | 2 +- tests/test_application.py | 4 +-- tests/test_audio.py | 2 +- tests/test_bot.py | 16 ++++++----- tests/test_callbackdatacache.py | 1 + tests/test_callbackquery.py | 1 + tests/test_callbackqueryhandler.py | 5 +++- tests/test_chatjoinrequesthandler.py | 1 + tests/test_chatmemberhandler.py | 4 ++- tests/test_chatmemberupdated.py | 2 +- tests/test_choseninlineresulthandler.py | 5 +++- tests/test_commandhandler.py | 2 +- tests/test_conversationhandler.py | 27 +++++++++++++++++++ tests/test_document.py | 2 +- tests/test_filters.py | 1 + tests/test_inlinequeryhandler.py | 5 +++- tests/test_inlinequeryresultaudio.py | 2 +- tests/test_inlinequeryresultcachedaudio.py | 2 +- tests/test_inlinequeryresultcacheddocument.py | 2 +- tests/test_inlinequeryresultcachedgif.py | 2 +- tests/test_inlinequeryresultcachedmpeg4gif.py | 4 ++- tests/test_inlinequeryresultcachedphoto.py | 2 +- tests/test_inlinequeryresultcachedvideo.py | 2 +- tests/test_inlinequeryresultcachedvoice.py | 2 +- tests/test_inlinequeryresultdocument.py | 2 +- tests/test_inlinequeryresultgif.py | 2 +- tests/test_inlinequeryresultmpeg4gif.py | 2 +- tests/test_inlinequeryresultphoto.py | 2 +- tests/test_inlinequeryresultvideo.py | 2 +- tests/test_inlinequeryresultvoice.py | 2 +- tests/test_inputmedia.py | 18 ++++++------- tests/test_inputtextmessagecontent.py | 2 +- tests/test_message.py | 1 + tests/test_messagehandler.py | 2 ++ tests/test_photo.py | 2 +- tests/test_poll.py | 12 ++++++--- tests/test_sticker.py | 2 +- tests/test_user.py | 1 + tests/test_video.py | 2 +- tests/test_voice.py | 2 +- 42 files changed, 112 insertions(+), 50 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 27298b6bcfe..b76797c54f3 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -2934,6 +2934,8 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if not copied: res = copy.copy(res) copied = True + + res._unfreeze() # pylint: disable=protected-access res.input_message_content = copy.copy(res.input_message_content) res.input_message_content._unfreeze() # pylint: disable=protected-access res.input_message_content.parse_mode = DefaultValue.get_value( @@ -2943,6 +2945,8 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if hasattr(res.input_message_content, "disable_web_page_preview"): if not copied: res = copy.copy(res) + + res._unfreeze() # pylint: disable=protected-access res.input_message_content = copy.copy(res.input_message_content) res.input_message_content._unfreeze() # pylint: disable=protected-access res.input_message_content.disable_web_page_preview = DefaultValue.get_value( @@ -2950,6 +2954,8 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ ) res.input_message_content._freeze() # pylint: disable=protected-access + res._freeze() # pylint: disable=protected-access + return res @_log diff --git a/telegram/ext/_callbackdatacache.py b/telegram/ext/_callbackdatacache.py index 6fb618fd151..5d63689db12 100644 --- a/telegram/ext/_callbackdatacache.py +++ b/telegram/ext/_callbackdatacache.py @@ -371,7 +371,9 @@ def process_callback_query(self, callback_query: CallbackQuery) -> None: # Get the cached callback data for the CallbackQuery keyboard_uuid, button_data = self.__get_keyboard_uuid_and_button_data(data) + callback_query._unfreeze() # pylint: disable=protected-access callback_query.data = button_data # type: ignore[assignment] + callback_query._freeze() # pylint: disable=protected-access # Map the callback queries ID to the keyboards UUID for later use if not mapped and not isinstance(button_data, InvalidCallbackData): diff --git a/tests/test_animation.py b/tests/test_animation.py index eda6a2fbc50..6c7af6e2cbd 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -166,7 +166,7 @@ async def test_send_animation_caption_entities(self, bot, chat_id, animation): ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) diff --git a/tests/test_application.py b/tests/test_application.py index eb5b741b116..4c781d0cf2d 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -470,7 +470,7 @@ def two(update, context): u = make_message_update(message="test") await app.process_update(u) self.received = None - u.message.text = "something" + u = make_message_update(message="something") await app.process_update(u) def test_add_handler_errors(self, app): @@ -540,7 +540,7 @@ async def test_add_handlers(self, app): app.add_handler(msg_handler_set_count, 1) app.add_handlers((msg_handler_inc_count, msg_handler_inc_count), 1) - photo_update = make_message_update(message=Message(2, None, None, photo=True)) + photo_update = make_message_update(message=Message(2, None, None, photo=(True,))) async with app: await app.start() diff --git a/tests/test_audio.py b/tests/test_audio.py index e8bd39fceea..cf53661a4ff 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -192,7 +192,7 @@ async def test_send_audio_caption_entities(self, bot, chat_id, audio): ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) diff --git a/tests/test_bot.py b/tests/test_bot.py index 0189763799a..efce8469d26 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -95,13 +95,14 @@ async def message(bot, chat_id): to_reply_to = await bot.send_message( chat_id, "Text", disable_web_page_preview=True, disable_notification=True ) - return await bot.send_message( + out = await bot.send_message( chat_id, "Text", reply_to_message_id=to_reply_to.message_id, disable_web_page_preview=True, disable_notification=True, ) + out._unfreeze() @pytest.fixture(scope="class") @@ -749,7 +750,7 @@ async def test_send_and_stop_poll(self, bot, super_group_id, reply_markup): assert message_quiz.poll.type == Poll.QUIZ assert message_quiz.poll.is_closed assert message_quiz.poll.explanation == "Here is a link" - assert message_quiz.poll.explanation_entities == explanation_entities + assert message_quiz.poll.explanation_entities == tuple(explanation_entities) @flaky(3, 1) @pytest.mark.parametrize( @@ -837,7 +838,7 @@ async def test_send_poll_explanation_entities(self, bot, chat_id): ) assert message.poll.explanation == test_string - assert message.poll.explanation_entities == entities + assert message.poll.explanation_entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) @@ -1548,7 +1549,7 @@ async def test_edit_message_text_entities(self, bot, message): ) assert message.text == test_string - assert message.entities == entities + assert message.entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) @@ -1621,7 +1622,7 @@ async def test_edit_message_caption_entities(self, bot, media_message): ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) # edit_message_media is tested in test_inputmedia @@ -1764,7 +1765,7 @@ async def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot, use_ip live_info = await bot.get_webhook_info() assert live_info.url == url assert live_info.max_connections == max_connections - assert live_info.allowed_updates == allowed_updates + assert live_info.allowed_updates == tuple(allowed_updates) assert live_info.ip_address == ip assert live_info.has_custom_certificate == use_ip @@ -2492,7 +2493,7 @@ async def test_send_message_entities(self, bot, chat_id): ] message = await bot.send_message(chat_id=chat_id, text=test_string, entities=entities) assert message.text == test_string - assert message.entities == entities + assert message.entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) @@ -3000,6 +3001,7 @@ async def test_arbitrary_callback_data_pinned_message_reply_to_message( message = Message( 1, None, None, reply_markup=bot.callback_data_cache.process_keyboard(reply_markup) ) + message._unfreeze() # We do to_dict -> de_json to make sure those aren't the same objects message.pinned_message = Message.de_json(message.to_dict(), bot) diff --git a/tests/test_callbackdatacache.py b/tests/test_callbackdatacache.py index 8cb3abc6a1d..3fb8c0455a1 100644 --- a/tests/test_callbackdatacache.py +++ b/tests/test_callbackdatacache.py @@ -185,6 +185,7 @@ def test_process_callback_query(self, callback_data_cache, data, message, invali chat = Chat(1, "private") effective_message = Message(message_id=1, date=datetime.now(), chat=chat, reply_markup=out) + effective_message._unfreeze() effective_message.reply_to_message = deepcopy(effective_message) effective_message.pinned_message = deepcopy(effective_message) cq_id = uuid4().hex diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index b2a06f7d026..bd376d593e6 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -33,6 +33,7 @@ def callback_query(bot, request): game_short_name=TestCallbackQuery.game_short_name, ) cbq.set_bot(bot) + cbq._unfreeze() if request.param == "message": cbq.message = TestCallbackQuery.message cbq.message.set_bot(bot) diff --git a/tests/test_callbackqueryhandler.py b/tests/test_callbackqueryhandler.py index 200c88ae714..b51aac66a93 100644 --- a/tests/test_callbackqueryhandler.py +++ b/tests/test_callbackqueryhandler.py @@ -66,7 +66,10 @@ def false_update(request): @pytest.fixture(scope="function") def callback_query(bot): - return Update(0, callback_query=CallbackQuery(2, User(1, "", False), None, data="test data")) + update = Update(0, callback_query=CallbackQuery(2, User(1, "", False), None, data="test data")) + update._unfreeze() + update.callback_query._unfreeze() + return update class TestCallbackQueryHandler: diff --git a/tests/test_chatjoinrequesthandler.py b/tests/test_chatjoinrequesthandler.py index 4a2fd9da4d2..14cf25f9482 100644 --- a/tests/test_chatjoinrequesthandler.py +++ b/tests/test_chatjoinrequesthandler.py @@ -162,6 +162,7 @@ def test_with_username(self, chat_join_request_update): handler = ChatJoinRequestHandler(self.callback, username=["@user_b"]) assert not handler.check_update(chat_join_request_update) + chat_join_request_update.chat_join_request.from_user._unfreeze() chat_join_request_update.chat_join_request.from_user.username = None assert not handler.check_update(chat_join_request_update) diff --git a/tests/test_chatmemberhandler.py b/tests/test_chatmemberhandler.py index 1b8b3e5819d..dd78bcae1cb 100644 --- a/tests/test_chatmemberhandler.py +++ b/tests/test_chatmemberhandler.py @@ -82,7 +82,9 @@ def chat_member_updated(): @pytest.fixture(scope="function") def chat_member(bot, chat_member_updated): - return Update(0, my_chat_member=chat_member_updated) + update = Update(0, my_chat_member=chat_member_updated) + update._unfreeze() + return update class TestChatMemberHandler: diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index ae28aa9ce10..f25be0f781a 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -216,7 +216,7 @@ def test_difference_required(self, user, chat): # We deliberately change an optional argument here to make sure that comparison doesn't # just happens by id/required args new_user = User(1, "First name", False, last_name="last name") - new_chat_member.user = new_user + new_chat_member = ChatMember(new_user, "new_status") assert chat_member_updated.difference() == { "status": ("old_status", "new_status"), "user": (user, new_user), diff --git a/tests/test_choseninlineresulthandler.py b/tests/test_choseninlineresulthandler.py index be384a00e9e..746b82d71f9 100644 --- a/tests/test_choseninlineresulthandler.py +++ b/tests/test_choseninlineresulthandler.py @@ -68,10 +68,13 @@ def false_update(request): @pytest.fixture(scope="class") def chosen_inline_result(): - return Update( + out = Update( 1, chosen_inline_result=ChosenInlineResult("result_id", User(1, "test_user", False), "query"), ) + out._unfreeze() + out.chosen_inline_result._unfreeze() + return out class TestChosenInlineResultHandler: diff --git a/tests/test_commandhandler.py b/tests/test_commandhandler.py index f88c7e39f7b..8b6454c989b 100644 --- a/tests/test_commandhandler.py +++ b/tests/test_commandhandler.py @@ -104,7 +104,7 @@ async def _test_context_args_or_regex(self, app, handler, text): app.add_handler(handler) update = make_command_update(text, bot=app.bot) assert not await self.response(app, update) - update.message.text += " one two" + update = make_command_update(text + " one two", bot=app.bot) assert await self.response(app, update) def _test_edited(self, message, handler_edited, handler_not_edited): diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 4cf248ba4a0..615e739fe63 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -519,6 +519,7 @@ async def callback(_, __): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -570,6 +571,7 @@ async def test_conversation_handler_end(self, caplog, app, bot, user1): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -609,6 +611,7 @@ async def test_conversation_handler_fallback(self, app, bot, user1, user2): entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/eat"))], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -661,6 +664,7 @@ async def callback(_, __): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) try: @@ -694,6 +698,7 @@ async def test_conversation_handler_per_chat(self, app, bot, user1, user2): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -740,6 +745,7 @@ async def test_conversation_handler_per_user(self, app, bot, user1): ], ) message.set_bot(bot) + message._unfreeze() # First check that updates without user won't be handled message.from_user = None @@ -802,6 +808,7 @@ async def two(update, context): ) if message: message.set_bot(bot) + message._unfreeze() inline_message_id = "42" if inline else None async with app: @@ -862,6 +869,7 @@ async def test_end_on_first_message(self, app, bot, user1): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert handler.check_update(Update(update_id=0, message=message)) @@ -888,6 +896,7 @@ async def test_end_on_first_message_non_blocking_handler(self, app, bot, user1): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # give the task a chance to finish @@ -907,6 +916,7 @@ async def test_none_on_first_message(self, app, bot, user1): # User starts the state machine and a callback function returns None message = Message(0, None, self.group, from_user=user1, text="/start") message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # Check that the same message is accepted again, i.e. the conversation immediately @@ -934,6 +944,7 @@ async def test_none_on_first_message_non_blocking_handler(self, app, bot, user1) ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # Give the task a chance to finish @@ -959,6 +970,7 @@ async def test_channel_message_without_chat(self, bot): ) message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, "Misses Test")) message.set_bot(bot) + message._unfreeze() update = Update(0, channel_post=message) assert not handler.check_update(update) @@ -972,6 +984,7 @@ async def test_all_update_types(self, app, bot, user1): ) message = Message(0, None, self.group, from_user=user1, text="ignore") message.set_bot(bot) + message._unfreeze() callback_query = CallbackQuery(0, user1, None, message=message, data="data") callback_query.set_bot(bot) chosen_inline_result = ChosenInlineResult(0, user1, "query") @@ -1015,6 +1028,7 @@ async def test_no_running_job_queue_warning(self, app, bot, user1, recwarn, jq): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1053,6 +1067,7 @@ class DictJB(JobQueue): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.start() @@ -1101,6 +1116,7 @@ async def raise_error(*a, **kw): ], ) message.set_bot(bot) + message._unfreeze() # start the conversation async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1149,6 +1165,7 @@ async def raise_error(*a, **kw): ], ) message.set_bot(bot) + message._unfreeze() # start the conversation async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1255,6 +1272,7 @@ def timeout(*a, **kw): ], ) message.set_bot(bot) + message._unfreeze() async with app: # start the conversation await app.process_update(Update(update_id=0, message=message)) @@ -1300,6 +1318,7 @@ def timeout(*args, **kwargs): ], ) message.set_bot(bot) + message._unfreeze() brew_message = Message( 0, None, @@ -1348,6 +1367,7 @@ async def start_callback(u, c): ], ) message.set_bot(bot) + message._unfreeze() update = Update(update_id=0, message=message) async def timeout_callback(u, c): @@ -1407,6 +1427,7 @@ async def test_conversation_timeout_keeps_extending(self, app, bot, user1): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.start() @@ -1458,6 +1479,7 @@ async def test_conversation_timeout_two_users(self, app, bot, user1, user2): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.start() @@ -1518,6 +1540,7 @@ async def test_conversation_handler_timeout_state(self, app, bot, user1): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.start() @@ -1591,6 +1614,7 @@ async def test_conversation_handler_timeout_state_context(self, app, bot, user1) ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.start() @@ -1673,6 +1697,7 @@ async def slowbrew(_update, context): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.start() @@ -1722,6 +1747,7 @@ async def test_nested_conversation_handler(self, app, bot, user1, user2): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -1850,6 +1876,7 @@ def test_callback(u, c): ], ) message.set_bot(bot) + message._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY diff --git a/tests/test_document.py b/tests/test_document.py index fca4bc9f1b6..268c08107c1 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -179,7 +179,7 @@ async def test_send_document_caption_entities(self, bot, chat_id, document): ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) diff --git a/tests/test_filters.py b/tests/test_filters.py index 05f8a12bd6c..e0691e01bd1 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -611,6 +611,7 @@ def test_filters_document_type(self, update): update.message.document = Document( "file_id", "unique_id", mime_type="application/vnd.android.package-archive" ) + update.message.document._unfreeze() assert filters.Document.APK.check_update(update) assert filters.Document.APPLICATION.check_update(update) assert not filters.Document.DOC.check_update(update) diff --git a/tests/test_inlinequeryhandler.py b/tests/test_inlinequeryhandler.py index cf55b59fe0f..1c5d45e9813 100644 --- a/tests/test_inlinequeryhandler.py +++ b/tests/test_inlinequeryhandler.py @@ -69,7 +69,7 @@ def false_update(request): @pytest.fixture(scope="function") def inline_query(bot): - return Update( + update = Update( 0, inline_query=InlineQuery( "id", @@ -79,6 +79,9 @@ def inline_query(bot): location=Location(latitude=-23.691288, longitude=-46.788279), ), ) + update._unfreeze() + update.inline_query._unfreeze() + return update class TestInlineQueryHandler: diff --git a/tests/test_inlinequeryresultaudio.py b/tests/test_inlinequeryresultaudio.py index 4d52de0e22b..04737c626a8 100644 --- a/tests/test_inlinequeryresultaudio.py +++ b/tests/test_inlinequeryresultaudio.py @@ -73,7 +73,7 @@ def test_expected_values(self, inline_query_result_audio): assert inline_query_result_audio.audio_duration == self.audio_duration assert inline_query_result_audio.caption == self.caption assert inline_query_result_audio.parse_mode == self.parse_mode - assert inline_query_result_audio.caption_entities == self.caption_entities + assert inline_query_result_audio.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_audio.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcachedaudio.py b/tests/test_inlinequeryresultcachedaudio.py index d673193922d..1c07f259b3b 100644 --- a/tests/test_inlinequeryresultcachedaudio.py +++ b/tests/test_inlinequeryresultcachedaudio.py @@ -64,7 +64,7 @@ def test_expected_values(self, inline_query_result_cached_audio): assert inline_query_result_cached_audio.audio_file_id == self.audio_file_id assert inline_query_result_cached_audio.caption == self.caption assert inline_query_result_cached_audio.parse_mode == self.parse_mode - assert inline_query_result_cached_audio.caption_entities == self.caption_entities + assert inline_query_result_cached_audio.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_cached_audio.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcacheddocument.py b/tests/test_inlinequeryresultcacheddocument.py index 98ae3638355..e364f995782 100644 --- a/tests/test_inlinequeryresultcacheddocument.py +++ b/tests/test_inlinequeryresultcacheddocument.py @@ -69,7 +69,7 @@ def test_expected_values(self, inline_query_result_cached_document): assert inline_query_result_cached_document.title == self.title assert inline_query_result_cached_document.caption == self.caption assert inline_query_result_cached_document.parse_mode == self.parse_mode - assert inline_query_result_cached_document.caption_entities == self.caption_entities + assert inline_query_result_cached_document.caption_entities == tuple(self.caption_entities) assert inline_query_result_cached_document.description == self.description assert ( inline_query_result_cached_document.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcachedgif.py b/tests/test_inlinequeryresultcachedgif.py index 01fa426e09c..82bbd7fa7f2 100644 --- a/tests/test_inlinequeryresultcachedgif.py +++ b/tests/test_inlinequeryresultcachedgif.py @@ -66,7 +66,7 @@ def test_expected_values(self, inline_query_result_cached_gif): assert inline_query_result_cached_gif.title == self.title assert inline_query_result_cached_gif.caption == self.caption assert inline_query_result_cached_gif.parse_mode == self.parse_mode - assert inline_query_result_cached_gif.caption_entities == self.caption_entities + assert inline_query_result_cached_gif.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_cached_gif.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcachedmpeg4gif.py b/tests/test_inlinequeryresultcachedmpeg4gif.py index 4ca209d88a6..fd1548c84f0 100644 --- a/tests/test_inlinequeryresultcachedmpeg4gif.py +++ b/tests/test_inlinequeryresultcachedmpeg4gif.py @@ -66,7 +66,9 @@ def test_expected_values(self, inline_query_result_cached_mpeg4_gif): assert inline_query_result_cached_mpeg4_gif.title == self.title assert inline_query_result_cached_mpeg4_gif.caption == self.caption assert inline_query_result_cached_mpeg4_gif.parse_mode == self.parse_mode - assert inline_query_result_cached_mpeg4_gif.caption_entities == self.caption_entities + assert inline_query_result_cached_mpeg4_gif.caption_entities == tuple( + self.caption_entities + ) assert ( inline_query_result_cached_mpeg4_gif.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcachedphoto.py b/tests/test_inlinequeryresultcachedphoto.py index cbd0789467e..c5e1289e76a 100644 --- a/tests/test_inlinequeryresultcachedphoto.py +++ b/tests/test_inlinequeryresultcachedphoto.py @@ -69,7 +69,7 @@ def test_expected_values(self, inline_query_result_cached_photo): assert inline_query_result_cached_photo.description == self.description assert inline_query_result_cached_photo.caption == self.caption assert inline_query_result_cached_photo.parse_mode == self.parse_mode - assert inline_query_result_cached_photo.caption_entities == self.caption_entities + assert inline_query_result_cached_photo.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_cached_photo.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcachedvideo.py b/tests/test_inlinequeryresultcachedvideo.py index c71fffe1298..e2e9ddfcbae 100644 --- a/tests/test_inlinequeryresultcachedvideo.py +++ b/tests/test_inlinequeryresultcachedvideo.py @@ -69,7 +69,7 @@ def test_expected_values(self, inline_query_result_cached_video): assert inline_query_result_cached_video.description == self.description assert inline_query_result_cached_video.caption == self.caption assert inline_query_result_cached_video.parse_mode == self.parse_mode - assert inline_query_result_cached_video.caption_entities == self.caption_entities + assert inline_query_result_cached_video.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_cached_video.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultcachedvoice.py b/tests/test_inlinequeryresultcachedvoice.py index 400cf8e82c0..569f0030a41 100644 --- a/tests/test_inlinequeryresultcachedvoice.py +++ b/tests/test_inlinequeryresultcachedvoice.py @@ -66,7 +66,7 @@ def test_expected_values(self, inline_query_result_cached_voice): assert inline_query_result_cached_voice.title == self.title assert inline_query_result_cached_voice.caption == self.caption assert inline_query_result_cached_voice.parse_mode == self.parse_mode - assert inline_query_result_cached_voice.caption_entities == self.caption_entities + assert inline_query_result_cached_voice.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_cached_voice.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultdocument.py b/tests/test_inlinequeryresultdocument.py index 168a98de115..41044240daa 100644 --- a/tests/test_inlinequeryresultdocument.py +++ b/tests/test_inlinequeryresultdocument.py @@ -76,7 +76,7 @@ def test_expected_values(self, inline_query_result_document): assert inline_query_result_document.title == self.title assert inline_query_result_document.caption == self.caption assert inline_query_result_document.parse_mode == self.parse_mode - assert inline_query_result_document.caption_entities == self.caption_entities + assert inline_query_result_document.caption_entities == tuple(self.caption_entities) assert inline_query_result_document.mime_type == self.mime_type assert inline_query_result_document.description == self.description assert inline_query_result_document.thumb_url == self.thumb_url diff --git a/tests/test_inlinequeryresultgif.py b/tests/test_inlinequeryresultgif.py index 10f08ba6fd5..f9972b32792 100644 --- a/tests/test_inlinequeryresultgif.py +++ b/tests/test_inlinequeryresultgif.py @@ -81,7 +81,7 @@ def test_expected_values(self, inline_query_result_gif): assert inline_query_result_gif.title == self.title assert inline_query_result_gif.caption == self.caption assert inline_query_result_gif.parse_mode == self.parse_mode - assert inline_query_result_gif.caption_entities == self.caption_entities + assert inline_query_result_gif.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_gif.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultmpeg4gif.py b/tests/test_inlinequeryresultmpeg4gif.py index 54b72357920..a602b83f89d 100644 --- a/tests/test_inlinequeryresultmpeg4gif.py +++ b/tests/test_inlinequeryresultmpeg4gif.py @@ -81,7 +81,7 @@ def test_expected_values(self, inline_query_result_mpeg4_gif): assert inline_query_result_mpeg4_gif.title == self.title assert inline_query_result_mpeg4_gif.caption == self.caption assert inline_query_result_mpeg4_gif.parse_mode == self.parse_mode - assert inline_query_result_mpeg4_gif.caption_entities == self.caption_entities + assert inline_query_result_mpeg4_gif.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_mpeg4_gif.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultphoto.py b/tests/test_inlinequeryresultphoto.py index 9d594440ccd..b16d8292f0b 100644 --- a/tests/test_inlinequeryresultphoto.py +++ b/tests/test_inlinequeryresultphoto.py @@ -79,7 +79,7 @@ def test_expected_values(self, inline_query_result_photo): assert inline_query_result_photo.description == self.description assert inline_query_result_photo.caption == self.caption assert inline_query_result_photo.parse_mode == self.parse_mode - assert inline_query_result_photo.caption_entities == self.caption_entities + assert inline_query_result_photo.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_photo.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultvideo.py b/tests/test_inlinequeryresultvideo.py index c7904873387..46db2808168 100644 --- a/tests/test_inlinequeryresultvideo.py +++ b/tests/test_inlinequeryresultvideo.py @@ -84,7 +84,7 @@ def test_expected_values(self, inline_query_result_video): assert inline_query_result_video.description == self.description assert inline_query_result_video.caption == self.caption assert inline_query_result_video.parse_mode == self.parse_mode - assert inline_query_result_video.caption_entities == self.caption_entities + assert inline_query_result_video.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_video.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inlinequeryresultvoice.py b/tests/test_inlinequeryresultvoice.py index 03058e37507..71a2ad130ca 100644 --- a/tests/test_inlinequeryresultvoice.py +++ b/tests/test_inlinequeryresultvoice.py @@ -69,7 +69,7 @@ def test_expected_values(self, inline_query_result_voice): assert inline_query_result_voice.voice_duration == self.voice_duration assert inline_query_result_voice.caption == self.caption assert inline_query_result_voice.parse_mode == self.parse_mode - assert inline_query_result_voice.caption_entities == self.caption_entities + assert inline_query_result_voice.caption_entities == tuple(self.caption_entities) assert ( inline_query_result_voice.input_message_content.to_dict() == self.input_message_content.to_dict() diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 77099324ddf..dd66591e3be 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -143,7 +143,7 @@ def test_expected_values(self, input_media_video): assert input_media_video.height == self.height assert input_media_video.duration == self.duration assert input_media_video.parse_mode == self.parse_mode - assert input_media_video.caption_entities == self.caption_entities + assert input_media_video.caption_entities == tuple(self.caption_entities) assert input_media_video.supports_streaming == self.supports_streaming assert isinstance(input_media_video.thumb, InputFile) @@ -204,7 +204,7 @@ def test_expected_values(self, input_media_photo): assert input_media_photo.media == self.media assert input_media_photo.caption == self.caption assert input_media_photo.parse_mode == self.parse_mode - assert input_media_photo.caption_entities == self.caption_entities + assert input_media_photo.caption_entities == tuple(self.caption_entities) def test_to_dict(self, input_media_photo): input_media_photo_dict = input_media_photo.to_dict() @@ -256,7 +256,7 @@ def test_expected_values(self, input_media_animation): assert input_media_animation.media == self.media assert input_media_animation.caption == self.caption assert input_media_animation.parse_mode == self.parse_mode - assert input_media_animation.caption_entities == self.caption_entities + assert input_media_animation.caption_entities == tuple(self.caption_entities) assert isinstance(input_media_animation.thumb, InputFile) def test_to_dict(self, input_media_animation): @@ -318,7 +318,7 @@ def test_expected_values(self, input_media_audio): assert input_media_audio.performer == self.performer assert input_media_audio.title == self.title assert input_media_audio.parse_mode == self.parse_mode - assert input_media_audio.caption_entities == self.caption_entities + assert input_media_audio.caption_entities == tuple(self.caption_entities) assert isinstance(input_media_audio.thumb, InputFile) def test_to_dict(self, input_media_audio): @@ -378,7 +378,7 @@ def test_expected_values(self, input_media_document): assert input_media_document.media == self.media assert input_media_document.caption == self.caption assert input_media_document.parse_mode == self.parse_mode - assert input_media_document.caption_entities == self.caption_entities + assert input_media_document.caption_entities == tuple(self.caption_entities) assert ( input_media_document.disable_content_type_detection == self.disable_content_type_detection @@ -533,7 +533,7 @@ async def test_send_media_group_with_group_caption( # Make sure first message got the caption, which will lead # to Telegram displaying its caption as group caption assert first_message.caption - assert first_message.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] + assert first_message.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) # Check that other messages have no captions assert all(mes.caption is None for mes in other_messages) @@ -839,7 +839,7 @@ def build_media(parse_mode, med_type): message.message_id, ) assert message.caption == test_caption - assert message.caption_entities == test_entities + assert message.caption_entities == tuple(test_entities) # make sure that the media was not modified assert media.parse_mode == copied_media.parse_mode @@ -854,7 +854,7 @@ def build_media(parse_mode, med_type): message.message_id, ) assert message.caption == test_caption - assert message.caption_entities == test_entities + assert message.caption_entities == tuple(test_entities) # make sure that the media was not modified assert media.parse_mode == copied_media.parse_mode @@ -869,6 +869,6 @@ def build_media(parse_mode, med_type): message.message_id, ) assert message.caption == markdown_caption - assert message.caption_entities == [] + assert message.caption_entities == () # make sure that the media was not modified assert media.parse_mode == copied_media.parse_mode diff --git a/tests/test_inputtextmessagecontent.py b/tests/test_inputtextmessagecontent.py index 9d6181ed7e4..d50472be272 100644 --- a/tests/test_inputtextmessagecontent.py +++ b/tests/test_inputtextmessagecontent.py @@ -48,7 +48,7 @@ def test_expected_values(self, input_text_message_content): assert input_text_message_content.parse_mode == self.parse_mode assert input_text_message_content.message_text == self.message_text assert input_text_message_content.disable_web_page_preview == self.disable_web_page_preview - assert input_text_message_content.entities == self.entities + assert input_text_message_content.entities == tuple(self.entities) def test_to_dict(self, input_text_message_content): input_text_message_content_dict = input_text_message_content.to_dict() diff --git a/tests/test_message.py b/tests/test_message.py index c9aca3572fb..f9ccfb033d1 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -68,6 +68,7 @@ def message(bot): from_user=TestMessage.from_user, ) message.set_bot(bot) + message._unfreeze() return message diff --git a/tests/test_messagehandler.py b/tests/test_messagehandler.py index daf8a451588..54ee4076c87 100644 --- a/tests/test_messagehandler.py +++ b/tests/test_messagehandler.py @@ -65,6 +65,8 @@ def false_update(request): @pytest.fixture(scope="class") def message(bot): message = Message(1, None, Chat(1, ""), from_user=User(1, "", False)) + message._unfreeze() + message.chat._unfreeze() message.set_bot(bot) return message diff --git a/tests/test_photo.py b/tests/test_photo.py index f478d58a265..b59d444f377 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -188,7 +188,7 @@ async def test_send_photo_caption_entities(self, bot, chat_id, photo_file, thumb ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) diff --git a/tests/test_poll.py b/tests/test_poll.py index 0d15a0207f5..382e6744f89 100644 --- a/tests/test_poll.py +++ b/tests/test_poll.py @@ -26,7 +26,9 @@ @pytest.fixture(scope="class") def poll_option(): - return PollOption(text=TestPollOption.text, voter_count=TestPollOption.voter_count) + out = PollOption(text=TestPollOption.text, voter_count=TestPollOption.voter_count) + out._unfreeze() + return out class TestPollOption: @@ -96,7 +98,7 @@ def test_de_json(self): assert poll_answer.poll_id == self.poll_id assert poll_answer.user == self.user - assert poll_answer.option_ids == self.option_ids + assert poll_answer.option_ids == tuple(self.option_ids) def test_to_dict(self, poll_answer): poll_answer_dict = poll_answer.to_dict() @@ -128,7 +130,7 @@ def test_equality(self): @pytest.fixture(scope="class") def poll(): - return Poll( + poll = Poll( TestPoll.id_, TestPoll.question, TestPoll.options, @@ -142,6 +144,8 @@ def poll(): open_period=TestPoll.open_period, close_date=TestPoll.close_date, ) + poll._unfreeze() + return poll class TestPoll: @@ -181,7 +185,7 @@ def test_de_json(self, bot): assert poll.id == self.id_ assert poll.question == self.question - assert poll.options == self.options + assert poll.options == tuple(self.options) assert poll.options[0].text == self.options[0].text assert poll.options[0].voter_count == self.options[0].voter_count assert poll.options[1].text == self.options[1].text diff --git a/tests/test_sticker.py b/tests/test_sticker.py index 80741f10191..aecce58a4bc 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -506,7 +506,7 @@ def test_de_json(self, bot, sticker): assert sticker_set.title == self.title assert sticker_set.is_animated == self.is_animated assert sticker_set.is_video == self.is_video - assert sticker_set.stickers == self.stickers + assert sticker_set.stickers == tuple(self.stickers) assert sticker_set.thumb == sticker.thumb assert sticker_set.sticker_type == self.sticker_type assert sticker_set.api_kwargs == {"contains_masks": self.contains_masks} diff --git a/tests/test_user.py b/tests/test_user.py index 8e3849bf070..145f509768b 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -56,6 +56,7 @@ def user(bot): added_to_attachment_menu=TestUser.added_to_attachment_menu, ) user.set_bot(bot) + user._unfreeze() return user diff --git a/tests/test_video.py b/tests/test_video.py index c3b7fc099b4..af98de44080 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -191,7 +191,7 @@ async def test_send_video_caption_entities(self, bot, chat_id, video): ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) @flaky(3, 1) async def test_resend(self, bot, chat_id, video): diff --git a/tests/test_voice.py b/tests/test_voice.py index d617472dbee..b2907a8531e 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -165,7 +165,7 @@ async def test_send_voice_caption_entities(self, bot, chat_id, voice_file): ) assert message.caption == test_string - assert message.caption_entities == entities + assert message.caption_entities == tuple(entities) @flaky(3, 1) @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) From ba5f30136873570f230a720650817ba45615f5ee Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:56:40 +0100 Subject: [PATCH 52/90] more tests working --- tests/test_chat.py | 1 + tests/test_conversationhandler.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/tests/test_chat.py b/tests/test_chat.py index ddc9d3cdbd8..e5dd3443cc6 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -46,6 +46,7 @@ def chat(bot): has_restricted_voice_and_video_messages=True, ) chat.set_bot(bot) + chat._unfreeze() return chat diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 615e739fe63..a1b189b3869 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -520,6 +520,7 @@ async def callback(_, __): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -572,6 +573,7 @@ async def test_conversation_handler_end(self, caplog, app, bot, user1): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -612,6 +614,7 @@ async def test_conversation_handler_fallback(self, app, bot, user1, user2): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -665,6 +668,7 @@ async def callback(_, __): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) try: @@ -699,6 +703,7 @@ async def test_conversation_handler_per_chat(self, app, bot, user1, user2): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -746,6 +751,7 @@ async def test_conversation_handler_per_user(self, app, bot, user1): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() # First check that updates without user won't be handled message.from_user = None @@ -809,6 +815,7 @@ async def two(update, context): if message: message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() inline_message_id = "42" if inline else None async with app: @@ -830,6 +837,7 @@ async def two(update, context): inline_message_id=inline_message_id, ) cbq_2.set_bot(bot) + cbq_2._unfreeze() await app.process_update(Update(update_id=0, callback_query=cbq_1)) # Make sure that we're in the correct state @@ -870,6 +878,7 @@ async def test_end_on_first_message(self, app, bot, user1): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert handler.check_update(Update(update_id=0, message=message)) @@ -897,6 +906,7 @@ async def test_end_on_first_message_non_blocking_handler(self, app, bot, user1): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # give the task a chance to finish @@ -917,6 +927,7 @@ async def test_none_on_first_message(self, app, bot, user1): message = Message(0, None, self.group, from_user=user1, text="/start") message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # Check that the same message is accepted again, i.e. the conversation immediately @@ -945,6 +956,7 @@ async def test_none_on_first_message_non_blocking_handler(self, app, bot, user1) ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # Give the task a chance to finish @@ -971,6 +983,7 @@ async def test_channel_message_without_chat(self, bot): message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, "Misses Test")) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() update = Update(0, channel_post=message) assert not handler.check_update(update) @@ -985,6 +998,7 @@ async def test_all_update_types(self, app, bot, user1): message = Message(0, None, self.group, from_user=user1, text="ignore") message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() callback_query = CallbackQuery(0, user1, None, message=message, data="data") callback_query.set_bot(bot) chosen_inline_result = ChosenInlineResult(0, user1, "query") @@ -1029,6 +1043,7 @@ async def test_no_running_job_queue_warning(self, app, bot, user1, recwarn, jq): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1068,6 +1083,7 @@ class DictJB(JobQueue): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.start() @@ -1117,6 +1133,7 @@ async def raise_error(*a, **kw): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() # start the conversation async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1166,6 +1183,7 @@ async def raise_error(*a, **kw): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() # start the conversation async with app: await app.process_update(Update(update_id=0, message=message)) @@ -1273,6 +1291,7 @@ def timeout(*a, **kw): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: # start the conversation await app.process_update(Update(update_id=0, message=message)) @@ -1319,6 +1338,7 @@ def timeout(*args, **kwargs): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() brew_message = Message( 0, None, @@ -1368,6 +1388,7 @@ async def start_callback(u, c): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() update = Update(update_id=0, message=message) async def timeout_callback(u, c): @@ -1428,6 +1449,7 @@ async def test_conversation_timeout_keeps_extending(self, app, bot, user1): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.start() @@ -1480,6 +1502,7 @@ async def test_conversation_timeout_two_users(self, app, bot, user1, user2): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.start() @@ -1541,6 +1564,7 @@ async def test_conversation_handler_timeout_state(self, app, bot, user1): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.start() @@ -1615,6 +1639,7 @@ async def test_conversation_handler_timeout_state_context(self, app, bot, user1) ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.start() @@ -1698,6 +1723,7 @@ async def slowbrew(_update, context): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.start() @@ -1748,6 +1774,7 @@ async def test_nested_conversation_handler(self, app, bot, user1, user2): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -1877,6 +1904,7 @@ def test_callback(u, c): ) message.set_bot(bot) message._unfreeze() + message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -2210,6 +2238,7 @@ async def blocking(_, __): text="/start", from_user=user1, ) + message._unfreeze() async with app: await app.process_update(Update(0, message=message)) From e3569bafb5961f0bba44897983a3eec1322533e1 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:14:47 +0100 Subject: [PATCH 53/90] more tests --- tests/test_helpers.py | 2 +- tests/test_message.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index fa1f2a40cf9..21765c0e5d8 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -125,7 +125,7 @@ def build_test_message(kwargs): config.update(**kwargs) return Message(**config) - message = build_test_message({message_type: True}) + message = build_test_message({message_type: (True,)}) # tuple for array-type args entity = message if entity_type is Message else Update(1, message=message) assert helpers.effective_message_type(entity) == message_type diff --git a/tests/test_message.py b/tests/test_message.py index f9ccfb033d1..dd43c16a675 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -16,6 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +from copy import copy from datetime import datetime import pytest @@ -64,11 +65,13 @@ def message(bot): message = Message( message_id=TestMessage.id_, date=TestMessage.date, - chat=TestMessage.chat, - from_user=TestMessage.from_user, + chat=copy(TestMessage.chat), + from_user=copy(TestMessage.from_user), ) message.set_bot(bot) message._unfreeze() + message.chat._unfreeze() + message.from_user._unfreeze() return message From 28486d33319afb9525d5ff5a71b443df2baa93a6 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 11 Nov 2022 13:32:22 +0100 Subject: [PATCH 54/90] *more tests!* --- tests/test_chatmemberupdated.py | 3 +++ tests/test_conversationhandler.py | 1 - tests/test_file.py | 1 + tests/test_filters.py | 5 +++++ tests/test_inlinequeryhandler.py | 1 + tests/test_inputinvoicemessagecontent.py | 4 ++-- tests/test_inputmedia.py | 4 ++-- tests/test_webhookinfo.py | 4 ++-- 8 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index 6269d2037c2..29cc9e5824f 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -217,6 +217,9 @@ def test_difference_required(self, user, chat): # just happens by id/required args new_user = User(1, "First name", False, last_name="last name") new_chat_member = ChatMember(new_user, "new_status") + chat_member_updated = ChatMemberUpdated( + chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member + ) assert chat_member_updated.difference() == { "status": ("old_status", "new_status"), "user": (user, new_user), diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index a14673d7ad3..5e988feddcf 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -814,7 +814,6 @@ async def two(update, context): if message: message.set_bot(bot) message._unfreeze() - message.entities[0]._unfreeze() inline_message_id = "42" if inline else None async with app: diff --git a/tests/test_file.py b/tests/test_file.py index 02d2f176ca6..512a4709deb 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -36,6 +36,7 @@ def file(bot): file_size=TestFile.file_size, ) file.set_bot(bot) + file._unfreeze() return file diff --git a/tests/test_filters.py b/tests/test_filters.py index e0691e01bd1..b5708ef4c38 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -721,6 +721,7 @@ def test_filters_file_extension_basic(self, update): file_name="file.jpg", mime_type="image/jpeg", ) + update.message.document._unfreeze() assert filters.Document.FileExtension("jpg").check_update(update) assert not filters.Document.FileExtension("jpeg").check_update(update) assert not filters.Document.FileExtension("file.jpg").check_update(update) @@ -744,6 +745,7 @@ def test_filters_file_extension_minds_dots(self, update): file_name="file.jpg", mime_type="image/jpeg", ) + update.message.document._unfreeze() assert not filters.Document.FileExtension(".jpg").check_update(update) assert not filters.Document.FileExtension("e.jpg").check_update(update) assert not filters.Document.FileExtension("file.jpg").check_update(update) @@ -773,6 +775,7 @@ def test_filters_file_extension_none_arg(self, update): file_name="file.jpg", mime_type="image/jpeg", ) + update.message.document._unfreeze() assert not filters.Document.FileExtension(None).check_update(update) update.message.document.file_name = "file" @@ -792,6 +795,7 @@ def test_filters_file_extension_case_sensitivity(self, update): file_name="file.jpg", mime_type="image/jpeg", ) + update.message.document._unfreeze() assert filters.Document.FileExtension("JPG").check_update(update) assert filters.Document.FileExtension("jpG").check_update(update) @@ -839,6 +843,7 @@ def test_filters_photo(self, update): def test_filters_sticker(self, update): assert not filters.Sticker.ALL.check_update(update) update.message.sticker = Sticker("1", "uniq", 1, 2, False, False, Sticker.REGULAR) + update.message.sticker._unfreeze() assert filters.Sticker.ALL.check_update(update) assert filters.Sticker.STATIC.check_update(update) assert not filters.Sticker.VIDEO.check_update(update) diff --git a/tests/test_inlinequeryhandler.py b/tests/test_inlinequeryhandler.py index 1c5d45e9813..39f119f06ad 100644 --- a/tests/test_inlinequeryhandler.py +++ b/tests/test_inlinequeryhandler.py @@ -146,6 +146,7 @@ async def test_context_pattern(self, app, inline_query): update = Update( update_id=0, inline_query=InlineQuery(id="id", from_user=None, query="", offset="") ) + update.inline_query._unfreeze() assert not handler.check_update(update) update.inline_query.query = "not_a_match" assert not handler.check_update(update) diff --git a/tests/test_inputinvoicemessagecontent.py b/tests/test_inputinvoicemessagecontent.py index 93a408d57ae..9b85a0047d6 100644 --- a/tests/test_inputinvoicemessagecontent.py +++ b/tests/test_inputinvoicemessagecontent.py @@ -82,7 +82,7 @@ def test_expected_values(self, input_invoice_message_content): assert input_invoice_message_content.payload == self.payload assert input_invoice_message_content.provider_token == self.provider_token assert input_invoice_message_content.currency == self.currency - assert input_invoice_message_content.prices == self.prices + assert input_invoice_message_content.prices == tuple(self.prices) assert input_invoice_message_content.max_tip_amount == self.max_tip_amount assert input_invoice_message_content.suggested_tip_amounts == [ int(amount) for amount in self.suggested_tip_amounts @@ -217,7 +217,7 @@ def test_de_json(self, bot): assert input_invoice_message_content.payload == self.payload assert input_invoice_message_content.provider_token == self.provider_token assert input_invoice_message_content.currency == self.currency - assert input_invoice_message_content.prices == self.prices + assert input_invoice_message_content.prices == tuple(self.prices) assert input_invoice_message_content.max_tip_amount == self.max_tip_amount assert input_invoice_message_content.suggested_tip_amounts == [ int(amount) for amount in self.suggested_tip_amounts diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 2df96d47568..eced7ebe9bb 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -462,7 +462,7 @@ async def test_send_media_group_photo(self, bot, chat_id, media_group): assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) assert all(mes.caption == f"photo {idx+1}" for idx, mes in enumerate(messages)) assert all( - mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages + mes.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) for mes in messages ) async def test_send_media_group_throws_error_with_group_caption_and_individual_captions( @@ -568,7 +568,7 @@ async def test_send_media_group_all_args(self, bot, raw_bot, chat_id, media_grou assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) assert all(mes.caption == f"photo {idx+1}" for idx, mes in enumerate(messages)) assert all( - mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] + mes.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) for mes in messages ) assert all(mes.has_protected_content for mes in messages) diff --git a/tests/test_webhookinfo.py b/tests/test_webhookinfo.py index 55d94e33e7f..eb526dd05e2 100644 --- a/tests/test_webhookinfo.py +++ b/tests/test_webhookinfo.py @@ -62,7 +62,7 @@ def test_to_dict(self, webhook_info): assert webhook_info_dict["pending_update_count"] == self.pending_update_count assert webhook_info_dict["last_error_date"] == self.last_error_date assert webhook_info_dict["max_connections"] == self.max_connections - assert webhook_info_dict["allowed_updates"] == self.allowed_updates + assert webhook_info_dict["allowed_updates"] == tuple(self.allowed_updates) assert webhook_info_dict["ip_address"] == self.ip_address assert ( webhook_info_dict["last_synchronization_error_date"] @@ -89,7 +89,7 @@ def test_de_json(self, bot): assert isinstance(webhook_info.last_error_date, datetime) assert webhook_info.last_error_date == from_timestamp(self.last_error_date) assert webhook_info.max_connections == self.max_connections - assert webhook_info.allowed_updates == self.allowed_updates + assert webhook_info.allowed_updates == tuple(self.allowed_updates) assert webhook_info.ip_address == self.ip_address assert isinstance(webhook_info.last_synchronization_error_date, datetime) assert webhook_info.last_synchronization_error_date == from_timestamp( From ccf8fe07dc8be3c85473ffbc24182b4a6c329ab0 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 14 Nov 2022 13:13:47 +0100 Subject: [PATCH 55/90] fix a few more tests --- telegram/ext/_extbot.py | 2 ++ tests/test_bot.py | 1 + tests/test_conversationhandler.py | 3 --- tests/test_encryptedpassportelement.py | 2 +- tests/test_game.py | 10 ++++++---- tests/test_inlinekeyboardmarkup.py | 4 +++- tests/test_inputinvoicemessagecontent.py | 13 ++++++------- tests/test_inputmedia.py | 2 +- tests/test_poll.py | 4 ++-- tests/test_shippingoption.py | 2 +- tests/test_userprofilephotos.py | 2 +- tests/test_videochat.py | 8 +++----- tests/test_webhookinfo.py | 2 +- 13 files changed, 28 insertions(+), 27 deletions(-) diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 4baa10f3ff2..8a1500b714b 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -553,8 +553,10 @@ def _effective_inline_results( # We build a new result in case the user wants to use the same object in # different places new_result = copy(result) + new_result._unfreeze() # pylint: disable=protected-access markup = self._replace_keyboard(result.reply_markup) # type: ignore[attr-defined] new_result.reply_markup = markup + new_result._freeze() # pylint: disable=protected-access results.append(new_result) return results, next_offset diff --git a/tests/test_bot.py b/tests/test_bot.py index 065a9a3da10..2067d66814b 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -102,6 +102,7 @@ async def message(bot, chat_id): disable_notification=True, ) out._unfreeze() + return out @pytest.fixture(scope="class") diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 5e988feddcf..678993f07a6 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -925,7 +925,6 @@ async def test_none_on_first_message(self, app, bot, user1): message = Message(0, None, self.group, from_user=user1, text="/start") message.set_bot(bot) message._unfreeze() - message.entities[0]._unfreeze() async with app: await app.process_update(Update(update_id=0, message=message)) # Check that the same message is accepted again, i.e. the conversation immediately @@ -981,7 +980,6 @@ async def test_channel_message_without_chat(self, bot): message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, "Misses Test")) message.set_bot(bot) message._unfreeze() - message.entities[0]._unfreeze() update = Update(0, channel_post=message) assert not handler.check_update(update) @@ -996,7 +994,6 @@ async def test_all_update_types(self, app, bot, user1): message = Message(0, None, self.group, from_user=user1, text="ignore") message.set_bot(bot) message._unfreeze() - message.entities[0]._unfreeze() callback_query = CallbackQuery(0, user1, None, message=message, data="data") callback_query.set_bot(bot) chosen_inline_result = ChosenInlineResult(0, user1, "query") diff --git a/tests/test_encryptedpassportelement.py b/tests/test_encryptedpassportelement.py index 2e67fe29d5a..a7077aca7b1 100644 --- a/tests/test_encryptedpassportelement.py +++ b/tests/test_encryptedpassportelement.py @@ -60,7 +60,7 @@ def test_expected_values(self, encrypted_passport_element): assert encrypted_passport_element.data == self.data assert encrypted_passport_element.phone_number == self.phone_number assert encrypted_passport_element.email == self.email - assert encrypted_passport_element.files == self.files + assert encrypted_passport_element.files == tuple(self.files) assert encrypted_passport_element.front_side == self.front_side assert encrypted_passport_element.reverse_side == self.reverse_side assert encrypted_passport_element.selfie == self.selfie diff --git a/tests/test_game.py b/tests/test_game.py index 5c7e96814ef..dc775016467 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -24,7 +24,7 @@ @pytest.fixture(scope="function") def game(): - return Game( + game = Game( TestGame.title, TestGame.description, TestGame.photo, @@ -32,6 +32,8 @@ def game(): text_entities=TestGame.text_entities, animation=TestGame.animation, ) + game._unfreeze() + return game class TestGame: @@ -61,7 +63,7 @@ def test_de_json_required(self, bot): assert game.title == self.title assert game.description == self.description - assert game.photo == self.photo + assert game.photo == tuple(self.photo) def test_de_json_all(self, bot): json_dict = { @@ -77,9 +79,9 @@ def test_de_json_all(self, bot): assert game.title == self.title assert game.description == self.description - assert game.photo == self.photo + assert game.photo == tuple(self.photo) assert game.text == self.text - assert game.text_entities == self.text_entities + assert game.text_entities == tuple(self.text_entities) assert game.animation == self.animation def test_to_dict(self, game): diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py index 58beb9e5b00..fecad81eadf 100644 --- a/tests/test_inlinekeyboardmarkup.py +++ b/tests/test_inlinekeyboardmarkup.py @@ -86,7 +86,7 @@ def test_from_column(self): assert len(inline_keyboard_markup[1]) == 1 def test_expected_values(self, inline_keyboard_markup): - assert inline_keyboard_markup.inline_keyboard == self.inline_keyboard + assert inline_keyboard_markup.inline_keyboard == tuple(self.inline_keyboard) def test_wrong_keyboard_inputs(self): with pytest.raises(ValueError): @@ -121,8 +121,10 @@ async def make_assertion( assert bool("'switch_inline_query': ''" in str(data["reply_markup"])) assert bool("'switch_inline_query_current_chat': ''" in str(data["reply_markup"])) + inline_keyboard_markup.inline_keyboard[0][0]._unfreeze() inline_keyboard_markup.inline_keyboard[0][0].callback_data = None inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = "" + inline_keyboard_markup.inline_keyboard[0][1]._unfreeze() inline_keyboard_markup.inline_keyboard[0][1].callback_data = None inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = "" diff --git a/tests/test_inputinvoicemessagecontent.py b/tests/test_inputinvoicemessagecontent.py index 9b85a0047d6..522aec386c4 100644 --- a/tests/test_inputinvoicemessagecontent.py +++ b/tests/test_inputinvoicemessagecontent.py @@ -84,9 +84,9 @@ def test_expected_values(self, input_invoice_message_content): assert input_invoice_message_content.currency == self.currency assert input_invoice_message_content.prices == tuple(self.prices) assert input_invoice_message_content.max_tip_amount == self.max_tip_amount - assert input_invoice_message_content.suggested_tip_amounts == [ + assert input_invoice_message_content.suggested_tip_amounts == tuple( int(amount) for amount in self.suggested_tip_amounts - ] + ) assert input_invoice_message_content.provider_data == self.provider_data assert input_invoice_message_content.photo_url == self.photo_url assert input_invoice_message_content.photo_size == int(self.photo_size) @@ -130,9 +130,8 @@ def test_to_dict(self, input_invoice_message_content): input_invoice_message_content_dict["max_tip_amount"] == input_invoice_message_content.max_tip_amount ) - assert ( - input_invoice_message_content_dict["suggested_tip_amounts"] - == input_invoice_message_content.suggested_tip_amounts + assert input_invoice_message_content_dict["suggested_tip_amounts"] == list( + input_invoice_message_content.suggested_tip_amounts ) assert ( input_invoice_message_content_dict["provider_data"] @@ -219,9 +218,9 @@ def test_de_json(self, bot): assert input_invoice_message_content.currency == self.currency assert input_invoice_message_content.prices == tuple(self.prices) assert input_invoice_message_content.max_tip_amount == self.max_tip_amount - assert input_invoice_message_content.suggested_tip_amounts == [ + assert input_invoice_message_content.suggested_tip_amounts == tuple( int(amount) for amount in self.suggested_tip_amounts - ] + ) assert input_invoice_message_content.provider_data == self.provider_data assert input_invoice_message_content.photo_url == self.photo_url assert input_invoice_message_content.photo_size == int(self.photo_size) diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index eced7ebe9bb..51b80205c88 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -728,7 +728,7 @@ async def test_send_media_group_default_parse_mode( for mes_group in (default, overridden_markdown_v2): first_message = mes_group[0] assert first_message.caption == "photo 1" - assert first_message.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] + assert first_message.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) # This check is valid for all 3 groups of messages for mes_group in (default, overridden_markdown_v2, overridden_none): diff --git a/tests/test_poll.py b/tests/test_poll.py index 382e6744f89..425c4eadf1a 100644 --- a/tests/test_poll.py +++ b/tests/test_poll.py @@ -106,7 +106,7 @@ def test_to_dict(self, poll_answer): assert isinstance(poll_answer_dict, dict) assert poll_answer_dict["poll_id"] == poll_answer.poll_id assert poll_answer_dict["user"] == poll_answer.user.to_dict() - assert poll_answer_dict["option_ids"] == poll_answer.option_ids + assert poll_answer_dict["option_ids"] == list(poll_answer.option_ids) def test_equality(self): a = PollAnswer(123, self.user, [2]) @@ -196,7 +196,7 @@ def test_de_json(self, bot): assert poll.type == self.type assert poll.allows_multiple_answers == self.allows_multiple_answers assert poll.explanation == self.explanation - assert poll.explanation_entities == self.explanation_entities + assert poll.explanation_entities == tuple(self.explanation_entities) assert poll.open_period == self.open_period assert abs(poll.close_date - self.close_date) < timedelta(seconds=1) assert to_timestamp(poll.close_date) == to_timestamp(self.close_date) diff --git a/tests/test_shippingoption.py b/tests/test_shippingoption.py index 910d275433a..663a5ad2712 100644 --- a/tests/test_shippingoption.py +++ b/tests/test_shippingoption.py @@ -42,7 +42,7 @@ def test_slot_behaviour(self, shipping_option, mro_slots): def test_expected_values(self, shipping_option): assert shipping_option.id == self.id_ assert shipping_option.title == self.title - assert shipping_option.prices == self.prices + assert shipping_option.prices == tuple(self.prices) def test_to_dict(self, shipping_option): shipping_option_dict = shipping_option.to_dict() diff --git a/tests/test_userprofilephotos.py b/tests/test_userprofilephotos.py index bd13c223b82..9b3e3f2c1a0 100644 --- a/tests/test_userprofilephotos.py +++ b/tests/test_userprofilephotos.py @@ -43,7 +43,7 @@ def test_de_json(self, bot): user_profile_photos = UserProfilePhotos.de_json(json_dict, bot) assert user_profile_photos.api_kwargs == {} assert user_profile_photos.total_count == self.total_count - assert user_profile_photos.photos == self.photos + assert user_profile_photos.photos == tuple(self.photos) def test_to_dict(self): user_profile_photos = UserProfilePhotos(self.total_count, self.photos) diff --git a/tests/test_videochat.py b/tests/test_videochat.py index d4aed477441..5155a944e28 100644 --- a/tests/test_videochat.py +++ b/tests/test_videochat.py @@ -109,7 +109,7 @@ def test_de_json(self, user1, user2, bot): video_chat_participants = VideoChatParticipantsInvited.de_json(json_data, bot) assert video_chat_participants.api_kwargs == {} - assert isinstance(video_chat_participants.users, list) + assert isinstance(video_chat_participants.users, tuple) assert video_chat_participants.users[0] == user1 assert video_chat_participants.users[1] == user2 assert video_chat_participants.users[0].id == user1.id @@ -117,9 +117,7 @@ def test_de_json(self, user1, user2, bot): @pytest.mark.parametrize("use_users", (True, False)) def test_to_dict(self, user1, user2, use_users): - video_chat_participants = VideoChatParticipantsInvited( - [user1, user2] if use_users else None - ) + video_chat_participants = VideoChatParticipantsInvited([user1, user2] if use_users else ()) video_chat_dict = video_chat_participants.to_dict() assert isinstance(video_chat_dict, dict) @@ -128,7 +126,7 @@ def test_to_dict(self, user1, user2, use_users): assert video_chat_dict["users"][0]["id"] == user1.id assert video_chat_dict["users"][1]["id"] == user2.id else: - assert video_chat_dict == {} + assert video_chat_dict == {"users": ()} def test_equality(self, user1, user2): a = VideoChatParticipantsInvited([user1]) diff --git a/tests/test_webhookinfo.py b/tests/test_webhookinfo.py index eb526dd05e2..f75e63424b3 100644 --- a/tests/test_webhookinfo.py +++ b/tests/test_webhookinfo.py @@ -62,7 +62,7 @@ def test_to_dict(self, webhook_info): assert webhook_info_dict["pending_update_count"] == self.pending_update_count assert webhook_info_dict["last_error_date"] == self.last_error_date assert webhook_info_dict["max_connections"] == self.max_connections - assert webhook_info_dict["allowed_updates"] == tuple(self.allowed_updates) + assert webhook_info_dict["allowed_updates"] == self.allowed_updates assert webhook_info_dict["ip_address"] == self.ip_address assert ( webhook_info_dict["last_synchronization_error_date"] From 7e8b08eaacbb0bb5cb1b5573423a72c91a5ffbbc Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:10:40 +0100 Subject: [PATCH 56/90] more tests --- telegram/_telegramobject.py | 3 +++ telegram/ext/_picklepersistence.py | 7 +++++++ tests/test_bot.py | 1 + tests/test_passport.py | 1 + tests/test_sticker.py | 2 +- tests/test_telegramobject.py | 12 +++++++++++- tests/test_videochat.py | 2 +- 7 files changed, 25 insertions(+), 3 deletions(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 4490e31ddf9..3182cbc8732 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -168,6 +168,9 @@ def __repr__(self) -> str: if not self.api_kwargs: # Drop api_kwargs from the representation, if empty as_dict.pop("api_kwargs", None) + else: + # Otherwise, we want want to skip the "mappingproxy" part of the repr + as_dict["api_kwargs"] = dict(self.api_kwargs) contents = ", ".join( f"{k}={as_dict[k]!r}" diff --git a/telegram/ext/_picklepersistence.py b/telegram/ext/_picklepersistence.py index af5126af66b..17387d39fa5 100644 --- a/telegram/ext/_picklepersistence.py +++ b/telegram/ext/_picklepersistence.py @@ -22,6 +22,7 @@ from copy import deepcopy from pathlib import Path from sys import version_info as py_ver +from types import MappingProxyType from typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, cast, overload from telegram import Bot, TelegramObject @@ -53,6 +54,9 @@ def _reconstruct_to(cls: Type[TelegramObj], kwargs: dict) -> TelegramObj: is changed, since `_custom_reduction` places references to this function into the pickled data. """ obj = cls.__new__(cls) + # Converting to MappingProxyType + # is necessary, since _custom_reduction converts it to a dict as MPT is not pickable. + kwargs["api_kwargs"] = MappingProxyType(kwargs["api_kwargs"]) obj.__setstate__(kwargs) return obj @@ -63,6 +67,9 @@ def _custom_reduction(cls: TelegramObj) -> Tuple[Callable, Tuple[Type[TelegramOb works as intended. """ data = cls._get_attrs(include_private=True) # pylint: disable=protected-access + # MappingProxyType is not pickable, so we convert it to a dict and revert in + # _reconstruct_to + data["api_kwargs"] = dict(data["api_kwargs"]) # type: ignore[arg-type] return _reconstruct_to, (cls.__class__, data) diff --git a/tests/test_bot.py b/tests/test_bot.py index 2067d66814b..3018855799a 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -804,6 +804,7 @@ async def test_send_close_date_default_tz(self, tz_bot, super_group_id): close_date=close_date, read_timeout=60, ) + msg.poll._unfreeze() # Sometimes there can be a few seconds delay, so don't let the test fail due to that- msg.poll.close_date = msg.poll.close_date.astimezone(aware_close_date.tzinfo) assert abs(msg.poll.close_date - aware_close_date) <= dtm.timedelta(seconds=5) diff --git a/tests/test_passport.py b/tests/test_passport.py index 72ac4412e3f..77f9a0c91ea 100644 --- a/tests/test_passport.py +++ b/tests/test_passport.py @@ -523,6 +523,7 @@ def test_equality(self, passport_data): assert hash(a) == hash(b) assert a is not b + passport_data.credentials._unfreeze() passport_data.credentials.hash = "NOTAPROPERHASH" c = PassportData(passport_data.data, passport_data.credentials) diff --git a/tests/test_sticker.py b/tests/test_sticker.py index 1af5fdb5d8c..b44ad66a3ba 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -824,7 +824,7 @@ def test_equality(self): self.is_video, self.sticker_type, ) - c = StickerSet(self.name, None, None, None, None, Sticker.CUSTOM_EMOJI) + c = StickerSet(self.name, "title", False, [], True, Sticker.CUSTOM_EMOJI) d = StickerSet( "blah", self.title, diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index b7845cf73a0..73a8981aba6 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -238,7 +238,15 @@ def test_pickle(self, bot): date = datetime.datetime.now() photo = PhotoSize("file_id", "unique", 21, 21) photo.set_bot(bot) - msg = Message(1, date, chat, from_user=user, text="foobar", photo=[photo]) + msg = Message( + 1, + date, + chat, + from_user=user, + text="foobar", + photo=[photo], + api_kwargs={"api": "kwargs"}, + ) msg.set_bot(bot) # Test pickling of TGObjects, we choose Message since it's contains the most subclasses. @@ -252,6 +260,8 @@ def test_pickle(self, bot): assert unpickled.from_user == user assert unpickled.date == date, f"{unpickled.date} != {date}" assert unpickled.photo[0] == photo + assert isinstance(unpickled.api_kwargs, MappingProxyType) + assert unpickled.api_kwargs == {"api": "kwargs"} def test_pickle_apply_api_kwargs(self, bot): """Makes sure that when a class gets new attributes, the api_kwargs are moved to the diff --git a/tests/test_videochat.py b/tests/test_videochat.py index 5155a944e28..f144669eed7 100644 --- a/tests/test_videochat.py +++ b/tests/test_videochat.py @@ -132,7 +132,7 @@ def test_equality(self, user1, user2): a = VideoChatParticipantsInvited([user1]) b = VideoChatParticipantsInvited([user1]) c = VideoChatParticipantsInvited([user1, user2]) - d = VideoChatParticipantsInvited(None) + d = VideoChatParticipantsInvited([]) e = VideoChatStarted() assert a == b From 0acdae3c703b772f9b7098857244c88c67787edd Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 15 Nov 2022 18:38:19 +0100 Subject: [PATCH 57/90] Make de_list return tuples --- telegram/_bot.py | 44 +++++++++++++++++++++--------- telegram/_passport/passportfile.py | 17 +++++++----- telegram/_telegramobject.py | 15 ++++++---- telegram/ext/_extbot.py | 12 ++++---- tests/conftest.py | 2 +- tests/test_bot.py | 12 ++++---- tests/test_inputmedia.py | 8 +++--- tests/test_userprofilephotos.py | 2 +- 8 files changed, 68 insertions(+), 44 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 147f6283784..05fd09bac59 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -1770,9 +1770,12 @@ async def send_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> List[Message]: + ) -> Tuple[Message, ...]: """Use this method to send a group of photos or videos as an album. + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Note: If you supply a :paramref:`caption` (along with either :paramref:`parse_mode` or :paramref:`caption_entities`), then items in :paramref:`media` must have no captions, @@ -1815,7 +1818,7 @@ async def send_media_group( .. versionadded:: 20.0 Returns: - List[:class:`telegram.Message`]: An array of the sent Messages. + Tuple[:class:`telegram.Message`]: An array of the sent Messages. Raises: :class:`telegram.error.TelegramError` @@ -3383,9 +3386,12 @@ async def get_updates( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[Update]: + ) -> Tuple[Update, ...]: """Use this method to receive incoming updates using long polling. + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Note: 1. This method will not work if an outgoing webhook is set up. 2. In order to avoid getting duplicate updates, recalculate offset after each @@ -3416,7 +3422,7 @@ async def get_updates( a short period of time. Returns: - List[:class:`telegram.Update`] + Tuple[:class:`telegram.Update`] Raises: :class:`telegram.error.TelegramError` @@ -3711,17 +3717,20 @@ async def get_chat_administrators( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[ChatMember]: + ) -> Tuple[ChatMember, ...]: """ Use this method to get a list of administrators in a chat. .. seealso:: :attr:`telegram.Chat.get_administrators` + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Args: chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| Returns: - List[:class:`telegram.ChatMember`]: On success, returns a list of ``ChatMember`` + Tuple[:class:`telegram.ChatMember`]: On success, returns a list of ``ChatMember`` objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned. @@ -4001,11 +4010,14 @@ async def get_game_high_scores( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[GameHighScore]: + ) -> Tuple[GameHighScore, ...]: """ Use this method to get data for high score tables. Will return the score of the specified user and several of their neighbors in a game. + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Note: This method will currently return scores for the target user, plus two of their closest neighbors on each side. Will also return the top three users if the user and @@ -4023,7 +4035,7 @@ async def get_game_high_scores( :paramref:`message_id` are not specified. Identifier of the inline message. Returns: - List[:class:`telegram.GameHighScore`] + Tuple[:class:`telegram.GameHighScore`] Raises: :class:`telegram.error.TelegramError` @@ -5387,18 +5399,21 @@ async def get_custom_emoji_stickers( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[Sticker]: + ) -> Tuple[Sticker, ...]: # skipcq: FLK-D207 """ Use this method to get information about emoji stickers by their identifiers. + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Args: custom_emoji_ids (List[:obj:`str`]): List of custom emoji identifiers. At most :tg-const:`telegram.constants.CustomEmojiStickerLimit.\ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. Returns: - List[:class:`telegram.Sticker`] + Tuple[:class:`telegram.Sticker`] Raises: :class:`telegram.error.TelegramError` @@ -6217,11 +6232,14 @@ async def get_my_commands( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[BotCommand]: + ) -> Tuple[BotCommand, ...]: """ Use this method to get the current list of the bot's commands for the given scope and user language. + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Args: scope (:class:`telegram.BotCommandScope`, optional): An object, describing scope of users. Defaults to :class:`telegram.BotCommandScopeDefault`. @@ -6234,8 +6252,8 @@ async def get_my_commands( .. versionadded:: 13.7 Returns: - List[:class:`telegram.BotCommand`]: On success, the commands set for the bot. An empty - list is returned if commands are not set. + Tuple[:class:`telegram.BotCommand`]: On success, the commands set for the bot. An empty + tuple is returned if commands are not set. Raises: :class:`telegram.error.TelegramError` diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 57164d0c91e..2c3cc23d597 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Encrypted PassportFile.""" -from typing import TYPE_CHECKING, List, Optional +from typing import TYPE_CHECKING, List, Optional, Tuple from telegram._telegramobject import TelegramObject from telegram._utils.defaultvalue import DEFAULT_NONE @@ -117,26 +117,29 @@ def de_json_decrypted( @classmethod def de_list_decrypted( cls, data: Optional[List[JSONDict]], bot: "Bot", credentials: List["FileCredentials"] - ) -> List[Optional["PassportFile"]]: + ) -> Tuple[Optional["PassportFile"], ...]: """Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account passport credentials. + .. versionchange:: 20.0 + Returns a tuple instead of a list. + Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (List[Dict[:obj:`str`, ...]]): The JSON data. bot (:class:`telegram.Bot`): The bot associated with these objects. credentials (:class:`telegram.FileCredentials`): The credentials Returns: - List[:class:`telegram.PassportFile`]: + Tuple[:class:`telegram.PassportFile`]: """ if not data: - return [] + return () - return [ + return tuple( cls.de_json_decrypted(passport_file, bot, credentials[i]) for i, passport_file in enumerate(data) - ] + ) async def get_file( self, diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index c16a1fb3957..db142a9b634 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -408,21 +408,24 @@ def _de_json( @classmethod def de_list( cls: Type[Tele_co], data: Optional[List[JSONDict]], bot: "Bot" - ) -> List[Optional[Tele_co]]: - """Converts JSON data to a list of Telegram objects. + ) -> Tuple[Optional[Tele_co], ...]: + """Converts a list of JSON objects to a tuple of Telegram objects. + + .. versionchange:: 20.0 + Returns a tuple instead of a list. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (List[Dict[:obj:`str`, ...]]): The JSON data. bot (:class:`telegram.Bot`): The bot associated with these objects. Returns: - A list of Telegram objects. + A tuple of Telegram objects. """ if not data: - return [] + return () - return [cls.de_json(d, bot) for d in data] + return tuple(cls.de_json(d, bot) for d in data) def to_json(self) -> str: """Gives a JSON representation of object. diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 768c588967f..48b23b52980 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -514,7 +514,7 @@ async def get_updates( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[Update]: + ) -> Tuple[Update, ...]: updates = await super().get_updates( offset=offset, limit=limit, @@ -1449,7 +1449,7 @@ async def get_chat_administrators( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, rate_limit_args: RLARGS = None, - ) -> List[ChatMember]: + ) -> Tuple[ChatMember, ...]: return await super().get_chat_administrators( chat_id=chat_id, read_timeout=read_timeout, @@ -1556,7 +1556,7 @@ async def get_game_high_scores( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, rate_limit_args: RLARGS = None, - ) -> List[GameHighScore]: + ) -> Tuple[GameHighScore, ...]: return await super().get_game_high_scores( user_id=user_id, chat_id=chat_id, @@ -1598,7 +1598,7 @@ async def get_my_commands( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, rate_limit_args: RLARGS = None, - ) -> List[BotCommand]: + ) -> Tuple[BotCommand, ...]: return await super().get_my_commands( scope=scope, language_code=language_code, @@ -1659,7 +1659,7 @@ async def get_custom_emoji_stickers( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, rate_limit_args: RLARGS = None, - ) -> List[Sticker]: + ) -> Tuple[Sticker, ...]: return await super().get_custom_emoji_stickers( custom_emoji_ids=custom_emoji_ids, read_timeout=read_timeout, @@ -2285,7 +2285,7 @@ async def send_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> List[Message]: + ) -> Tuple[Message, ...]: return await super().send_media_group( chat_id=chat_id, media=media, diff --git a/tests/conftest.py b/tests/conftest.py index b472932d52a..ceb721bc026 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -734,7 +734,7 @@ async def check_defaults_handling( kwargs["tzinfo"] = pytz.timezone("America/New_York") defaults_custom_defaults = Defaults(**kwargs) - expected_return_values = [None, []] if return_value is None else [return_value] + expected_return_values = [None, ()] if return_value is None else [return_value] async def make_assertion( url, request_data: RequestData, df_value=DEFAULT_NONE, *args, **kwargs diff --git a/tests/test_bot.py b/tests/test_bot.py index 5cd42dc5b54..92bf26af8ab 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -1829,7 +1829,7 @@ async def test_get_updates(self, bot): await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = await bot.get_updates(timeout=1) - assert isinstance(updates, list) + assert isinstance(updates, tuple) if updates: assert isinstance(updates[0], Update) @@ -1861,7 +1861,7 @@ async def post(*args, **kwargs): monkeypatch.setattr(BaseRequest, "post", post) updates = await bot.get_updates(timeout=1) - assert isinstance(updates, list) + assert isinstance(updates, tuple) assert len(updates) == 1 assert isinstance(updates[0].callback_query.data, InvalidCallbackData) @@ -1986,7 +1986,7 @@ async def test_get_chat(self, bot, super_group_id): @pytest.mark.flaky(3, 1) async def test_get_chat_administrators(self, bot, channel_id): admins = await bot.get_chat_administrators(channel_id) - assert isinstance(admins, list) + assert isinstance(admins, tuple) for a in admins: assert a.status in ("administrator", "creator") @@ -2738,7 +2738,7 @@ async def test_get_set_chat_menu_button(self, bot, chat_id): async def test_set_and_get_my_commands(self, bot): commands = [BotCommand("cmd1", "descr1"), ["cmd2", "descr2"]] await bot.set_my_commands([]) - assert await bot.get_my_commands() == [] + assert await bot.get_my_commands() == () assert await bot.set_my_commands(commands) for i, bc in enumerate(await bot.get_my_commands()): @@ -3160,7 +3160,7 @@ async def post(*args, **kwargs): await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = await bot.get_updates(timeout=1) - assert isinstance(updates, list) + assert isinstance(updates, tuple) assert len(updates) == 1 effective_message = updates[0][message_type] @@ -3227,7 +3227,7 @@ async def post(*args, **kwargs): await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = await bot.get_updates(timeout=1) - assert isinstance(updates, list) + assert isinstance(updates, tuple) assert len(updates) == 1 message = updates[0][message_type] diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 51b80205c88..0b891075818 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -456,7 +456,7 @@ class TestSendMediaGroup: @pytest.mark.flaky(3, 1) async def test_send_media_group_photo(self, bot, chat_id, media_group): messages = await bot.send_media_group(chat_id, media_group) - assert isinstance(messages, list) + assert isinstance(messages, tuple) assert len(messages) == 3 assert all(isinstance(mes, Message) for mes in messages) assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) @@ -522,7 +522,7 @@ async def test_send_media_group_with_group_caption( assert not any(item.parse_mode for item in media_group_no_caption_args) - assert isinstance(messages, list) + assert isinstance(messages, tuple) assert len(messages) == 3 assert all(isinstance(mes, Message) for mes in messages) @@ -562,7 +562,7 @@ async def test_send_media_group_all_args(self, bot, raw_bot, chat_id, media_grou a.parse_mode == b.parse_mode for a, b in zip(media_group, copied_media_group) ) - assert isinstance(messages, list) + assert isinstance(messages, tuple) assert len(messages) == 3 assert all(isinstance(mes, Message) for mes in messages) assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) @@ -642,7 +642,7 @@ async def func(): func, "Type of file mismatch", "Telegram did not accept the file." ) - assert isinstance(messages, list) + assert isinstance(messages, tuple) assert len(messages) == 3 assert all(isinstance(mes, Message) for mes in messages) assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) diff --git a/tests/test_userprofilephotos.py b/tests/test_userprofilephotos.py index 9b3e3f2c1a0..c8edb0daebe 100644 --- a/tests/test_userprofilephotos.py +++ b/tests/test_userprofilephotos.py @@ -43,7 +43,7 @@ def test_de_json(self, bot): user_profile_photos = UserProfilePhotos.de_json(json_dict, bot) assert user_profile_photos.api_kwargs == {} assert user_profile_photos.total_count == self.total_count - assert user_profile_photos.photos == tuple(self.photos) + assert user_profile_photos.photos == tuple(tuple(p) for p in self.photos) def test_to_dict(self): user_profile_photos = UserProfilePhotos(self.total_count, self.photos) From 3688ff8b925ed8808312d5181e12bb30bd30f1f0 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 15 Nov 2022 19:10:36 +0100 Subject: [PATCH 58/90] Start with the hashes --- telegram/_bot.py | 6 ++++-- telegram/_games/game.py | 3 --- telegram/_inline/inlinekeyboardmarkup.py | 3 --- telegram/_inline/inputinvoicemessagecontent.py | 14 -------------- telegram/_replykeyboardmarkup.py | 10 ---------- telegram/_userprofilephotos.py | 3 --- telegram/_videochat.py | 3 --- tests/test_bot.py | 7 +++++-- 8 files changed, 9 insertions(+), 40 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 05fd09bac59..33ec587246a 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -6785,10 +6785,12 @@ def to_dict(self, recursive: bool = True) -> JSONDict: # skipcq: PYL-W0613 return data def __eq__(self, other: object) -> bool: - return self.bot == other + if isinstance(other, self.__class__): + return self.bot == other.bot + return False def __hash__(self) -> int: - return hash(self.bot) + return hash((self.__class__, self.bot)) # camelCase aliases getMe = get_me diff --git a/telegram/_games/game.py b/telegram/_games/game.py index ddde132d617..582fdfccaec 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -195,6 +195,3 @@ def parse_text_entities(self, types: List[str] = None) -> Dict[MessageEntity, st for entity in self.text_entities if entity.type in types } - - def __hash__(self) -> int: - return hash((self.title, self.description, tuple(p for p in self.photo))) diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 1e661f80f90..4c4566bfaf9 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -141,6 +141,3 @@ def from_column( """ button_grid = [[button] for button in button_column] return cls(button_grid, **kwargs) # type: ignore[arg-type] - - def __hash__(self) -> int: - return hash(tuple(tuple(button for button in row) for row in self.inline_keyboard)) diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 903c9ec1d6f..678486f0142 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -233,20 +233,6 @@ def __init__( self._freeze() - def __hash__(self) -> int: - # we override this as self.prices is a list and not hashable - prices = tuple(self.prices) - return hash( - ( - self.title, - self.description, - self.payload, - self.provider_token, - self.currency, - prices, - ) - ) - @classmethod def de_json( cls, data: Optional[JSONDict], bot: "Bot" diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index a6ef6f88c64..10b5afa8cba 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -279,13 +279,3 @@ def from_column( input_field_placeholder=input_field_placeholder, **kwargs, # type: ignore[arg-type] ) - - def __hash__(self) -> int: - return hash( - ( - tuple(tuple(button for button in row) for row in self.keyboard), - self.resize_keyboard, - self.one_time_keyboard, - self.selective, - ) - ) diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 01b9c79a57d..ef1af00a475 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -75,6 +75,3 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["UserProfileP data["photos"] = [PhotoSize.de_list(photo, bot) for photo in data["photos"]] return super().de_json(data=data, bot=bot) - - def __hash__(self) -> int: - return hash(tuple(tuple(p for p in photo) for photo in self.photos)) diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 7308a6e7c24..8803c2bab04 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -130,9 +130,6 @@ def de_json( data["users"] = User.de_list(data.get("users", []), bot) return super().de_json(data=data, bot=bot) - def __hash__(self) -> int: - return hash(None) if self.users is None else hash(tuple(self.users)) - class VideoChatScheduled(TelegramObject): """This object represents a service message about a video chat scheduled in the chat. diff --git a/tests/test_bot.py b/tests/test_bot.py index 92bf26af8ab..527354ac0e4 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -400,8 +400,8 @@ async def test_get_me_and_properties_not_initialized(self, bot: Bot, attribute): async def test_equality(self): async with make_bot(token=FALLBACKS[0]["token"]) as a, make_bot( token=FALLBACKS[0]["token"] - ) as b, make_bot(token=FALLBACKS[1]["token"]) as c: - d = Update(123456789) + ) as b, make_bot(token=FALLBACKS[1]["token"]) as c, Bot(token=FALLBACKS[0]["token"]) as d: + e = Update(123456789) assert a == b assert hash(a) == hash(b) @@ -413,6 +413,9 @@ async def test_equality(self): assert a != d assert hash(a) != hash(d) + assert a != e + assert hash(a) != hash(e) + @pytest.mark.flaky(3, 1) async def test_to_dict(self, bot): to_dict_bot = bot.to_dict() From 9a17d488e53d229116750f050879afb3308f0a89 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 18 Nov 2022 15:53:58 +0100 Subject: [PATCH 59/90] handle nested lists --- immutabilize.py | 2 +- telegram/_inline/inlinekeyboardmarkup.py | 16 ++++++++-------- telegram/_replykeyboardmarkup.py | 22 +++++++++------------- telegram/_telegramobject.py | 2 +- telegram/_userprofilephotos.py | 17 +++++++++++------ telegram/_utils/markup.py | 8 ++++++-- tests/test_inlinekeyboardmarkup.py | 7 ++++++- tests/test_replykeyboardmarkup.py | 11 ++++++++--- 8 files changed, 50 insertions(+), 35 deletions(-) diff --git a/immutabilize.py b/immutabilize.py index 9766c659c18..3e5f900f2ad 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -98,7 +98,7 @@ continue class_source_lines[i] = class_source_lines[i].replace( - f"{param} (List[", f"{param} (Sequence[" + f"{param} (List[", f"{param} (Tuple[" ) whitespaces = re.match(r" +", class_source_lines[i]).end() + 4 j = i + 1 diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 4c4566bfaf9..61dd4c1feee 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -40,15 +40,15 @@ class InlineKeyboardMarkup(TelegramObject): * :any:`Inline Keyboard 2 ` Args: - inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button - rows, each represented by a list of InlineKeyboardButton objects. + inline_keyboard (Sequence[Sequence[:class:`telegram.InlineKeyboardButton`]]): Sequence of + button rows, each represented by a sequence of InlineKeyboardButton objects. .. versionchanged:: 20.0 |squenceclassargs| Attributes: - inline_keyboard (Sequence[List[:class:`telegram.InlineKeyboardButton`]]): List of button - rows, each represented by a list of InlineKeyboardButton objects. + inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of + button rows, each represented by a tuple of InlineKeyboardButton objects. .. versionchanged:: 20.0 |tupleclassattrs| @@ -59,18 +59,18 @@ class InlineKeyboardMarkup(TelegramObject): def __init__( self, - inline_keyboard: Sequence[List[InlineKeyboardButton]], + inline_keyboard: Sequence[Sequence[InlineKeyboardButton]], *, api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) if not check_keyboard_type(inline_keyboard): raise ValueError( - "The parameter `inline_keyboard` should be a list of " - "list of InlineKeyboardButtons" + "The parameter `keyboard` should be a sequence of sequences of " + "strings or KeyboardButtons" ) # Required - self.inline_keyboard = tuple(inline_keyboard) + self.inline_keyboard = tuple(tuple(row) for row in inline_keyboard) self._id_attrs = (self.inline_keyboard,) diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 10b5afa8cba..c02d5e54f1d 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -40,8 +40,8 @@ class ReplyKeyboardMarkup(TelegramObject): * :any:`Conversation Bot 2 ` Args: - keyboard (List[List[:obj:`str` | :class:`telegram.KeyboardButton`]]): Array of button rows, - each represented by an Array of :class:`telegram.KeyboardButton` objects. + keyboard (Sequence[Sequence[:obj:`str` | :class:`telegram.KeyboardButton`]]): Array of + button rows, each represented by an Array of :class:`telegram.KeyboardButton` objects. resize_keyboard (:obj:`bool`, optional): Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to :obj:`False`, in which case the custom keyboard is always of the @@ -66,7 +66,8 @@ class ReplyKeyboardMarkup(TelegramObject): .. versionadded:: 13.7 Attributes: - keyboard (List[List[:class:`telegram.KeyboardButton` | :obj:`str`]]): Array of button rows. + keyboard (Tuple[Tuple[:class:`telegram.KeyboardButton` | :obj:`str`]]): Array of button + rows. resize_keyboard (:obj:`bool`): Optional. Requests clients to resize the keyboard. one_time_keyboard (:obj:`bool`): Optional. Requests clients to hide the keyboard as soon as it's been used. @@ -99,20 +100,15 @@ def __init__( super().__init__(api_kwargs=api_kwargs) if not check_keyboard_type(keyboard): raise ValueError( - "The parameter `keyboard` should be a list of list of " + "The parameter `keyboard` should be a sequence of sequences of " "strings or KeyboardButtons" ) # Required - self.keyboard = [] - for row in keyboard: - button_row = [] - for button in row: - if isinstance(button, KeyboardButton): - button_row.append(button) # telegram.KeyboardButton - else: - button_row.append(KeyboardButton(button)) # str - self.keyboard.append(button_row) + self.keyboard = tuple( + tuple(KeyboardButton(button) if isinstance(button, str) else button for button in row) + for row in keyboard + ) # Optionals self.resize_keyboard = resize_keyboard diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index db142a9b634..f511dbe1124 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -465,7 +465,7 @@ def to_dict(self, recursive: bool = True) -> JSONDict: for item in value: if hasattr(item, "to_dict"): val.append(item.to_dict(recursive=recursive)) - # This branch is useful for e.g. List[List[PhotoSize|KeyboardButton]] + # This branch is useful for e.g. Tuple[Tuple[PhotoSize|KeyboardButton]] elif isinstance(item, (tuple, list)): val.append( [ diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index ef1af00a475..763024c5bc6 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -17,7 +17,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/]. """This module contains an object that represents a Telegram UserProfilePhotos.""" -from typing import TYPE_CHECKING, List, Optional, Sequence +from typing import TYPE_CHECKING, Optional, Sequence from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject @@ -35,15 +35,16 @@ class UserProfilePhotos(TelegramObject): Args: total_count (:obj:`int`): Total number of profile pictures the target user has. - photos (Sequence[List[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to - 4 sizes each). + photos (Sequence[Sequence[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up + to 4 sizes each). .. versionchanged:: 20.0 |squenceclassargs| Attributes: total_count (:obj:`int`): Total number of profile pictures. - photos (Sequence[List[:class:`telegram.PhotoSize`]]): Requested profile pictures. + photos (Tuple[Tuple[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up + to 4 sizes each). .. versionchanged:: 20.0 |tupleclassattrs| @@ -53,12 +54,16 @@ class UserProfilePhotos(TelegramObject): __slots__ = ("photos", "total_count") def __init__( - self, total_count: int, photos: Sequence[List[PhotoSize]], *, api_kwargs: JSONDict = None + self, + total_count: int, + photos: Sequence[Sequence[PhotoSize]], + *, + api_kwargs: JSONDict = None, ): super().__init__(api_kwargs=api_kwargs) # Required self.total_count = total_count - self.photos = tuple(photos) + self.photos = tuple(tuple(sizes) for sizes in photos) self._id_attrs = (self.total_count, self.photos) diff --git a/telegram/_utils/markup.py b/telegram/_utils/markup.py index 888937ba601..50a94fc5c25 100644 --- a/telegram/_utils/markup.py +++ b/telegram/_utils/markup.py @@ -27,15 +27,19 @@ class ``telegram.ReplyMarkup``. user. Changes to this module are not considered breaking changes and may not be documented in the changelog. """ +from collections.abc import Sequence def check_keyboard_type(keyboard: object) -> bool: """Checks if the keyboard provided is of the correct type - A list of lists. Implicitly tested in the init-tests of `{Inline, Reply}KeyboardMarkup` """ - if not isinstance(keyboard, list): + # string and bytes may actually be used for ReplyKeyboardMarkup in which case each button + # would contain a single character. But that use case should be discouraged and we don't + # allow it here. + if not isinstance(keyboard, Sequence) or isinstance(keyboard, (str, bytes)): return False for row in keyboard: - if not isinstance(row, list): + if not isinstance(row, Sequence) or isinstance(keyboard, (str, bytes)): return False return True diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py index fecad81eadf..e1b404e4787 100644 --- a/tests/test_inlinekeyboardmarkup.py +++ b/tests/test_inlinekeyboardmarkup.py @@ -86,13 +86,18 @@ def test_from_column(self): assert len(inline_keyboard_markup[1]) == 1 def test_expected_values(self, inline_keyboard_markup): - assert inline_keyboard_markup.inline_keyboard == tuple(self.inline_keyboard) + assert inline_keyboard_markup.inline_keyboard == tuple( + tuple(row) for row in self.inline_keyboard + ) def test_wrong_keyboard_inputs(self): with pytest.raises(ValueError): InlineKeyboardMarkup( [[InlineKeyboardButton("b1", "1")], InlineKeyboardButton("b2", "2")] ) + with pytest.raises(ValueError): + InlineKeyboardMarkup("strings_are_not_allowed") + InlineKeyboardMarkup(["strings_are_not_allowed"], ["in_the_rows_either"]) with pytest.raises(ValueError): InlineKeyboardMarkup(InlineKeyboardButton("b1", "1")) diff --git a/tests/test_replykeyboardmarkup.py b/tests/test_replykeyboardmarkup.py index b33046fed4a..5a8ca505722 100644 --- a/tests/test_replykeyboardmarkup.py +++ b/tests/test_replykeyboardmarkup.py @@ -96,7 +96,9 @@ def test_from_column(self): assert len(reply_keyboard_markup[1]) == 1 def test_expected_values(self, reply_keyboard_markup): - assert isinstance(reply_keyboard_markup.keyboard, list) + assert isinstance(reply_keyboard_markup.keyboard, tuple) + for row in reply_keyboard_markup.keyboard: + assert isinstance(row, tuple) assert isinstance(reply_keyboard_markup.keyboard[0][0], KeyboardButton) assert isinstance(reply_keyboard_markup.keyboard[0][1], KeyboardButton) assert reply_keyboard_markup.resize_keyboard == self.resize_keyboard @@ -105,9 +107,12 @@ def test_expected_values(self, reply_keyboard_markup): def test_wrong_keyboard_inputs(self): with pytest.raises(ValueError): - ReplyKeyboardMarkup([["button1"], "Button2"]) + ReplyKeyboardMarkup([["button1"], 1]) with pytest.raises(ValueError): - ReplyKeyboardMarkup("button") + ReplyKeyboardMarkup("strings_are_not_allowed") + ReplyKeyboardMarkup(["strings_are_not_allowed"], ["in_the_rows_either"]) + with pytest.raises(ValueError): + ReplyKeyboardMarkup(KeyboardButton("button1")) def test_to_dict(self, reply_keyboard_markup): reply_keyboard_markup_dict = reply_keyboard_markup.to_dict() From 3cdb7189bf679bf6ffa48f77977323594e2cba4e Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 18 Nov 2022 15:58:54 +0100 Subject: [PATCH 60/90] Fix docstrings for attributes --- telegram/_files/inputmedia.py | 12 ++++++------ telegram/_files/sticker.py | 2 +- telegram/_games/game.py | 4 ++-- telegram/_inline/inlinequeryresultaudio.py | 2 +- telegram/_inline/inlinequeryresultcachedaudio.py | 2 +- telegram/_inline/inlinequeryresultcacheddocument.py | 2 +- telegram/_inline/inlinequeryresultcachedgif.py | 2 +- telegram/_inline/inlinequeryresultcachedmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultcachedphoto.py | 2 +- telegram/_inline/inlinequeryresultcachedvideo.py | 2 +- telegram/_inline/inlinequeryresultcachedvoice.py | 2 +- telegram/_inline/inlinequeryresultdocument.py | 2 +- telegram/_inline/inlinequeryresultgif.py | 2 +- telegram/_inline/inlinequeryresultmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultphoto.py | 2 +- telegram/_inline/inlinequeryresultvideo.py | 2 +- telegram/_inline/inlinequeryresultvoice.py | 2 +- telegram/_inline/inputinvoicemessagecontent.py | 4 ++-- telegram/_inline/inputtextmessagecontent.py | 2 +- telegram/_message.py | 10 +++++----- telegram/_passport/credentials.py | 4 ++-- telegram/_passport/encryptedpassportelement.py | 4 ++-- telegram/_passport/passportdata.py | 2 +- telegram/_payment/shippingoption.py | 2 +- telegram/_poll.py | 6 +++--- telegram/_videochat.py | 2 +- telegram/_webhookinfo.py | 2 +- 27 files changed, 42 insertions(+), 42 deletions(-) diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 579fbe3a7f7..d9cb436d5d4 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -71,7 +71,7 @@ class InputMedia(TelegramObject): media (:obj:`str` | :class:`telegram.InputFile`): Media to send. caption (:obj:`str`): Optional. Caption of the media to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. .. versionchanged:: 20.0 @@ -156,7 +156,7 @@ class InputMediaAnimation(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Animation to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. .. versionchanged:: 20.0 @@ -245,7 +245,7 @@ class InputMediaPhoto(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Photo to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. .. versionchanged:: 20.0 @@ -332,7 +332,7 @@ class InputMediaVideo(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Video file to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. .. versionchanged:: 20.0 @@ -441,7 +441,7 @@ class InputMediaAudio(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): Audio file to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. .. versionchanged:: 20.0 @@ -540,7 +540,7 @@ class InputMediaDocument(InputMedia): media (:obj:`str` | :class:`telegram.InputFile`): File to send. caption (:obj:`str`): Optional. Caption of the document to be sent. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption. .. versionchanged:: 20.0 diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 4daadedc672..50b6b1a3d2f 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -228,7 +228,7 @@ class StickerSet(TelegramObject): is_video (:obj:`bool`): :obj:`True`, if the sticker set contains video stickers. .. versionadded:: 13.11 - stickers (Sequence[:class:`telegram.Sticker`]): List of all set stickers. + stickers (Tuple[:class:`telegram.Sticker`]): List of all set stickers. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 582fdfccaec..bcbabcba66c 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -64,7 +64,7 @@ class Game(TelegramObject): Attributes: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. - photo (Sequence[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game + photo (Tuple[:class:`telegram.PhotoSize`]): Photo that will be displayed in the game message in chats. .. versionchanged:: 20.0 @@ -74,7 +74,7 @@ class Game(TelegramObject): game message. Can be automatically edited to include current high scores for the game when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited using :meth:`telegram.Bot.edit_message_text`. - text_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities that + text_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities that appear in text, such as usernames, URLs, bot commands, etc. This list is empty if the message does not contain text entities. diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 302368c57d0..8701b6690d9 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -73,7 +73,7 @@ class InlineQueryResultAudio(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 9f7fed5b836..27a17dee1c5 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -67,7 +67,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 5ab7b8c4ec9..1b4c8731a9a 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -71,7 +71,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption.. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 7e949730b4a..65f09651344 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -70,7 +70,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index ffd194c735d..5d730fdd89b 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -70,7 +70,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index ca28c74ffef..07fb3d5d654 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -72,7 +72,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index da8077fa548..f5b77f899bc 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -72,7 +72,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 000c3f8e9d8..ed6c65b61e4 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -69,7 +69,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index c0941ddcbf1..8265b53d654 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -75,7 +75,7 @@ class InlineQueryResultDocument(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 04dad8ac075..ff766dc1bfa 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -82,7 +82,7 @@ class InlineQueryResultGif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 879ef4ebf42..e942cbf6f81 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -82,7 +82,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 5a02f13b246..cb811ae473b 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -79,7 +79,7 @@ class InlineQueryResultPhoto(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 767eb4ccf8c..32bb8aee9e7 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -84,7 +84,7 @@ class InlineQueryResultVideo(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index 71c3305f8dd..a151f175ce8 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -71,7 +71,7 @@ class InlineQueryResultVoice(InlineQueryResult): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 678486f0142..31ad6c7a8b0 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -111,7 +111,7 @@ class InputInvoiceMessageContent(InputMessageContent): `@Botfather `_. currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on `currencies `_ - prices (Sequence[:class:`telegram.LabeledPrice`]): Price breakdown, a list of + prices (Tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components. .. versionchanged:: 20.0 @@ -119,7 +119,7 @@ class InputInvoiceMessageContent(InputMessageContent): max_tip_amount (:obj:`int`): Optional. The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). - suggested_tip_amounts (Sequence[:obj:`int`]): Optional. An array of suggested + suggested_tip_amounts (Tuple[:obj:`int`]): Optional. An array of suggested amounts of tip in the smallest units of the currency (integer, not float/double). .. versionchanged:: 20.0 diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index fd8318ed2d8..cdc36a30afd 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -59,7 +59,7 @@ class InputTextMessageContent(InputMessageContent): parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. See the constants in :class:`telegram.constants.ParseMode` for the available modes. - entities (Sequence[:class:`telegram.MessageEntity`]): Optional. List of special + entities (Tuple[:class:`telegram.MessageEntity`]): Optional. List of special entities that appear in the caption, which can be specified instead of :paramref:`parse_mode`. diff --git a/telegram/_message.py b/telegram/_message.py index dd098b6ec4d..e89d950abf0 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -308,7 +308,7 @@ class Message(TelegramObject): message belongs to. text (:obj:`str`): Optional. For text messages, the actual UTF-8 text of the message, 0-:tg-const:`telegram.constants.MessageLimit.TEXT_LENGTH` characters. - entities (Sequence[:class:`telegram.MessageEntity`]): Optional. For text messages, special + entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text. See :attr:`parse_entity` and :attr:`parse_entities` methods for how to use properly. This list is empty if the message does not contain entities. @@ -316,7 +316,7 @@ class Message(TelegramObject): .. versionchanged:: 20.0 |tupleclassattrs| - caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. For messages with a + caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the caption. See :attr:`Message.parse_caption_entity` and :attr:`parse_caption_entities` methods for how to use properly. This list is empty if the message does not contain @@ -333,7 +333,7 @@ class Message(TelegramObject): about the animation. For backward compatibility, when this field is set, the document field will also be set. game (:class:`telegram.Game`): Optional. Message is a game, information about the game. - photo (Sequence[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available + photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available sizes of the photo. This list is empty if the message does not contain a photo. .. versionchanged:: 20.0 @@ -347,7 +347,7 @@ class Message(TelegramObject): the file. video_note (:class:`telegram.VideoNote`): Optional. Message is a video note, information about the video message. - new_chat_members (Sequence[:class:`telegram.User`]): Optional. New members that were added + new_chat_members (Tuple[:class:`telegram.User`]): Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members). This list is empty if the message does not contain new chat members. @@ -366,7 +366,7 @@ class Message(TelegramObject): left_chat_member (:class:`telegram.User`): Optional. A member was removed from the group, information about them (this member may be the bot itself). new_chat_title (:obj:`str`): Optional. A chat title was changed to this value. - new_chat_photo (Sequence[:class:`telegram.PhotoSize`]): A chat photo was changed to + new_chat_photo (Tuple[:class:`telegram.PhotoSize`]): A chat photo was changed to this value. This list is empty if the message does not contain a new chat photo. .. versionchanged:: 20.0 diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 742d5c809e2..9e231993547 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -362,7 +362,7 @@ class SecureValue(TelegramObject): selfie (:class:`telegram.FileCredentials`, optional): Credentials for encrypted selfie of the user with a document. Can be available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (Sequence[:class:`telegram.FileCredentials`], optional): Credentials for an + translation (Tuple[:class:`telegram.FileCredentials`], optional): Credentials for an encrypted translation of the document. Available for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration". @@ -370,7 +370,7 @@ class SecureValue(TelegramObject): .. versionchanged:: 20.0 |tupleclassattrs| - files (Sequence[:class:`telegram.FileCredentials`], optional): Credentials for encrypted + files (Tuple[:class:`telegram.FileCredentials`], optional): Credentials for encrypted files. Available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index da3e50e655a..35d8c94fad9 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -100,7 +100,7 @@ class EncryptedPassportElement(TelegramObject): "phone_number" type. email (:obj:`str`): Optional. User's verified email address, available only for "email" type. - files (Sequence[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted + files (Tuple[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. @@ -117,7 +117,7 @@ class EncryptedPassportElement(TelegramObject): selfie (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the selfie of the user holding a document, provided by the user; available for "passport", "driver_license", "identity_card" and "internal_passport". - translation (Sequence[:class:`telegram.PassportFile`]): Optional. Array of + translation (Tuple[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files with translated versions of documents provided by the user. Available if requested for "passport", "driver_license", "identity_card", "internal_passport", diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 6a9d8af8150..7f66e66b1ad 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -48,7 +48,7 @@ class PassportData(TelegramObject): credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. Attributes: - data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted + data (Tuple[:class:`telegram.EncryptedPassportElement`]): Array with encrypted information about documents and other Telegram Passport elements that was shared with the bot. diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index 38d087025cd..cfdc2087ee2 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -46,7 +46,7 @@ class ShippingOption(TelegramObject): Attributes: id (:obj:`str`): Shipping option identifier. title (:obj:`str`): Option title. - prices (Sequence[:class:`telegram.LabeledPrice`]): List of price portions. + prices (Tuple[:class:`telegram.LabeledPrice`]): List of price portions. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_poll.py b/telegram/_poll.py index bca82885c20..a0f9b8c8b39 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -84,7 +84,7 @@ class PollAnswer(TelegramObject): Attributes: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (Sequence[:obj:`int`]): Identifiers of answer options, chosen by the user. + option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. .. versionchanged:: 20.0 |tupleclassattrs| @@ -161,7 +161,7 @@ class Poll(TelegramObject): Attributes: id (:obj:`str`): Unique poll identifier. question (:obj:`str`): Poll question, 1-300 characters. - options (Sequence[:class:`PollOption`]): List of poll options. + options (Tuple[:class:`PollOption`]): List of poll options. .. versionchanged:: 20.0 |tupleclassattrs| @@ -176,7 +176,7 @@ class Poll(TelegramObject): (not forwarded), by the bot or to a private chat with the bot. explanation (:obj:`str`): Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters. - explanation_entities (Sequence[:class:`telegram.MessageEntity`]): Special entities + explanation_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. This list is empty if the message does not contain explanation entities. diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 8803c2bab04..17c192ee924 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -96,7 +96,7 @@ class VideoChatParticipantsInvited(TelegramObject): |squenceclassargs| Attributes: - users (Sequence[:class:`telegram.User`]): New members that were invited to the video chat. + users (Tuple[:class:`telegram.User`]): New members that were invited to the video chat. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index b6fce11887f..00a1d50d33e 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -77,7 +77,7 @@ class WebhookInfo(TelegramObject): most recent error that happened when trying to deliver an update via webhook. max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery. - allowed_updates (Sequence[:obj:`str`]): Optional. A list of update types the bot is + allowed_updates (Tuple[:obj:`str`]): Optional. A list of update types the bot is subscribed to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. From 46d4433f49df8a9a889f1a09ccaf58fd5cd33677 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 18 Nov 2022 16:04:55 +0100 Subject: [PATCH 61/90] doc fixes --- telegram/_passport/passportfile.py | 2 +- telegram/_telegramobject.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 2c3cc23d597..39deba8cb34 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -121,7 +121,7 @@ def de_list_decrypted( """Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account passport credentials. - .. versionchange:: 20.0 + .. versionchanged:: 20.0 Returns a tuple instead of a list. Args: diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index f511dbe1124..9b4487e881e 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -411,7 +411,7 @@ def de_list( ) -> Tuple[Optional[Tele_co], ...]: """Converts a list of JSON objects to a tuple of Telegram objects. - .. versionchange:: 20.0 + .. versionchanged:: 20.0 Returns a tuple instead of a list. Args: From 408d7eb6b358eedffab3f04420b550354f253c8f Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 18 Nov 2022 16:05:44 +0100 Subject: [PATCH 62/90] silence DS on transition script --- immutabilize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/immutabilize.py b/immutabilize.py index 3e5f900f2ad..c944cdcb7b3 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -28,7 +28,7 @@ print("Processing class", name) # first adjust the __init__ of the class params = inspect.signature(cls.__init__).parameters - params_to_change = dict() + params_to_change = {} for param in params.values(): if "List" in str(param.annotation): print(" Converting list-type parameter", param.name, "to Sequence") From 58d099673c8edbc1a7e92e0b8a6d09e03d302635 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 18 Nov 2022 16:50:06 +0100 Subject: [PATCH 63/90] =?UTF-8?q?fix=20a=20test=20that=20hid=20in=20timeou?= =?UTF-8?q?t=20errors=20so=20far=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_bot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_bot.py b/tests/test_bot.py index 527354ac0e4..d46e4eeb5cf 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -868,11 +868,11 @@ async def test_send_poll_default_parse_mode(self, default_bot, super_group_id): explanation=explanation_markdown, ) assert message.poll.explanation == explanation - assert message.poll.explanation_entities == [ + assert message.poll.explanation_entities == ( MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.BOLD, 7, 4), MessageEntity(MessageEntity.CODE, 12, 4), - ] + ) message = await default_bot.send_poll( chat_id=super_group_id, @@ -885,7 +885,7 @@ async def test_send_poll_default_parse_mode(self, default_bot, super_group_id): explanation_parse_mode=None, ) assert message.poll.explanation == explanation_markdown - assert message.poll.explanation_entities == [] + assert message.poll.explanation_entities == () message = await default_bot.send_poll( chat_id=super_group_id, @@ -898,7 +898,7 @@ async def test_send_poll_default_parse_mode(self, default_bot, super_group_id): explanation_parse_mode="HTML", ) assert message.poll.explanation == explanation_markdown - assert message.poll.explanation_entities == [] + assert message.poll.explanation_entities == () @pytest.mark.flaky(3, 1) @pytest.mark.parametrize( From ee9e47efd01040724e00593b6e7cda3ca1f54b0d Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 25 Nov 2022 12:52:33 +0100 Subject: [PATCH 64/90] fix a few tests --- telegram/_forumtopic.py | 4 ++++ tests/test_chat.py | 4 ++-- tests/test_inputmedia.py | 2 +- tests/test_telegramobject.py | 1 + 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/telegram/_forumtopic.py b/telegram/_forumtopic.py index 3cf3fe55be7..38684118dac 100644 --- a/telegram/_forumtopic.py +++ b/telegram/_forumtopic.py @@ -66,6 +66,8 @@ def __init__( self._id_attrs = (self.message_thread_id, self.name, self.icon_color) + self._freeze() + class ForumTopicCreated(TelegramObject): """ @@ -107,6 +109,8 @@ def __init__( self._id_attrs = (self.name, self.icon_color) + self._freeze() + class ForumTopicClosed(TelegramObject): """ diff --git a/tests/test_chat.py b/tests/test_chat.py index bcef0d8060c..ebc7e02c3f7 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -135,7 +135,7 @@ def test_de_json(self, bot): "all_members_are_administrators": self.all_members_are_administrators } assert chat.is_forum == self.is_forum - assert chat.active_usernames == self.active_usernames + assert chat.active_usernames == tuple(self.active_usernames) assert chat.emoji_status_custom_emoji_id == self.emoji_status_custom_emoji_id def test_to_dict(self, chat): @@ -160,7 +160,7 @@ def test_to_dict(self, chat): == chat.has_restricted_voice_and_video_messages ) assert chat_dict["is_forum"] == chat.is_forum - assert chat_dict["active_usernames"] == chat.active_usernames + assert chat_dict["active_usernames"] == list(chat.active_usernames) assert chat_dict["emoji_status_custom_emoji_id"] == chat.emoji_status_custom_emoji_id def test_enum_init(self): diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 6d591250163..4d397f524a5 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -476,7 +476,7 @@ async def test_send_media_group_with_message_thread_id( media_group, message_thread_id=real_topic.message_thread_id, ) - assert isinstance(messages, list) + assert isinstance(messages, tuple) assert len(messages) == 3 assert all(isinstance(mes, Message) for mes in messages) assert all(i.message_thread_id == real_topic.message_thread_id for i in messages) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index dd868be6d10..3978ffc58f5 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -154,6 +154,7 @@ def test_to_dict_missing_attribute(self): message = Message( 1, datetime.datetime.now(), Chat(1, "private"), from_user=User(1, "", False) ) + message._unfreeze() del message.chat message_dict = message.to_dict() From dbccb6d56274e39cbbba1d5ef182d203edb7e6e6 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 25 Nov 2022 13:04:13 +0100 Subject: [PATCH 65/90] fix pickle-related tests --- telegram/_telegramobject.py | 14 +++++++++++--- telegram/ext/_picklepersistence.py | 4 ---- tests/test_telegramobject.py | 7 ++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 9fa92e7643e..ad27eac1017 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -162,7 +162,7 @@ def __repr__(self) -> str: # Drop api_kwargs from the representation, if empty as_dict.pop("api_kwargs", None) else: - # Otherwise, we want want to skip the "mappingproxy" part of the repr + # Otherwise, we want to skip the "mappingproxy" part of the repr as_dict["api_kwargs"] = dict(self.api_kwargs) contents = ", ".join( @@ -276,7 +276,9 @@ def __setstate__(self, state: dict) -> None: setattr(self, "api_kwargs", MappingProxyType(api_kwargs)) # Apply freezing if necessary - if state["_frozen"]: + # we .get(…) the setting for backwards compatibility with objects that were pickled + # before the freeze feature was introduced + if state.get("_frozen", False): self._freeze() def __deepcopy__(self: Tele_co, memodict: dict) -> Tele_co: @@ -313,7 +315,13 @@ def __deepcopy__(self: Tele_co, memodict: dict) -> Tele_co: setattr(result, k, MappingProxyType(deepcopy(dict(self.api_kwargs), memodict))) continue - setattr(result, k, deepcopy(getattr(self, k), memodict)) + try: + setattr(result, k, deepcopy(getattr(self, k), memodict)) + except AttributeError: + # Skip missing attributes. This can happen if the object was loaded from a pickle + # file that was created with an older version of the library, where the class + # did not have the attribute yet. + continue # Apply freezing if necessary if self._frozen: diff --git a/telegram/ext/_picklepersistence.py b/telegram/ext/_picklepersistence.py index 888c39f06cc..1d0c216d082 100644 --- a/telegram/ext/_picklepersistence.py +++ b/telegram/ext/_picklepersistence.py @@ -22,7 +22,6 @@ from copy import deepcopy from pathlib import Path from sys import version_info as py_ver -from types import MappingProxyType from typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, cast, overload from telegram import Bot, TelegramObject @@ -54,9 +53,6 @@ def _reconstruct_to(cls: Type[TelegramObj], kwargs: dict) -> TelegramObj: is changed, since `_custom_reduction` places references to this function into the pickled data. """ obj = cls.__new__(cls) - # Converting to MappingProxyType - # is necessary, since _custom_reduction converts it to a dict as MPT is not pickable. - kwargs["api_kwargs"] = MappingProxyType(kwargs["api_kwargs"]) obj.__setstate__(kwargs) return obj diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 3978ffc58f5..eb22dd35200 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -290,7 +290,7 @@ def test_pickle_apply_api_kwargs(self): assert obj.foo == "bar" assert obj.api_kwargs == {} - async def test_pickle_removed_and_added_attribute(self): + async def test_pickle_backwards_compatibility(self): """Test when newer versions of the library remove or add attributes from classes (which the old pickled versions still/don't have). """ @@ -316,6 +316,11 @@ async def test_pickle_removed_and_added_attribute(self): # New attribute should not be available either as is always the case for pickle chat.is_forum + # Ensure that loading objects that were pickled before attributes were made immutable + # are still mutable + chat.id = 7 + assert chat.id == 7 + def test_deepcopy_telegram_obj(self, bot): chat = Chat(2, Chat.PRIVATE) user = User(3, "first_name", False) From 373d1798a820ef474ba8fa4b23487010fdf68f37 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 25 Nov 2022 13:06:29 +0100 Subject: [PATCH 66/90] add CSI comment --- telegram/ext/_picklepersistence.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telegram/ext/_picklepersistence.py b/telegram/ext/_picklepersistence.py index 1d0c216d082..42eb3241123 100644 --- a/telegram/ext/_picklepersistence.py +++ b/telegram/ext/_picklepersistence.py @@ -63,8 +63,8 @@ def _custom_reduction(cls: TelegramObj) -> Tuple[Callable, Tuple[Type[TelegramOb works as intended. """ data = cls._get_attrs(include_private=True) # pylint: disable=protected-access - # MappingProxyType is not pickable, so we convert it to a dict and revert in - # _reconstruct_to + # MappingProxyType is not pickable, so we convert it to a dict + # no need to convert back to MPT in _reconstruct_to, since it's done in __setstate__ data["api_kwargs"] = dict(data["api_kwargs"]) # type: ignore[arg-type] return _reconstruct_to, (cls.__class__, data) From 8b767addf9325cd83982f5c013cd21ef61d52f95 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 27 Nov 2022 20:50:53 +0100 Subject: [PATCH 67/90] a few doc fixes --- telegram/_inline/inlinekeyboardmarkup.py | 6 ++++-- telegram/_inline/inlinequeryresultvideo.py | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 61dd4c1feee..96d6d4167fa 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -41,14 +41,16 @@ class InlineKeyboardMarkup(TelegramObject): Args: inline_keyboard (Sequence[Sequence[:class:`telegram.InlineKeyboardButton`]]): Sequence of - button rows, each represented by a sequence of InlineKeyboardButton objects. + button rows, each represented by a sequence of :class:~`telegram.InlineKeyboardButton` + objects. .. versionchanged:: 20.0 |squenceclassargs| Attributes: inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of - button rows, each represented by a tuple of InlineKeyboardButton objects. + button rows, each represented by a tuple of :class:~`telegram.InlineKeyboardButton` + objects. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 4e968a659b5..d167c4ad671 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -78,22 +78,22 @@ class InlineQueryResultVideo(InlineQueryResult): mime_type (:obj:`str`): Mime type of the content of video url, "text/html" or "video/mp4". thumb_url (:obj:`str`): URL of the thumbnail (JPEG only) for the video. title (:obj:`str`): Title for the result. - caption (:obj:`str`, optional): Caption of the video to be sent, + caption (:obj:`str`): Optional. Caption of the video to be sent, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 |squenceclassargs| - video_width (:obj:`int`,: VOptional. ideo width. + video_width (:obj:`int`): Optional. Video width. video_height (:obj:`int`): Optional. Video height. video_duration (:obj:`int`): Optional. Video duration in seconds. description (:obj:`str`): Optional. Short description of the result. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. - input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the + input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). From a260699cd62f431feb8642c4e26e318dc4742e6d Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 27 Nov 2022 21:14:20 +0100 Subject: [PATCH 68/90] add a context manager to unfreeze TO --- telegram/_telegramobject.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index ad27eac1017..2b676792488 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -21,6 +21,7 @@ import inspect import json from collections.abc import Sized +from contextlib import contextmanager from copy import deepcopy from itertools import chain from types import MappingProxyType @@ -94,12 +95,26 @@ def __init__(self, *, api_kwargs: JSONDict = None) -> None: # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs self.api_kwargs: Mapping[str, Any] = MappingProxyType(api_kwargs or {}) + self._freeze() + def _freeze(self) -> None: self._frozen = True def _unfreeze(self) -> None: self._frozen = False + @contextmanager + def _unfrozen(self: Tele_co) -> Iterator[Tele_co]: + """Context manager to temporarily unfreeze the object. For internal use only. + + Note: + with to._unfrozen() as other_to: + assert to is other_to + """ + self._unfreeze() + yield self + self._freeze() + def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None: """Loops through the api kwargs and for every key that exists as attribute of the object (and is None), it moves the value from `api_kwargs` to the attribute. From 53d91c792f956edad3d27823a9a86bcf006ada46 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 27 Nov 2022 22:19:55 +0100 Subject: [PATCH 69/90] use the new context manager --- telegram/_bot.py | 48 +++++------ telegram/_botcommandscope.py | 38 ++++----- telegram/_chatmember.py | 72 ++++++++--------- telegram/_files/_basethumbedmedium.py | 1 - telegram/_files/animation.py | 18 ++--- telegram/_files/audio.py | 18 ++--- telegram/_files/document.py | 10 +-- telegram/_files/inputmedia.py | 46 +++++------ telegram/_files/photosize.py | 10 +-- telegram/_files/sticker.py | 28 +++---- telegram/_files/video.py | 18 ++--- telegram/_files/videonote.py | 10 +-- telegram/_files/voice.py | 12 ++- telegram/_inline/inlinekeyboardbutton.py | 7 +- telegram/_inline/inlinequeryresultarticle.py | 24 +++--- telegram/_inline/inlinequeryresultaudio.py | 24 +++--- .../_inline/inlinequeryresultcachedaudio.py | 18 ++--- .../inlinequeryresultcacheddocument.py | 22 +++-- .../_inline/inlinequeryresultcachedgif.py | 20 +++-- .../inlinequeryresultcachedmpeg4gif.py | 20 +++-- .../_inline/inlinequeryresultcachedphoto.py | 22 +++-- .../_inline/inlinequeryresultcachedsticker.py | 12 ++- .../_inline/inlinequeryresultcachedvideo.py | 22 +++-- .../_inline/inlinequeryresultcachedvoice.py | 20 +++-- telegram/_inline/inlinequeryresultcontact.py | 24 +++--- telegram/_inline/inlinequeryresultdocument.py | 30 ++++--- telegram/_inline/inlinequeryresultgame.py | 10 +-- telegram/_inline/inlinequeryresultgif.py | 30 ++++--- telegram/_inline/inlinequeryresultlocation.py | 36 ++++----- telegram/_inline/inlinequeryresultmpeg4gif.py | 32 ++++---- telegram/_inline/inlinequeryresultphoto.py | 28 +++---- telegram/_inline/inlinequeryresultvenue.py | 32 ++++---- telegram/_inline/inlinequeryresultvideo.py | 34 ++++---- telegram/_inline/inlinequeryresultvoice.py | 22 +++-- telegram/_menubutton.py | 10 +-- telegram/_passport/credentials.py | 14 ++-- telegram/_passport/passportelementerrors.py | 80 ++++++++----------- telegram/_telegramobject.py | 2 - telegram/ext/_callbackdatacache.py | 5 +- telegram/ext/_extbot.py | 71 ++++++++-------- tests/test_telegramobject.py | 12 ++- 41 files changed, 456 insertions(+), 556 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 272df754752..8cb91742974 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -359,17 +359,15 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: # skipcq: PYL-R020 if isinstance(val, InputMedia): # Copy object as not to edit it in-place val = copy.copy(val) - val._unfreeze() # pylint: disable=protected-access - val.parse_mode = DefaultValue.get_value(val.parse_mode) - val._freeze() # pylint: disable=protected-access + with val._unfrozen(): # pylint: disable=protected-access + val.parse_mode = DefaultValue.get_value(val.parse_mode) data[key] = val elif key == "media" and isinstance(val, list): # Copy objects as not to edit them in-place copy_list = [copy.copy(media) for media in val] for media in copy_list: - media._unfreeze() # pylint: disable=protected-access - media.parse_mode = DefaultValue.get_value(media.parse_mode) - media._freeze() # pylint: disable=protected-access + with media._unfrozen(): # pylint: disable=protected-access + media.parse_mode = DefaultValue.get_value(media.parse_mode) data[key] = copy_list # 2) @@ -1908,14 +1906,13 @@ async def send_media_group( # Copy first item (to avoid mutation of original object), apply group caption to it. # This will lead to the group being shown with this caption. item_to_get_caption = copy.copy(media[0]) - item_to_get_caption._unfreeze() # pylint: disable=protected-access - item_to_get_caption.caption = caption - if parse_mode is not DEFAULT_NONE: - item_to_get_caption.parse_mode = parse_mode - item_to_get_caption.caption_entities = ( - tuple(caption_entities) if caption_entities else None - ) - item_to_get_caption._freeze() # pylint: disable=protected-access + with item_to_get_caption._unfrozen(): # pylint: disable=protected-access + item_to_get_caption.caption = caption + if parse_mode is not DEFAULT_NONE: + item_to_get_caption.parse_mode = parse_mode + item_to_get_caption.caption_entities = ( + tuple(caption_entities) if caption_entities else None + ) # copy the list (just the references) to avoid mutating the original list media = media[:] @@ -2647,9 +2644,8 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if hasattr(res, "parse_mode"): res = copy.copy(res) copied = True - res._unfreeze() # pylint: disable=protected-access - res.parse_mode = DefaultValue.get_value(res.parse_mode) - res._freeze() # pylint: disable=protected-access + with res._unfrozen(): # pylint: disable=protected-access + res.parse_mode = DefaultValue.get_value(res.parse_mode) if hasattr(res, "input_message_content") and res.input_message_content: if hasattr(res.input_message_content, "parse_mode"): if not copied: @@ -2658,22 +2654,20 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ res._unfreeze() # pylint: disable=protected-access res.input_message_content = copy.copy(res.input_message_content) - res.input_message_content._unfreeze() # pylint: disable=protected-access - res.input_message_content.parse_mode = DefaultValue.get_value( - res.input_message_content.parse_mode - ) - res.input_message_content._freeze() # pylint: disable=protected-access + with res.input_message_content._unfrozen(): # pylint: disable=protected-access + res.input_message_content.parse_mode = DefaultValue.get_value( + res.input_message_content.parse_mode + ) if hasattr(res.input_message_content, "disable_web_page_preview"): if not copied: res = copy.copy(res) res._unfreeze() # pylint: disable=protected-access res.input_message_content = copy.copy(res.input_message_content) - res.input_message_content._unfreeze() # pylint: disable=protected-access - res.input_message_content.disable_web_page_preview = DefaultValue.get_value( - res.input_message_content.disable_web_page_preview - ) - res.input_message_content._freeze() # pylint: disable=protected-access + with res.input_message_content._unfrozen(): # pylint: disable=protected-access + res.input_message_content.disable_web_page_preview = DefaultValue.get_value( + res.input_message_content.disable_web_page_preview + ) res._freeze() # pylint: disable=protected-access diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 8daf488165d..bfe289cca67 100644 --- a/telegram/_botcommandscope.py +++ b/telegram/_botcommandscope.py @@ -199,13 +199,11 @@ class BotCommandScopeChat(BotCommandScope): def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT, api_kwargs=api_kwargs) - self._unfreeze() - self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) - ) - self._id_attrs = (self.type, self.chat_id) - - self._freeze() + with self._unfrozen(): + self.chat_id = ( + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) + ) + self._id_attrs = (self.type, self.chat_id) class BotCommandScopeChatAdministrators(BotCommandScope): @@ -228,13 +226,11 @@ class BotCommandScopeChatAdministrators(BotCommandScope): def __init__(self, chat_id: Union[str, int], *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS, api_kwargs=api_kwargs) - self._unfreeze() - self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) - ) - self._id_attrs = (self.type, self.chat_id) - - self._freeze() + with self._unfrozen(): + self.chat_id = ( + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) + ) + self._id_attrs = (self.type, self.chat_id) class BotCommandScopeChatMember(BotCommandScope): @@ -260,11 +256,9 @@ class BotCommandScopeChatMember(BotCommandScope): def __init__(self, chat_id: Union[str, int], user_id: int, *, api_kwargs: JSONDict = None): super().__init__(type=BotCommandScope.CHAT_MEMBER, api_kwargs=api_kwargs) - self._unfreeze() - self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) - ) - self.user_id = user_id - self._id_attrs = (self.type, self.chat_id, self.user_id) - - self._freeze() + with self._unfrozen(): + self.chat_id = ( + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) + ) + self.user_id = user_id + self._id_attrs = (self.type, self.chat_id, self.user_id) diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index 1e1a333f310..678e682b7a3 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -161,11 +161,9 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.OWNER, user=user, api_kwargs=api_kwargs) - self._unfreeze() - self.is_anonymous = is_anonymous - self.custom_title = custom_title - - self._freeze() + with self._unfrozen(): + self.is_anonymous = is_anonymous + self.custom_title = custom_title class ChatMemberAdministrator(ChatMember): @@ -300,23 +298,21 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.ADMINISTRATOR, user=user, api_kwargs=api_kwargs) - self._unfreeze() - self.can_be_edited = can_be_edited - self.is_anonymous = is_anonymous - self.can_manage_chat = can_manage_chat - self.can_delete_messages = can_delete_messages - self.can_manage_video_chats = can_manage_video_chats - self.can_restrict_members = can_restrict_members - self.can_promote_members = can_promote_members - self.can_change_info = can_change_info - self.can_invite_users = can_invite_users - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_pin_messages = can_pin_messages - self.can_manage_topics = can_manage_topics - self.custom_title = custom_title - - self._freeze() + with self._unfrozen(): + self.can_be_edited = can_be_edited + self.is_anonymous = is_anonymous + self.can_manage_chat = can_manage_chat + self.can_delete_messages = can_delete_messages + self.can_manage_video_chats = can_manage_video_chats + self.can_restrict_members = can_restrict_members + self.can_promote_members = can_promote_members + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_post_messages = can_post_messages + self.can_edit_messages = can_edit_messages + self.can_pin_messages = can_pin_messages + self.can_manage_topics = can_manage_topics + self.custom_title = custom_title class ChatMemberMember(ChatMember): @@ -448,20 +444,18 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.RESTRICTED, user=user, api_kwargs=api_kwargs) - self._unfreeze() - self.is_member = is_member - self.can_change_info = can_change_info - self.can_invite_users = can_invite_users - self.can_pin_messages = can_pin_messages - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.can_manage_topics = can_manage_topics - self.until_date = until_date - - self._freeze() + with self._unfrozen(): + self.is_member = is_member + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews + self.can_manage_topics = can_manage_topics + self.until_date = until_date class ChatMemberLeft(ChatMember): @@ -523,7 +517,5 @@ def __init__( api_kwargs: JSONDict = None, ): super().__init__(status=ChatMember.BANNED, user=user, api_kwargs=api_kwargs) - self._unfreeze() - self.until_date = until_date - - self._freeze() + with self._unfrozen(): + self.until_date = until_date diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index fee876a3723..4db1d6a608f 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.py @@ -74,7 +74,6 @@ def __init__( file_size=file_size, api_kwargs=api_kwargs, ) - self._unfreeze() self.thumb = thumb @classmethod diff --git a/telegram/_files/animation.py b/telegram/_files/animation.py index 3988aa3e203..eb6f795414a 100644 --- a/telegram/_files/animation.py +++ b/telegram/_files/animation.py @@ -82,13 +82,11 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.width = width - self.height = height - self.duration = duration - # Optional - self.mime_type = mime_type - self.file_name = file_name - - self._freeze() + with self._unfrozen(): + # Required + self.width = width + self.height = height + self.duration = duration + # Optional + self.mime_type = mime_type + self.file_name = file_name diff --git a/telegram/_files/audio.py b/telegram/_files/audio.py index a5a4f4eb9fb..1b67748b34d 100644 --- a/telegram/_files/audio.py +++ b/telegram/_files/audio.py @@ -86,13 +86,11 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.duration = duration - # Optional - self.performer = performer - self.title = title - self.mime_type = mime_type - self.file_name = file_name - - self._freeze() + with self._unfrozen(): + # Required + self.duration = duration + # Optional + self.performer = performer + self.title = title + self.mime_type = mime_type + self.file_name = file_name diff --git a/telegram/_files/document.py b/telegram/_files/document.py index 7d264b0fcc4..621154914f2 100644 --- a/telegram/_files/document.py +++ b/telegram/_files/document.py @@ -73,9 +73,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) - self._unfreeze() - # Optional - self.mime_type = mime_type - self.file_name = file_name - - self._freeze() + with self._unfrozen(): + # Optional + self.mime_type = mime_type + self.file_name = file_name diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index c023b15aaa2..5cfe4a7f581 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -193,13 +193,11 @@ def __init__( parse_mode, api_kwargs=api_kwargs, ) - self._unfreeze() - self.thumb = self._parse_thumb_input(thumb) - self.width = width - self.height = height - self.duration = duration - - self._freeze() + with self._unfrozen(): + self.thumb = self._parse_thumb_input(thumb) + self.width = width + self.height = height + self.duration = duration class InputMediaPhoto(InputMedia): @@ -362,14 +360,12 @@ def __init__( parse_mode, api_kwargs=api_kwargs, ) - self._unfreeze() - self.width = width - self.height = height - self.duration = duration - self.thumb = self._parse_thumb_input(thumb) - self.supports_streaming = supports_streaming - - self._freeze() + with self._unfrozen(): + self.width = width + self.height = height + self.duration = duration + self.thumb = self._parse_thumb_input(thumb) + self.supports_streaming = supports_streaming class InputMediaAudio(InputMedia): @@ -462,13 +458,11 @@ def __init__( parse_mode, api_kwargs=api_kwargs, ) - self._unfreeze() - self.thumb = self._parse_thumb_input(thumb) - self.duration = duration - self.title = title - self.performer = performer - - self._freeze() + with self._unfrozen(): + self.thumb = self._parse_thumb_input(thumb) + self.duration = duration + self.title = title + self.performer = performer class InputMediaDocument(InputMedia): @@ -545,8 +539,6 @@ def __init__( parse_mode, api_kwargs=api_kwargs, ) - self._unfreeze() - self.thumb = self._parse_thumb_input(thumb) - self.disable_content_type_detection = disable_content_type_detection - - self._freeze() + with self._unfrozen(): + self.thumb = self._parse_thumb_input(thumb) + self.disable_content_type_detection = disable_content_type_detection diff --git a/telegram/_files/photosize.py b/telegram/_files/photosize.py index 2bfadebd3a4..0628529578e 100644 --- a/telegram/_files/photosize.py +++ b/telegram/_files/photosize.py @@ -68,9 +68,7 @@ def __init__( file_size=file_size, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.width = width - self.height = height - - self._freeze() + with self._unfrozen(): + # Required + self.width = width + self.height = height diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 50b6b1a3d2f..1a13f7c355b 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -149,21 +149,19 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.width = width - self.height = height - self.is_animated = is_animated - self.is_video = is_video - self.type = type - # Optional - self.emoji = emoji - self.set_name = set_name - self.mask_position = mask_position - self.premium_animation = premium_animation - self.custom_emoji_id = custom_emoji_id - - self._freeze() + with self._unfrozen(): + # Required + self.width = width + self.height = height + self.is_animated = is_animated + self.is_video = is_video + self.type = type + # Optional + self.emoji = emoji + self.set_name = set_name + self.mask_position = mask_position + self.premium_animation = premium_animation + self.custom_emoji_id = custom_emoji_id REGULAR: ClassVar[str] = constants.StickerType.REGULAR """:const:`telegram.constants.StickerType.REGULAR`""" diff --git a/telegram/_files/video.py b/telegram/_files/video.py index 22d2022cd99..b305bf32530 100644 --- a/telegram/_files/video.py +++ b/telegram/_files/video.py @@ -82,13 +82,11 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.width = width - self.height = height - self.duration = duration - # Optional - self.mime_type = mime_type - self.file_name = file_name - - self._freeze() + with self._unfrozen(): + # Required + self.width = width + self.height = height + self.duration = duration + # Optional + self.mime_type = mime_type + self.file_name = file_name diff --git a/telegram/_files/videonote.py b/telegram/_files/videonote.py index d5a4c6c88d9..31ec471f2c4 100644 --- a/telegram/_files/videonote.py +++ b/telegram/_files/videonote.py @@ -73,9 +73,7 @@ def __init__( thumb=thumb, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.length = length - self.duration = duration - - self._freeze() + with self._unfrozen(): + # Required + self.length = length + self.duration = duration diff --git a/telegram/_files/voice.py b/telegram/_files/voice.py index 853fc9ee604..47d6f7b6799 100644 --- a/telegram/_files/voice.py +++ b/telegram/_files/voice.py @@ -67,10 +67,8 @@ def __init__( file_size=file_size, api_kwargs=api_kwargs, ) - self._unfreeze() - # Required - self.duration = duration - # Optional - self.mime_type = mime_type - - self._freeze() + with self._unfrozen(): + # Required + self.duration = duration + # Optional + self.mime_type = mime_type diff --git a/telegram/_inline/inlinekeyboardbutton.py b/telegram/_inline/inlinekeyboardbutton.py index e1396555ca7..34afb1e0ca3 100644 --- a/telegram/_inline/inlinekeyboardbutton.py +++ b/telegram/_inline/inlinekeyboardbutton.py @@ -241,10 +241,9 @@ def update_callback_data(self, callback_data: Union[str, object]) -> None: Args: callback_data (:class:`object`): The new callback data. """ - self._unfreeze() - self.callback_data = callback_data - self._set_id_attrs() - self._freeze() + with self._unfrozen(): + self.callback_data = callback_data + self._set_id_attrs() MIN_CALLBACK_DATA: ClassVar[int] = constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA """:const:`telegram.constants.InlineKeyboardButtonLimit.MIN_CALLBACK_DATA` diff --git a/telegram/_inline/inlinequeryresultarticle.py b/telegram/_inline/inlinequeryresultarticle.py index 9d9c1c83c54..d77abf6a4db 100644 --- a/telegram/_inline/inlinequeryresultarticle.py +++ b/telegram/_inline/inlinequeryresultarticle.py @@ -102,17 +102,15 @@ def __init__( # Required super().__init__(InlineQueryResultType.ARTICLE, id, api_kwargs=api_kwargs) - self._unfreeze() - self.title = title - self.input_message_content = input_message_content + with self._unfrozen(): + self.title = title + self.input_message_content = input_message_content - # Optional - self.reply_markup = reply_markup - self.url = url - self.hide_url = hide_url - self.description = description - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - self._freeze() + # Optional + self.reply_markup = reply_markup + self.url = url + self.hide_url = hide_url + self.description = description + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index c9f3d69275e..48884481d20 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -111,17 +111,15 @@ def __init__( # Required super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) - self._unfreeze() - self.audio_url = audio_url - self.title = title + with self._unfrozen(): + self.audio_url = audio_url + self.title = title - # Optionals - self.performer = performer - self.audio_duration = audio_duration - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.performer = performer + self.audio_duration = audio_duration + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index b353859268f..4061214b900 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -99,14 +99,12 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.AUDIO, id, api_kwargs=api_kwargs) - self._unfreeze() - self.audio_file_id = audio_file_id + with self._unfrozen(): + self.audio_file_id = audio_file_id - # Optionals - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 9617fa0ffc7..b082233a3e2 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -107,16 +107,14 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) - self._unfreeze() - self.title = title - self.document_file_id = document_file_id + with self._unfrozen(): + self.title = title + self.document_file_id = document_file_id - # Optionals - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.description = description + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 1fffaaa8c17..daecf6a179b 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -104,15 +104,13 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) - self._unfreeze() - self.gif_file_id = gif_file_id + with self._unfrozen(): + self.gif_file_id = gif_file_id - # Optionals - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.title = title + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index aa8c315c91b..8c22aa558df 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -104,15 +104,13 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) - self._unfreeze() - self.mpeg4_file_id = mpeg4_file_id + with self._unfrozen(): + self.mpeg4_file_id = mpeg4_file_id - # Optionals - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.title = title + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index ec04a888d5b..0452abbef5c 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -108,16 +108,14 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) - self._unfreeze() - self.photo_file_id = photo_file_id + with self._unfrozen(): + self.photo_file_id = photo_file_id - # Optionals - self.title = title - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.title = title + self.description = description + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedsticker.py b/telegram/_inline/inlinequeryresultcachedsticker.py index 0f1a15232f6..f4a121e6cab 100644 --- a/telegram/_inline/inlinequeryresultcachedsticker.py +++ b/telegram/_inline/inlinequeryresultcachedsticker.py @@ -71,11 +71,9 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.STICKER, id, api_kwargs=api_kwargs) - self._unfreeze() - self.sticker_file_id = sticker_file_id + with self._unfrozen(): + self.sticker_file_id = sticker_file_id - # Optionals - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 0209ff707d0..9c23cf6b4a9 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -104,16 +104,14 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) - self._unfreeze() - self.video_file_id = video_file_id - self.title = title + with self._unfrozen(): + self.video_file_id = video_file_id + self.title = title - # Optionals - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.description = description + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 2e7f89f6a93..ee8d9f5c194 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -102,15 +102,13 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) - self._unfreeze() - self.voice_file_id = voice_file_id - self.title = title + with self._unfrozen(): + self.voice_file_id = voice_file_id + self.title = title - # Optionals - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcontact.py b/telegram/_inline/inlinequeryresultcontact.py index 4985ac68ee5..8a46d67d80e 100644 --- a/telegram/_inline/inlinequeryresultcontact.py +++ b/telegram/_inline/inlinequeryresultcontact.py @@ -101,17 +101,15 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.CONTACT, id, api_kwargs=api_kwargs) - self._unfreeze() - self.phone_number = phone_number - self.first_name = first_name + with self._unfrozen(): + self.phone_number = phone_number + self.first_name = first_name - # Optionals - self.last_name = last_name - self.vcard = vcard - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - self._freeze() + # Optionals + self.last_name = last_name + self.vcard = vcard + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index df28dedd558..af9109be9dc 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -126,20 +126,18 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.DOCUMENT, id, api_kwargs=api_kwargs) - self._unfreeze() - self.document_url = document_url - self.title = title - self.mime_type = mime_type + with self._unfrozen(): + self.document_url = document_url + self.title = title + self.mime_type = mime_type - # Optionals - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.description = description - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - self._freeze() + # Optionals + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.description = description + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height diff --git a/telegram/_inline/inlinequeryresultgame.py b/telegram/_inline/inlinequeryresultgame.py index 13260610eee..31f9470926d 100644 --- a/telegram/_inline/inlinequeryresultgame.py +++ b/telegram/_inline/inlinequeryresultgame.py @@ -58,10 +58,8 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.GAME, id, api_kwargs=api_kwargs) - self._unfreeze() - self.id = id - self.game_short_name = game_short_name + with self._unfrozen(): + self.id = id + self.game_short_name = game_short_name - self.reply_markup = reply_markup - - self._freeze() + self.reply_markup = reply_markup diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 68490bf530b..d069f886200 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -127,20 +127,18 @@ def __init__( # Required super().__init__(InlineQueryResultType.GIF, id, api_kwargs=api_kwargs) - self._unfreeze() - self.gif_url = gif_url - self.thumb_url = thumb_url + with self._unfrozen(): + self.gif_url = gif_url + self.thumb_url = thumb_url - # Optionals - self.gif_width = gif_width - self.gif_height = gif_height - self.gif_duration = gif_duration - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_mime_type = thumb_mime_type - - self._freeze() + # Optionals + self.gif_width = gif_width + self.gif_height = gif_height + self.gif_duration = gif_duration + self.title = title + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.thumb_mime_type = thumb_mime_type diff --git a/telegram/_inline/inlinequeryresultlocation.py b/telegram/_inline/inlinequeryresultlocation.py index 7dfde2bf02f..145d5138134 100644 --- a/telegram/_inline/inlinequeryresultlocation.py +++ b/telegram/_inline/inlinequeryresultlocation.py @@ -127,25 +127,23 @@ def __init__( ): # Required super().__init__(constants.InlineQueryResultType.LOCATION, id, api_kwargs=api_kwargs) - self._unfreeze() - self.latitude = latitude - self.longitude = longitude - self.title = title - - # Optionals - self.live_period = live_period - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - self.horizontal_accuracy = horizontal_accuracy - self.heading = heading - self.proximity_alert_radius = ( - int(proximity_alert_radius) if proximity_alert_radius else None - ) - - self._freeze() + with self._unfrozen(): + self.latitude = latitude + self.longitude = longitude + self.title = title + + # Optionals + self.live_period = live_period + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height + self.horizontal_accuracy = horizontal_accuracy + self.heading = heading + self.proximity_alert_radius = ( + int(proximity_alert_radius) if proximity_alert_radius else None + ) HORIZONTAL_ACCURACY: ClassVar[int] = constants.LocationLimit.HORIZONTAL_ACCURACY """:const:`telegram.constants.LocationLimit.HORIZONTAL_ACCURACY` diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 68a055644d0..2e23335acc4 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -128,20 +128,18 @@ def __init__( # Required super().__init__(InlineQueryResultType.MPEG4GIF, id, api_kwargs=api_kwargs) - self._unfreeze() - self.mpeg4_url = mpeg4_url - self.thumb_url = thumb_url - - # Optional - self.mpeg4_width = mpeg4_width - self.mpeg4_height = mpeg4_height - self.mpeg4_duration = mpeg4_duration - self.title = title - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_mime_type = thumb_mime_type - - self._freeze() + with self._unfrozen(): + self.mpeg4_url = mpeg4_url + self.thumb_url = thumb_url + + # Optional + self.mpeg4_width = mpeg4_width + self.mpeg4_height = mpeg4_height + self.mpeg4_duration = mpeg4_duration + self.title = title + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.thumb_mime_type = thumb_mime_type diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 379155336a0..bfa35e52327 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -121,19 +121,17 @@ def __init__( ): # Required super().__init__(InlineQueryResultType.PHOTO, id, api_kwargs=api_kwargs) - self._unfreeze() - self.photo_url = photo_url - self.thumb_url = thumb_url + with self._unfrozen(): + self.photo_url = photo_url + self.thumb_url = thumb_url - # Optionals - self.photo_width = photo_width - self.photo_height = photo_height - self.title = title - self.description = description - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optionals + self.photo_width = photo_width + self.photo_height = photo_height + self.title = title + self.description = description + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultvenue.py b/telegram/_inline/inlinequeryresultvenue.py index 7c1b15e0a78..9b6359b456c 100644 --- a/telegram/_inline/inlinequeryresultvenue.py +++ b/telegram/_inline/inlinequeryresultvenue.py @@ -124,21 +124,19 @@ def __init__( # Required super().__init__(InlineQueryResultType.VENUE, id, api_kwargs=api_kwargs) - self._unfreeze() - self.latitude = latitude - self.longitude = longitude - self.title = title - self.address = address + with self._unfrozen(): + self.latitude = latitude + self.longitude = longitude + self.title = title + self.address = address - # Optional - self.foursquare_id = foursquare_id - self.foursquare_type = foursquare_type - self.google_place_id = google_place_id - self.google_place_type = google_place_type - self.reply_markup = reply_markup - self.input_message_content = input_message_content - self.thumb_url = thumb_url - self.thumb_width = thumb_width - self.thumb_height = thumb_height - - self._freeze() + # Optional + self.foursquare_id = foursquare_id + self.foursquare_type = foursquare_type + self.google_place_id = google_place_id + self.google_place_type = google_place_type + self.reply_markup = reply_markup + self.input_message_content = input_message_content + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index d167c4ad671..be82d07d82a 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -138,21 +138,19 @@ def __init__( # Required super().__init__(InlineQueryResultType.VIDEO, id, api_kwargs=api_kwargs) - self._unfreeze() - self.video_url = video_url - self.mime_type = mime_type - self.thumb_url = thumb_url - self.title = title - - # Optional - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.video_width = video_width - self.video_height = video_height - self.video_duration = video_duration - self.description = description - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + with self._unfrozen(): + self.video_url = video_url + self.mime_type = mime_type + self.thumb_url = thumb_url + self.title = title + + # Optional + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.video_width = video_width + self.video_height = video_height + self.video_duration = video_duration + self.description = description + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index 6d41687a496..d7d53987074 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -109,16 +109,14 @@ def __init__( # Required super().__init__(InlineQueryResultType.VOICE, id, api_kwargs=api_kwargs) - self._unfreeze() - self.voice_url = voice_url - self.title = title + with self._unfrozen(): + self.voice_url = voice_url + self.title = title - # Optional - self.voice_duration = voice_duration - self.caption = caption - self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None - self.reply_markup = reply_markup - self.input_message_content = input_message_content - - self._freeze() + # Optional + self.voice_duration = voice_duration + self.caption = caption + self.parse_mode = parse_mode + self.caption_entities = tuple(caption_entities) if caption_entities else None + self.reply_markup = reply_markup + self.input_message_content = input_message_content diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 8c912bd7df5..ea10434e658 100644 --- a/telegram/_menubutton.py +++ b/telegram/_menubutton.py @@ -147,13 +147,11 @@ class MenuButtonWebApp(MenuButton): def __init__(self, text: str, web_app: WebAppInfo, *, api_kwargs: JSONDict = None): super().__init__(type=constants.MenuButtonType.WEB_APP, api_kwargs=api_kwargs) - self._unfreeze() - self.text = text - self.web_app = web_app + with self._unfrozen(): + self.text = text + self.web_app = web_app - self._id_attrs = (self.type, self.text, self.web_app) - - self._freeze() + self._id_attrs = (self.type, self.text, self.web_app) @classmethod def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MenuButtonWebApp"]: diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index bb989662668..5d2df4277d0 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -429,15 +429,13 @@ def __init__( self, hash: str, secret: str, *, api_kwargs: JSONDict = None # skipcq: PYL-W0622 ): super().__init__(api_kwargs=api_kwargs) - super()._unfreeze() - self.hash = hash - self.secret = secret + with self._unfrozen(): + self.hash = hash + self.secret = secret - # Aliases just to be sure - self.file_hash = self.hash - self.data_hash = self.hash - - self._freeze() + # Aliases just to be sure + self.file_hash = self.hash + self.data_hash = self.hash class DataCredentials(_CredentialsBase): diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index afae22cb67a..58ce566a7c8 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -97,13 +97,17 @@ def __init__( ): # Required super().__init__("data", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.field_name = field_name - self.data_hash = data_hash + with self._unfrozen(): + self.field_name = field_name + self.data_hash = data_hash - self._id_attrs = (self.source, self.type, self.field_name, self.data_hash, self.message) - - self._freeze() + self._id_attrs = ( + self.source, + self.type, + self.field_name, + self.data_hash, + self.message, + ) class PassportElementErrorFile(PassportElementError): @@ -136,12 +140,10 @@ class PassportElementErrorFile(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("file", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hash = file_hash + with self._unfrozen(): + self.file_hash = file_hash - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - self._freeze() + self._id_attrs = (self.source, self.type, self.file_hash, self.message) class PassportElementErrorFiles(PassportElementError): @@ -174,12 +176,10 @@ class PassportElementErrorFiles(PassportElementError): def __init__(self, type: str, file_hashes: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("files", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hashes = file_hashes + with self._unfrozen(): + self.file_hashes = file_hashes - self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) - - self._freeze() + self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) class PassportElementErrorFrontSide(PassportElementError): @@ -212,12 +212,10 @@ class PassportElementErrorFrontSide(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("front_side", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) + with self._unfrozen(): + self.file_hash = file_hash - self._freeze() + self._id_attrs = (self.source, self.type, self.file_hash, self.message) class PassportElementErrorReverseSide(PassportElementError): @@ -250,12 +248,10 @@ class PassportElementErrorReverseSide(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("reverse_side", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) + with self._unfrozen(): + self.file_hash = file_hash - self._freeze() + self._id_attrs = (self.source, self.type, self.file_hash, self.message) class PassportElementErrorSelfie(PassportElementError): @@ -286,12 +282,10 @@ class PassportElementErrorSelfie(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("selfie", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hash = file_hash - - self._id_attrs = (self.source, self.type, self.file_hash, self.message) + with self._unfrozen(): + self.file_hash = file_hash - self._freeze() + self._id_attrs = (self.source, self.type, self.file_hash, self.message) class PassportElementErrorTranslationFile(PassportElementError): @@ -326,12 +320,10 @@ class PassportElementErrorTranslationFile(PassportElementError): def __init__(self, type: str, file_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("translation_file", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hash = file_hash + with self._unfrozen(): + self.file_hash = file_hash - self._id_attrs = (self.source, self.type, self.file_hash, self.message) - - self._freeze() + self._id_attrs = (self.source, self.type, self.file_hash, self.message) class PassportElementErrorTranslationFiles(PassportElementError): @@ -366,12 +358,10 @@ class PassportElementErrorTranslationFiles(PassportElementError): def __init__(self, type: str, file_hashes: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("translation_files", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.file_hashes = file_hashes + with self._unfrozen(): + self.file_hashes = file_hashes - self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) - - self._freeze() + self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) class PassportElementErrorUnspecified(PassportElementError): @@ -400,9 +390,7 @@ class PassportElementErrorUnspecified(PassportElementError): def __init__(self, type: str, element_hash: str, message: str, *, api_kwargs: JSONDict = None): # Required super().__init__("unspecified", type, message, api_kwargs=api_kwargs) - self._unfreeze() - self.element_hash = element_hash + with self._unfrozen(): + self.element_hash = element_hash - self._id_attrs = (self.source, self.type, self.element_hash, self.message) - - self._freeze() + self._id_attrs = (self.source, self.type, self.element_hash, self.message) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 2b676792488..b02373d84f0 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -95,8 +95,6 @@ def __init__(self, *, api_kwargs: JSONDict = None) -> None: # We don't do anything with api_kwargs here - see docstring of _apply_api_kwargs self.api_kwargs: Mapping[str, Any] = MappingProxyType(api_kwargs or {}) - self._freeze() - def _freeze(self) -> None: self._frozen = True diff --git a/telegram/ext/_callbackdatacache.py b/telegram/ext/_callbackdatacache.py index 7923adb4437..26fc9508b87 100644 --- a/telegram/ext/_callbackdatacache.py +++ b/telegram/ext/_callbackdatacache.py @@ -381,9 +381,8 @@ def process_callback_query(self, callback_query: CallbackQuery) -> None: # Get the cached callback data for the CallbackQuery keyboard_uuid, button_data = self.__get_keyboard_uuid_and_button_data(data) - callback_query._unfreeze() # pylint: disable=protected-access - callback_query.data = button_data # type: ignore[assignment] - callback_query._freeze() # pylint: disable=protected-access + with callback_query._unfrozen(): # pylint: disable=protected-access + callback_query.data = button_data # type: ignore[assignment] # Map the callback queries ID to the keyboards UUID for later use if not mapped and not isinstance(button_data, InvalidCallbackData): diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 62ef5df5c62..40a21520042 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -211,23 +211,21 @@ def __init__( private_key_password=private_key_password, local_mode=local_mode, ) - self._unfreeze() - self._defaults = defaults - self._rate_limiter = rate_limiter - self._callback_data_cache: Optional[CallbackDataCache] = None + with self._unfrozen(): + self._defaults = defaults + self._rate_limiter = rate_limiter + self._callback_data_cache: Optional[CallbackDataCache] = None - # set up callback_data - if arbitrary_callback_data is False: - return + # set up callback_data + if arbitrary_callback_data is False: + return - if not isinstance(arbitrary_callback_data, bool): - maxsize = cast(int, arbitrary_callback_data) - else: - maxsize = 1024 - - self._callback_data_cache = CallbackDataCache(bot=self, maxsize=maxsize) + if not isinstance(arbitrary_callback_data, bool): + maxsize = cast(int, arbitrary_callback_data) + else: + maxsize = 1024 - self._freeze() + self._callback_data_cache = CallbackDataCache(bot=self, maxsize=maxsize) @property def callback_data_cache(self) -> Optional[CallbackDataCache]: @@ -383,18 +381,16 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: elif isinstance(val, InputMedia) and val.parse_mode is DEFAULT_NONE: # Copy object as not to edit it in-place val = copy(val) - val._unfreeze() # pylint: disable=protected-access - val.parse_mode = self.defaults.parse_mode if self.defaults else None - val._freeze() # pylint: disable=protected-access + with val._unfrozen(): # pylint: disable=protected-access + val.parse_mode = self.defaults.parse_mode if self.defaults else None data[key] = val elif key == "media" and isinstance(val, list): # Copy objects as not to edit them in-place copy_list = [copy(media) for media in val] for media in copy_list: if media.parse_mode is DEFAULT_NONE: - media._unfreeze() # pylint: disable=protected-access - media.parse_mode = self.defaults.parse_mode if self.defaults else None - media._freeze() # pylint: disable=protected-access + with media._unfrozen(): # pylint: disable=protected-access + media.parse_mode = self.defaults.parse_mode if self.defaults else None data[key] = copy_list @@ -563,10 +559,12 @@ def _effective_inline_results( # We build a new result in case the user wants to use the same object in # different places new_result = copy(result) - new_result._unfreeze() # pylint: disable=protected-access - markup = self._replace_keyboard(result.reply_markup) # type: ignore[attr-defined] - new_result.reply_markup = markup - new_result._freeze() # pylint: disable=protected-access + with new_result._unfrozen(): # pylint: disable=protected-access + markup = self._replace_keyboard( + result.reply_markup # type: ignore[attr-defined] + ) + new_result.reply_markup = markup + results.append(new_result) return results, next_offset @@ -581,10 +579,9 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ copied = False if hasattr(res, "parse_mode") and res.parse_mode is DEFAULT_NONE: res = copy(res) - res._unfreeze() # pylint: disable=protected-access - copied = True - res.parse_mode = self.defaults.parse_mode if self.defaults else None - res._freeze() # pylint: disable=protected-access + with res._unfrozen(): # pylint: disable=protected-access + copied = True + res.parse_mode = self.defaults.parse_mode if self.defaults else None if hasattr(res, "input_message_content") and res.input_message_content: if ( hasattr(res.input_message_content, "parse_mode") @@ -593,22 +590,20 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if not copied: res = copy(res) copied = True - res.input_message_content._unfreeze() # pylint: disable=protected-access - res.input_message_content.parse_mode = ( - self.defaults.parse_mode if self.defaults else None - ) - res.input_message_content._freeze() # pylint: disable=protected-access + with res.input_message_content._unfrozen(): # pylint: disable=protected-access + res.input_message_content.parse_mode = ( + self.defaults.parse_mode if self.defaults else None + ) if ( hasattr(res.input_message_content, "disable_web_page_preview") and res.input_message_content.disable_web_page_preview is DEFAULT_NONE ): if not copied: res = copy(res) - res.input_message_content._unfreeze() # pylint: disable=protected-access - res.input_message_content.disable_web_page_preview = ( - self.defaults.disable_web_page_preview if self.defaults else None - ) - res.input_message_content._freeze() # pylint: disable=protected-access + with res.input_message_content._unfrozen(): # pylint: disable=protected-access + res.input_message_content.disable_web_page_preview = ( + self.defaults.disable_web_page_preview if self.defaults else None + ) return res diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index eb22dd35200..a81a5c03163 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -19,6 +19,7 @@ import datetime import inspect import pickle +import re from copy import deepcopy from pathlib import Path from types import MappingProxyType @@ -425,7 +426,16 @@ def test_subclasses_are_frozen(self, cls): return source_lines, first_line = inspect.getsourcelines(cls.__init__) - assert " self._freeze()" in source_lines[-1], f"{cls.__name__} is not frozen correctly" + + # We use regex matching since a simple "if self._freeze() in source_lines[-1]" would also + # allo commented lines. + last_line_freezes = re.match(r"\s*self\.\_freeze\(\)", source_lines[-1]) + uses_with_unfrozen = re.search( + r"\n\s*with self\.\_unfrozen\(\)\:", inspect.getsource(cls.__init__) + ) + + print(last_line_freezes, uses_with_unfrozen) + assert last_line_freezes or uses_with_unfrozen, f"{cls.__name__} is not frozen correctly" def test_freeze_unfreeze(self): class TestSub(TelegramObject): From 11b32c5e37ed621927cb1c5642c59f24e9d30e6c Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Mon, 28 Nov 2022 09:55:38 +0100 Subject: [PATCH 70/90] try fixing tests --- tests/test_application.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_application.py b/tests/test_application.py index 4c781d0cf2d..8e32142b38b 100644 --- a/tests/test_application.py +++ b/tests/test_application.py @@ -1424,6 +1424,7 @@ async def post_init(app: Application) -> None: events.append("post_init") app = Application.builder().token(bot.token).post_init(post_init).build() + app.bot._unfreeze() monkeypatch.setattr(app.bot, "get_updates", get_updates) monkeypatch.setattr( app, "initialize", call_after(app.initialize, lambda _: events.append("init")) @@ -1466,6 +1467,7 @@ async def post_shutdown(app: Application) -> None: events.append("post_shutdown") app = Application.builder().token(bot.token).post_shutdown(post_shutdown).build() + app.bot._unfreeze() monkeypatch.setattr(app.bot, "get_updates", get_updates) monkeypatch.setattr( app, "shutdown", call_after(app.shutdown, lambda _: events.append("shutdown")) @@ -1646,6 +1648,7 @@ async def post_init(app: Application) -> None: events.append("post_init") app = Application.builder().token(bot.token).post_init(post_init).build() + app.bot._unfreeze() monkeypatch.setattr(app.bot, "set_webhook", set_webhook) monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook) monkeypatch.setattr( @@ -1705,6 +1708,7 @@ async def post_shutdown(app: Application) -> None: events.append("post_shutdown") app = Application.builder().token(bot.token).post_shutdown(post_shutdown).build() + app.bot._unfreeze() monkeypatch.setattr(app.bot, "set_webhook", set_webhook) monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook) monkeypatch.setattr( From 81b8abd8a6b62a13743ee1ec8c8b4af0b432f708 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Wed, 30 Nov 2022 16:46:53 +0100 Subject: [PATCH 71/90] Get started on review --- docs/source/telegram.telegramobject.rst | 2 +- setup.cfg | 1 + telegram/_bot.py | 22 ++++++++++------------ telegram/_telegramobject.py | 22 ++++++++++++++-------- telegram/ext/_callbackdatacache.py | 2 +- telegram/ext/_extbot.py | 12 ++++++------ 6 files changed, 33 insertions(+), 28 deletions(-) diff --git a/docs/source/telegram.telegramobject.rst b/docs/source/telegram.telegramobject.rst index c9ce365a461..ca05cdd812e 100644 --- a/docs/source/telegram.telegramobject.rst +++ b/docs/source/telegram.telegramobject.rst @@ -4,4 +4,4 @@ telegram.TelegramObject .. autoclass:: telegram.TelegramObject :members: :show-inheritance: - :special-members: __repr__, __getitem__, __eq__, __hash__, __setstate__, __getstate__, __deepcopy__ + :special-members: __repr__, __getitem__, __eq__, __hash__, __setstate__, __getstate__, __deepcopy__, __setattr__, __delattr__ diff --git a/setup.cfg b/setup.cfg index 7e74d0fa237..fa56c5b5f0b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ disable = duplicate-code,too-many-arguments,too-many-public-methods,too-few-publ missing-class-docstring,too-many-locals,too-many-lines,too-many-branches, too-many-statements enable=useless-suppression ; Warns about unused pylint ignores +exclude-protected=_unfrozen [tool:pytest] testpaths = tests diff --git a/telegram/_bot.py b/telegram/_bot.py index b5978e33583..58cb25e2521 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -359,14 +359,14 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: # skipcq: PYL-R020 if isinstance(val, InputMedia): # Copy object as not to edit it in-place val = copy.copy(val) - with val._unfrozen(): # pylint: disable=protected-access + with val._unfrozen(): val.parse_mode = DefaultValue.get_value(val.parse_mode) data[key] = val elif key == "media" and isinstance(val, list): # Copy objects as not to edit them in-place copy_list = [copy.copy(media) for media in val] for media in copy_list: - with media._unfrozen(): # pylint: disable=protected-access + with media._unfrozen(): media.parse_mode = DefaultValue.get_value(media.parse_mode) data[key] = copy_list @@ -1909,7 +1909,7 @@ async def send_media_group( # Copy first item (to avoid mutation of original object), apply group caption to it. # This will lead to the group being shown with this caption. item_to_get_caption = copy.copy(media[0]) - with item_to_get_caption._unfrozen(): # pylint: disable=protected-access + with item_to_get_caption._unfrozen(): item_to_get_caption.caption = caption if parse_mode is not DEFAULT_NONE: item_to_get_caption.parse_mode = parse_mode @@ -2647,7 +2647,7 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if hasattr(res, "parse_mode"): res = copy.copy(res) copied = True - with res._unfrozen(): # pylint: disable=protected-access + with res._unfrozen(): res.parse_mode = DefaultValue.get_value(res.parse_mode) if hasattr(res, "input_message_content") and res.input_message_content: if hasattr(res.input_message_content, "parse_mode"): @@ -2655,9 +2655,9 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ res = copy.copy(res) copied = True - res._unfreeze() # pylint: disable=protected-access - res.input_message_content = copy.copy(res.input_message_content) - with res.input_message_content._unfrozen(): # pylint: disable=protected-access + with res._unfrozen(): + res.input_message_content = copy.copy(res.input_message_content) + with res.input_message_content._unfrozen(): res.input_message_content.parse_mode = DefaultValue.get_value( res.input_message_content.parse_mode ) @@ -2665,15 +2665,13 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if not copied: res = copy.copy(res) - res._unfreeze() # pylint: disable=protected-access - res.input_message_content = copy.copy(res.input_message_content) - with res.input_message_content._unfrozen(): # pylint: disable=protected-access + with res._unfrozen(): + res.input_message_content = copy.copy(res.input_message_content) + with res.input_message_content._unfrozen(): res.input_message_content.disable_web_page_preview = DefaultValue.get_value( res.input_message_content.disable_web_page_preview ) - res._freeze() # pylint: disable=protected-access - return res @_log diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index b02373d84f0..02423093b41 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -116,7 +116,7 @@ def _unfrozen(self: Tele_co) -> Iterator[Tele_co]: def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None: """Loops through the api kwargs and for every key that exists as attribute of the object (and is None), it moves the value from `api_kwargs` to the attribute. - *Edits `api_kwargs` in pace!* + *Edits `api_kwargs` in place!* This method is currently only called in the unpickling process, i.e. not on "normal" init. This is because @@ -134,6 +134,11 @@ def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None: setattr(self, key, api_kwargs.pop(key)) def __setattr__(self, key: str, value: object) -> None: + """ "Overrides :meth:`object.__setattr__` to prevent the overriding of attributes. + + Raises: + :exc:`AttributeError` + """ # protected attributes can always be set for convenient internal use if (key == "_frozen") or (not getattr(self, "_frozen", True)) or key.startswith("_"): super().__setattr__(key, value) @@ -144,6 +149,11 @@ def __setattr__(self, key: str, value: object) -> None: ) def __delattr__(self, key: str) -> None: + """ "Overrides :meth:`object.__delattr__` to prevent the deletion of attributes. + + Raises: + :exc:`AttributeError` + """ # protected attributes can always be set for convenient internal use if (key == "_frozen") or (not getattr(self, "_frozen", True)) or key.startswith("_"): super().__delattr__(key) @@ -267,14 +277,10 @@ def __setstate__(self, state: dict) -> None: # get api_kwargs first because we may need to add entries to it (see try-except below) api_kwargs = state.pop("api_kwargs", {}) + # get _frozen before the loop to avoid setting it to True in the loop + frozen = state.pop("_frozen", False) for key, val in state.items(): - if key == "_frozen": - # Setting the frozen status to True would prevent the attributes from being set - continue - if key == "api_kwargs": - # See below - continue try: setattr(self, key, val) @@ -291,7 +297,7 @@ def __setstate__(self, state: dict) -> None: # Apply freezing if necessary # we .get(…) the setting for backwards compatibility with objects that were pickled # before the freeze feature was introduced - if state.get("_frozen", False): + if frozen: self._freeze() def __deepcopy__(self: Tele_co, memodict: dict) -> Tele_co: diff --git a/telegram/ext/_callbackdatacache.py b/telegram/ext/_callbackdatacache.py index 26fc9508b87..c62d4db6255 100644 --- a/telegram/ext/_callbackdatacache.py +++ b/telegram/ext/_callbackdatacache.py @@ -381,7 +381,7 @@ def process_callback_query(self, callback_query: CallbackQuery) -> None: # Get the cached callback data for the CallbackQuery keyboard_uuid, button_data = self.__get_keyboard_uuid_and_button_data(data) - with callback_query._unfrozen(): # pylint: disable=protected-access + with callback_query._unfrozen(): callback_query.data = button_data # type: ignore[assignment] # Map the callback queries ID to the keyboards UUID for later use diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 26515634fdc..6b2a14e53f8 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -381,7 +381,7 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: 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(): # pylint: disable=protected-access + with val._unfrozen(): val.parse_mode = self.defaults.parse_mode if self.defaults else None data[key] = val elif key == "media" and isinstance(val, list): @@ -389,7 +389,7 @@ def _insert_defaults(self, data: Dict[str, object]) -> None: copy_list = [copy(media) for media in val] for media in copy_list: if media.parse_mode is DEFAULT_NONE: - with media._unfrozen(): # pylint: disable=protected-access + with media._unfrozen(): media.parse_mode = self.defaults.parse_mode if self.defaults else None data[key] = copy_list @@ -567,7 +567,7 @@ def _effective_inline_results( # We build a new result in case the user wants to use the same object in # different places new_result = copy(result) - with new_result._unfrozen(): # pylint: disable=protected-access + with new_result._unfrozen(): markup = self._replace_keyboard( result.reply_markup # type: ignore[attr-defined] ) @@ -587,7 +587,7 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ copied = False if hasattr(res, "parse_mode") and res.parse_mode is DEFAULT_NONE: res = copy(res) - with res._unfrozen(): # pylint: disable=protected-access + with res._unfrozen(): copied = True res.parse_mode = self.defaults.parse_mode if self.defaults else None if hasattr(res, "input_message_content") and res.input_message_content: @@ -598,7 +598,7 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ if not copied: res = copy(res) copied = True - with res.input_message_content._unfrozen(): # pylint: disable=protected-access + with res.input_message_content._unfrozen(): res.input_message_content.parse_mode = ( self.defaults.parse_mode if self.defaults else None ) @@ -608,7 +608,7 @@ def _insert_defaults_for_ilq_results(self, res: "InlineQueryResult") -> "InlineQ ): if not copied: res = copy(res) - with res.input_message_content._unfrozen(): # pylint: disable=protected-access + with res.input_message_content._unfrozen(): res.input_message_content.disable_web_page_preview = ( self.defaults.disable_web_page_preview if self.defaults else None ) From 5800635917c9703fdec17f7bb62acdbaa2721cf5 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 18:10:21 +0100 Subject: [PATCH 72/90] A bit more review --- telegram/_chat.py | 10 +++------- telegram/_games/game.py | 2 +- telegram/_inline/inlinekeyboardmarkup.py | 8 ++++---- telegram/_inline/inlinequeryresultcachedaudio.py | 2 +- telegram/_poll.py | 3 ++- tests/test_chat.py | 9 +++++++++ tests/test_inlinekeyboardmarkup.py | 1 + tests/test_replykeyboardmarkup.py | 4 ++-- tests/test_telegramobject.py | 1 - 9 files changed, 23 insertions(+), 17 deletions(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index c9a491a0aec..d316adde577 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -158,9 +158,6 @@ class Chat(TelegramObject): #collectible-usernames>`_; for private chats, supergroups and channels. Returned only in :meth:`telegram.Bot.get_chat`. - .. versionchanged:: 20.0 - |squenceclassargs| - .. versionadded:: 20.0 emoji_status_custom_emoji_id (:obj:`str`, optional): Custom emoji identifier of emoji status of the other party in a private chat. Returned only in @@ -241,9 +238,8 @@ class Chat(TelegramObject): usernames `_; for private chats, supergroups and channels. Returned only in :meth:`telegram.Bot.get_chat`. - - .. versionchanged:: 20.0 - |tupleclassattrs| + This list is empty if the chat has no active usernames or this chat instance was not + obtained via :meth:`~telegram.bot.get_chat`. .. versionadded:: 20.0 emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji @@ -358,7 +354,7 @@ def __init__( self.join_by_request = join_by_request self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages self.is_forum = is_forum - self.active_usernames = tuple(active_usernames) if active_usernames else None + self.active_usernames = tuple(active_usernames) if active_usernames else () self.emoji_status_custom_emoji_id = emoji_status_custom_emoji_id self._id_attrs = (self.id,) diff --git a/telegram/_games/game.py b/telegram/_games/game.py index bac930c4808..024c1869c15 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -74,7 +74,7 @@ class Game(TelegramObject): game message. Can be automatically edited to include current high scores for the game when the bot calls :meth:`telegram.Bot.set_game_score`, or manually edited using :meth:`telegram.Bot.edit_message_text`. - text_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities that + text_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc. This list is empty if the message does not contain text entities. diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 96d6d4167fa..d2d3fde2c5a 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -41,7 +41,7 @@ class InlineKeyboardMarkup(TelegramObject): Args: inline_keyboard (Sequence[Sequence[:class:`telegram.InlineKeyboardButton`]]): Sequence of - button rows, each represented by a sequence of :class:~`telegram.InlineKeyboardButton` + button rows, each represented by a sequence of :class:`~telegram.InlineKeyboardButton` objects. .. versionchanged:: 20.0 @@ -49,7 +49,7 @@ class InlineKeyboardMarkup(TelegramObject): Attributes: inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of - button rows, each represented by a tuple of :class:~`telegram.InlineKeyboardButton` + button rows, each represented by a tuple of :class:`~telegram.InlineKeyboardButton` objects. .. versionchanged:: 20.0 @@ -68,8 +68,8 @@ def __init__( super().__init__(api_kwargs=api_kwargs) if not check_keyboard_type(inline_keyboard): raise ValueError( - "The parameter `keyboard` should be a sequence of sequences of " - "strings or KeyboardButtons" + "The parameter `inline_keyboard` should be a sequence of sequences of " + "InlineKeyboardButtons" ) # Required self.inline_keyboard = tuple(tuple(row) for row in inline_keyboard) diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 4061214b900..8e6ee3ffde8 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -64,7 +64,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult): caption (:obj:`str`): Optional. Caption, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. - parse_mode (:obj:`str`, optional): |parse_mode| + parse_mode (:obj:`str`): Optional. |parse_mode| caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 diff --git a/telegram/_poll.py b/telegram/_poll.py index 2883f53f7f4..feaf5034627 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -96,7 +96,8 @@ class PollAnswer(TelegramObject): Attributes: poll_id (:obj:`str`): Unique poll identifier. user (:class:`telegram.User`): The user, who changed the answer to the poll. - option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. + option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. May be + empty if the user retracted their vote. .. versionchanged:: 20.0 |tupleclassattrs| diff --git a/tests/test_chat.py b/tests/test_chat.py index ebc7e02c3f7..6abbd7e0735 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -163,6 +163,15 @@ def test_to_dict(self, chat): assert chat_dict["active_usernames"] == list(chat.active_usernames) assert chat_dict["emoji_status_custom_emoji_id"] == chat.emoji_status_custom_emoji_id + def test_always_tuples_attributes(self): + chat = Chat( + id=123, + title="title", + type=Chat.PRIVATE, + ) + assert isinstance(chat.active_usernames, tuple) + assert chat.active_usernames == () + def test_enum_init(self): chat = Chat(id=1, type="foo") assert chat.type == "foo" diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py index e1b404e4787..5d602933756 100644 --- a/tests/test_inlinekeyboardmarkup.py +++ b/tests/test_inlinekeyboardmarkup.py @@ -97,6 +97,7 @@ def test_wrong_keyboard_inputs(self): ) with pytest.raises(ValueError): InlineKeyboardMarkup("strings_are_not_allowed") + with pytest.raises(ValueError): InlineKeyboardMarkup(["strings_are_not_allowed"], ["in_the_rows_either"]) with pytest.raises(ValueError): InlineKeyboardMarkup(InlineKeyboardButton("b1", "1")) diff --git a/tests/test_replykeyboardmarkup.py b/tests/test_replykeyboardmarkup.py index 5a8ca505722..8cd3bb877c8 100644 --- a/tests/test_replykeyboardmarkup.py +++ b/tests/test_replykeyboardmarkup.py @@ -97,8 +97,7 @@ def test_from_column(self): def test_expected_values(self, reply_keyboard_markup): assert isinstance(reply_keyboard_markup.keyboard, tuple) - for row in reply_keyboard_markup.keyboard: - assert isinstance(row, tuple) + assert all(isinstance(row, tuple) for row in reply_keyboard_markup.keyboard) assert isinstance(reply_keyboard_markup.keyboard[0][0], KeyboardButton) assert isinstance(reply_keyboard_markup.keyboard[0][1], KeyboardButton) assert reply_keyboard_markup.resize_keyboard == self.resize_keyboard @@ -110,6 +109,7 @@ def test_wrong_keyboard_inputs(self): ReplyKeyboardMarkup([["button1"], 1]) with pytest.raises(ValueError): ReplyKeyboardMarkup("strings_are_not_allowed") + with pytest.raises(ValueError): ReplyKeyboardMarkup(["strings_are_not_allowed"], ["in_the_rows_either"]) with pytest.raises(ValueError): ReplyKeyboardMarkup(KeyboardButton("button1")) diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index a81a5c03163..67755f157fc 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -434,7 +434,6 @@ def test_subclasses_are_frozen(self, cls): r"\n\s*with self\.\_unfrozen\(\)\:", inspect.getsource(cls.__init__) ) - print(last_line_freezes, uses_with_unfrozen) assert last_line_freezes or uses_with_unfrozen, f"{cls.__name__} is not frozen correctly" def test_freeze_unfreeze(self): From 2fafd40ecb85f028d41efe83158fb9d171170036 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 18:16:08 +0100 Subject: [PATCH 73/90] squecne -> sequence --- docs/substitutions/global.rst | 2 +- immutabilize.py | 2 +- telegram/_files/inputmedia.py | 12 ++++++------ telegram/_files/sticker.py | 2 +- telegram/_games/game.py | 4 ++-- telegram/_inline/inlinekeyboardmarkup.py | 2 +- telegram/_inline/inlinequeryresultaudio.py | 2 +- telegram/_inline/inlinequeryresultcachedaudio.py | 2 +- telegram/_inline/inlinequeryresultcacheddocument.py | 2 +- telegram/_inline/inlinequeryresultcachedgif.py | 2 +- telegram/_inline/inlinequeryresultcachedmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultcachedphoto.py | 2 +- telegram/_inline/inlinequeryresultcachedvoice.py | 2 +- telegram/_inline/inlinequeryresultdocument.py | 2 +- telegram/_inline/inlinequeryresultgif.py | 2 +- telegram/_inline/inlinequeryresultmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultphoto.py | 2 +- telegram/_inline/inlinequeryresultvideo.py | 4 ++-- telegram/_inline/inlinequeryresultvoice.py | 2 +- telegram/_inline/inputinvoicemessagecontent.py | 4 ++-- telegram/_message.py | 10 +++++----- telegram/_passport/encryptedpassportelement.py | 4 ++-- telegram/_passport/passportdata.py | 2 +- telegram/_payment/shippingoption.py | 2 +- telegram/_poll.py | 6 +++--- telegram/_userprofilephotos.py | 2 +- telegram/_videochat.py | 2 +- telegram/_webhookinfo.py | 2 +- 28 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 7e91395343a..68c5bafb02f 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -42,6 +42,6 @@ .. |reply_to_msg_id| replace:: If the message is a reply, ID of the original message. -.. |squenceclassargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list. The input is converted to a tuple. +.. |sequenceeclassargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list. The input is converted to a tuple. .. |tupleclassattrs| replace:: This attribute is now an immutable tuple. diff --git a/immutabilize.py b/immutabilize.py index c944cdcb7b3..25e9c6d78b4 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -85,7 +85,7 @@ class_source_lines[j - 1] += ( f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" - " |squenceclassargs|" + " |sequenceeclassargs|" ) if class_source_lines[j]: class_source_lines[j - 1] += "\n" diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 5cfe4a7f581..642a96ee86a 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -58,7 +58,7 @@ class InputMedia(TelegramObject): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| parse_mode (:obj:`str`, optional): |parse_mode| @@ -136,7 +136,7 @@ class InputMediaAnimation(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| width (:obj:`int`, optional): Animation width. height (:obj:`int`, optional): Animation height. @@ -222,7 +222,7 @@ class InputMediaPhoto(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.PHOTO`. @@ -293,7 +293,7 @@ class InputMediaVideo(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| width (:obj:`int`, optional): Video width. height (:obj:`int`, optional): Video height. @@ -395,7 +395,7 @@ class InputMediaAudio(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| duration (:obj:`int`): Duration of the audio in seconds as defined by sender. performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio @@ -487,7 +487,7 @@ class InputMediaDocument(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ optional): |thumbdocstringnopath| diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 1a13f7c355b..75852c431e0 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -209,7 +209,7 @@ class StickerSet(TelegramObject): stickers (Sequence[:class:`telegram.Sticker`]): List of all set stickers. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| sticker_type (:obj:`str`): Type of stickers in the set, currently one of :attr:`telegram.Sticker.REGULAR`, :attr:`telegram.Sticker.MASK`, diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 024c1869c15..9dbc2b976b2 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -45,7 +45,7 @@ class Game(TelegramObject): message in chats. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| text (:obj:`str`, optional): Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game @@ -56,7 +56,7 @@ class Game(TelegramObject): appear in text, such as usernames, URLs, bot commands, etc. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| animation (:class:`telegram.Animation`, optional): Animation that will be displayed in the game message in chats. Upload via `BotFather `_. diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index d2d3fde2c5a..133c734dc6a 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -45,7 +45,7 @@ class InlineKeyboardMarkup(TelegramObject): objects. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 48884481d20..fe12d5d0513 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -51,7 +51,7 @@ class InlineQueryResultAudio(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 8e6ee3ffde8..e02b0567fc7 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -48,7 +48,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index b082233a3e2..c984f225d99 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -50,7 +50,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index daecf6a179b..e7a78af35a9 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -50,7 +50,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index 8c22aa558df..b5b048e4317 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -50,7 +50,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 0452abbef5c..abf92e2e4cf 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -51,7 +51,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index ee8d9f5c194..20c5799c71f 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -49,7 +49,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index af9109be9dc..6f6ec06415e 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -49,7 +49,7 @@ class InlineQueryResultDocument(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| document_url (:obj:`str`): A valid URL for the file. mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index d069f886200..88243a963bd 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -56,7 +56,7 @@ class InlineQueryResultGif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 2e23335acc4..c489a821fb7 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -56,7 +56,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index bfa35e52327..01eaa3455e3 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -54,7 +54,7 @@ class InlineQueryResultPhoto(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index be82d07d82a..0f4920a5a1c 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -56,7 +56,7 @@ class InlineQueryResultVideo(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| video_width (:obj:`int`, optional): Video width. video_height (:obj:`int`, optional): Video height. @@ -85,7 +85,7 @@ class InlineQueryResultVideo(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| video_width (:obj:`int`): Optional. Video width. video_height (:obj:`int`): Optional. Video height. diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index d7d53987074..2977a44aa13 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -50,7 +50,7 @@ class InlineQueryResultVoice(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| voice_duration (:obj:`int`, optional): Recording duration in seconds. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 31ad6c7a8b0..776d2b039ce 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -56,7 +56,7 @@ class InputInvoiceMessageContent(InputMessageContent): etc.) .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the *smallest* units of the currency (integer, **not** float/double). For example, for a @@ -71,7 +71,7 @@ class InputInvoiceMessageContent(InputMessageContent): :attr:`max_tip_amount`. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| provider_data (:obj:`str`, optional): An object for data about the invoice, which will be shared with the payment provider. A detailed description of the required diff --git a/telegram/_message.py b/telegram/_message.py index 6f5cde08b7f..2e1f5961702 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -146,7 +146,7 @@ class Message(TelegramObject): This list is empty if the message does not contain entities. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the @@ -155,7 +155,7 @@ class Message(TelegramObject): caption entities. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| audio (:class:`telegram.Audio`, optional): Message is an audio file, information about the file. @@ -169,7 +169,7 @@ class Message(TelegramObject): of the photo. This list is empty if the message does not contain a photo. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information about the sticker. @@ -184,7 +184,7 @@ class Message(TelegramObject): members). This list is empty if the message does not contain new chat members. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| caption (:obj:`str`, optional): Caption for the animation, audio, document, photo, video or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. @@ -202,7 +202,7 @@ class Message(TelegramObject): this value. This list is empty if the message does not contain a new chat photo. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| delete_chat_photo (:obj:`bool`, optional): Service message: The chat photo was deleted. group_chat_created (:obj:`bool`, optional): Service message: The group has been created. diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 35d8c94fad9..2fb3a20c2ee 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -64,7 +64,7 @@ class EncryptedPassportElement(TelegramObject): "rental_agreement", "passport_registration" and "temporary_registration" types. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| front_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the front side of the document, provided by the user. Available for "passport", @@ -83,7 +83,7 @@ class EncryptedPassportElement(TelegramObject): "temporary_registration" types. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 7f66e66b1ad..61e5d99662c 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -43,7 +43,7 @@ class PassportData(TelegramObject): the bot. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index cfdc2087ee2..cae746a7f56 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -41,7 +41,7 @@ class ShippingOption(TelegramObject): prices (Sequence[:class:`telegram.LabeledPrice`]): List of price portions. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: id (:obj:`str`): Shipping option identifier. diff --git a/telegram/_poll.py b/telegram/_poll.py index feaf5034627..4f04ea2a3ce 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -91,7 +91,7 @@ class PollAnswer(TelegramObject): user. May be empty if the user retracted their vote. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: poll_id (:obj:`str`): Unique poll identifier. @@ -148,7 +148,7 @@ class Poll(TelegramObject): options (Sequence[:class:`PollOption`]): List of poll options. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. @@ -166,7 +166,7 @@ class Poll(TelegramObject): .. versionchanged:: 20.0 * This attribute is now always a (possibly empty) list and never :obj:`None`. - * |squenceclassargs| + * |sequenceeclassargs| open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active after creation. close_date (:obj:`datetime.datetime`, optional): Point in time (Unix timestamp) when the diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 763024c5bc6..fad8edc2a31 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -39,7 +39,7 @@ class UserProfilePhotos(TelegramObject): to 4 sizes each). .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: total_count (:obj:`int`): Total number of profile pictures. diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 17c192ee924..23c57abaab3 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -93,7 +93,7 @@ class VideoChatParticipantsInvited(TelegramObject): users (Sequence[:class:`telegram.User`]): New members that were invited to the video chat. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| Attributes: users (Tuple[:class:`telegram.User`]): New members that were invited to the video chat. diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 00a1d50d33e..6e3f53979d3 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -59,7 +59,7 @@ class WebhookInfo(TelegramObject): :attr:`telegram.Update.chat_member`. .. versionchanged:: 20.0 - |squenceclassargs| + |sequenceeclassargs| last_synchronization_error_date (:obj:`int`, optional): Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters. From 23123f5cc8ded748e1b7c8d347359c774d051f99 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 18:37:30 +0100 Subject: [PATCH 74/90] a few more docstring transitions --- telegram/_inline/inputtextmessagecontent.py | 11 +++++++++-- telegram/_message.py | 14 +++++++------- telegram/_poll.py | 7 ++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index ed6e0f02ace..eba6fc8476c 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -41,7 +41,11 @@ class InputTextMessageContent(InputMessageContent): :tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after entities parsing. parse_mode (:obj:`str`, optional): |parse_mode| - entities (List[:class:`telegram.MessageEntity`], optional): |caption_entities| + entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| + + .. versionchanged:: 20.0 + |sequenceeclassargs| + disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the sent message. @@ -50,7 +54,10 @@ class InputTextMessageContent(InputMessageContent): 1-:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - entities (List[:class:`telegram.MessageEntity`]): Optional. |caption_entities| + entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| + + .. versionchanged:: 20.0 + |tupleclassattrs| disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in the sent message. diff --git a/telegram/_message.py b/telegram/_message.py index 2e1f5961702..39ec6df2643 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -165,8 +165,8 @@ class Message(TelegramObject): about the animation. For backward compatibility, when this field is set, the document field will also be set. game (:class:`telegram.Game`, optional): Message is a game, information about the game. - photo (List[:class:`telegram.PhotoSize`], optional): Message is a photo, available sizes - of the photo. This list is empty if the message does not contain a photo. + photo (Sequence[:class:`telegram.PhotoSize`], optional): Message is a photo, available + sizes of the photo. This list is empty if the message does not contain a photo. .. versionchanged:: 20.0 |sequenceeclassargs| @@ -179,9 +179,9 @@ class Message(TelegramObject): the file. video_note (:class:`telegram.VideoNote`, optional): Message is a video note, information about the video message. - new_chat_members (List[:class:`telegram.User`], optional): New members that were added to - the group or supergroup and information about them (the bot itself may be one of these - members). This list is empty if the message does not contain new chat members. + new_chat_members (Sequence[:class:`telegram.User`], optional): New members that were added + to the group or supergroup and information about them (the bot itself may be one of + these members). This list is empty if the message does not contain new chat members. .. versionchanged:: 20.0 |sequenceeclassargs| @@ -198,8 +198,8 @@ class Message(TelegramObject): left_chat_member (:class:`telegram.User`, optional): A member was removed from the group, information about them (this member may be the bot itself). new_chat_title (:obj:`str`, optional): A chat title was changed to this value. - new_chat_photo (List[:class:`telegram.PhotoSize`], optional): A chat photo was changed to - this value. This list is empty if the message does not contain a new chat photo. + new_chat_photo (Sequence[:class:`telegram.PhotoSize`], optional): A chat photo was changed + to this value. This list is empty if the message does not contain a new chat photo. .. versionchanged:: 20.0 |sequenceeclassargs| diff --git a/telegram/_poll.py b/telegram/_poll.py index 4f04ea2a3ce..ccbc5266f58 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -159,9 +159,10 @@ class Poll(TelegramObject): explanation (:obj:`str`, optional): Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-:tg-const:`telegram.Poll.MAX_EXPLANATION_LENGTH` characters. - explanation_entities (List[:class:`telegram.MessageEntity`], optional): Special entities - like usernames, URLs, bot commands, etc. that appear in the :attr:`explanation`. - This list is empty if the message does not contain explanation entities. + explanation_entities (Sequence[:class:`telegram.MessageEntity`], optional): Special + entities like usernames, URLs, bot commands, etc. that appear in the + :attr:`explanation`. This list is empty if the message does not contain explanation + entities. .. versionchanged:: 20.0 From 109f355f66d518be0ce0ca72f6f73f75ecbd41a6 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 18:57:00 +0100 Subject: [PATCH 75/90] A few "List" -> "Tuple" --- telegram/_bot.py | 4 ++-- telegram/_callbackquery.py | 4 ++-- telegram/_chat.py | 9 +++++---- telegram/_message.py | 8 ++++---- telegram/_passport/passportdata.py | 19 +++++++++++-------- telegram/_user.py | 5 +++-- telegram/ext/_extbot.py | 2 +- 7 files changed, 28 insertions(+), 23 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 58cb25e2521..0aeb8d41f3f 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -6940,14 +6940,14 @@ async def get_forum_topic_icon_stickers( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List[Sticker]: + ) -> Tuple[Sticker]: """Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. Requires no parameters. .. versionadded:: 20.0 Returns: - List[:class:`telegram.Sticker`] + Tuple[:class:`telegram.Sticker`] Raises: :class:`telegram.error.TelegramError` diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index 3ea1ff87a7e..ed1a037f7d7 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -592,7 +592,7 @@ async def get_game_high_scores( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List["GameHighScore"]: + ) -> Tuple["GameHighScore"]: """Shortcut for either:: await update.callback_query.message.get_game_high_score(*args, **kwargs) @@ -608,7 +608,7 @@ async def get_game_high_scores( :meth:`telegram.Message.get_game_high_scores`. Returns: - List[:class:`telegram.GameHighScore`] + Tuple[:class:`telegram.GameHighScore`] """ if self.inline_message_id: diff --git a/telegram/_chat.py b/telegram/_chat.py index d316adde577..4d4dc6b465f 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -550,7 +550,7 @@ async def get_administrators( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List["ChatMember"]: + ) -> Tuple["ChatMember"]: """Shortcut for:: await bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs) @@ -559,7 +559,7 @@ async def get_administrators( :meth:`telegram.Bot.get_chat_administrators`. Returns: - List[:class:`telegram.ChatMember`]: A list of administrators in a chat. An Array of + Tuple[:class:`telegram.ChatMember`]: A tuple of administrators in a chat. An Array of :class:`telegram.ChatMember` objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned. @@ -1296,7 +1296,7 @@ async def send_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> List["Message"]: + ) -> Tuple["Message"]: """Shortcut for:: await bot.send_media_group(update.effective_chat.id, *args, **kwargs) @@ -1304,7 +1304,8 @@ async def send_media_group( For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. Returns: - List[:class:`telegram.Message`]: On success, instance representing the message posted. + Tuple[:class:`telegram.Message`:] On success, a tuple of :class:`~telegram.Message` + instances that were sent is returned. """ return await self.get_bot().send_media_group( diff --git a/telegram/_message.py b/telegram/_message.py index 39ec6df2643..c6fd4b5cb8a 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -1137,7 +1137,7 @@ async def reply_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> List["Message"]: + ) -> Tuple["Message"]: """Shortcut for:: await bot.send_media_group(update.effective_message.chat_id, *args, **kwargs) @@ -1151,7 +1151,7 @@ async def reply_media_group( chats. Returns: - List[:class:`telegram.Message`]: An array of the sent Messages. + Tuple[:class:`telegram.Message`]: An array of the sent Messages. Raises: :class:`telegram.error.TelegramError` @@ -2638,7 +2638,7 @@ async def get_game_high_scores( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List["GameHighScore"]: + ) -> Tuple["GameHighScore"]: """Shortcut for:: await bot.get_game_high_scores( @@ -2654,7 +2654,7 @@ async def get_game_high_scores( behaviour is undocumented and might be changed by Telegram. Returns: - List[:class:`telegram.GameHighScore`] + Tuple[:class:`telegram.GameHighScore`] """ return await self.get_bot().get_game_high_scores( chat_id=self.chat_id, diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 61e5d99662c..cdd68f4944e 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -17,7 +17,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/]. """Contains information about Telegram Passport data shared with the bot by the user.""" -from typing import TYPE_CHECKING, List, Optional, Sequence +from typing import TYPE_CHECKING, Optional, Sequence, Tuple from telegram._passport.credentials import EncryptedCredentials from telegram._passport.encryptedpassportelement import EncryptedPassportElement @@ -74,7 +74,7 @@ def __init__( self.data = tuple(data) self.credentials = credentials - self._decrypted_data: Optional[List[EncryptedPassportElement]] = None + self._decrypted_data: Optional[Tuple[EncryptedPassportElement]] = None self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) self._freeze() @@ -93,23 +93,26 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PassportData return super().de_json(data=data, bot=bot) @property - def decrypted_data(self) -> List[EncryptedPassportElement]: + def decrypted_data(self) -> Tuple[EncryptedPassportElement]: """ - List[:class:`telegram.EncryptedPassportElement`]: Lazily decrypt and return information + Tuple[:class:`telegram.EncryptedPassportElement`]: Lazily decrypt and return information about documents and other Telegram Passport elements which were shared with the bot. + .. versionchanged:: 20.0 + Returns a tuple instead of a list. + Raises: telegram.error.PassportDecryptionError: Decryption failed. Usually due to bad private/public key but can also suggest malformed/tampered data. """ if self._decrypted_data is None: - self._decrypted_data = [ - EncryptedPassportElement.de_json_decrypted( # type: ignore[misc] + self._decrypted_data = tuple( # type: ignore[assignment] + EncryptedPassportElement.de_json_decrypted( element.to_dict(), self.get_bot(), self.decrypted_credentials ) for element in self.data - ] - return self._decrypted_data + ) + return self._decrypted_data # type: ignore[return-value] @property def decrypted_credentials(self) -> "Credentials": diff --git a/telegram/_user.py b/telegram/_user.py index 0f4e7c64d18..df2811f4950 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -484,7 +484,7 @@ async def send_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> List["Message"]: + ) -> Tuple["Message"]: """Shortcut for:: await bot.send_media_group(update.effective_user.id, *args, **kwargs) @@ -492,7 +492,8 @@ async def send_media_group( For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. Returns: - List[:class:`telegram.Message`:] On success, instance representing the message posted. + Tuple[:class:`telegram.Message`:] On success, a tuple of :class:`~telegram.Message` + instances that were sent is returned. """ return await self.get_bot().send_media_group( diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 6b2a14e53f8..349578fe2af 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -1610,7 +1610,7 @@ async def get_forum_topic_icon_stickers( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, rate_limit_args: RLARGS = None, - ) -> List[Sticker]: + ) -> Tuple[Sticker]: return await super().get_forum_topic_icon_stickers( read_timeout=read_timeout, write_timeout=write_timeout, From 93e5af85c227d59989227d5718719c8a663f531e Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 19:09:53 +0100 Subject: [PATCH 76/90] Rename every mention of "list" in return docstrings to tuple - it's just one :D --- telegram/_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 0aeb8d41f3f..aa85afd9e52 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -3834,7 +3834,7 @@ async def get_chat_administrators( chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| Returns: - Tuple[:class:`telegram.ChatMember`]: On success, returns a list of ``ChatMember`` + Tuple[:class:`telegram.ChatMember`]: On success, returns a tuple of ``ChatMember`` objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned. From 687f572be157fdac92ea02ce5cab4c6466e04571 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 19:17:35 +0100 Subject: [PATCH 77/90] add versionchanged to TO --- telegram/_chat.py | 2 +- telegram/_telegramobject.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index 4d4dc6b465f..13e340a4d02 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -239,7 +239,7 @@ class Chat(TelegramObject): #collectible-usernames>`_; for private chats, supergroups and channels. Returned only in :meth:`telegram.Bot.get_chat`. This list is empty if the chat has no active usernames or this chat instance was not - obtained via :meth:`~telegram.bot.get_chat`. + obtained via :meth:`~telegram.Bot.get_chat`. .. versionadded:: 20.0 emoji_status_custom_emoji_id (:obj:`str`): Optional. Custom emoji identifier of emoji diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 02423093b41..bfe41673994 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -65,6 +65,9 @@ class TelegramObject: * String representations objects of this type was overhauled. See :meth:`__repr__` for details. As this class doesn't implement :meth:`object.__str__`, the default implementation will be used, which is equivalent to :meth:`__repr__`. + * Objects of this class (or subclasses) are now immutable. This means that you can't set + or delete attributes anymore. Moreover, attributes that were formerly of type + :obj:`list` are now of type :obj:`tuple`. Arguments: api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| From 662ebc8af116fc599d23b8ab2e15c2767aa650c8 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Thu, 1 Dec 2022 23:43:27 +0100 Subject: [PATCH 78/90] fix two tests --- telegram/_utils/markup.py | 2 +- tests/test_inlinekeyboardmarkup.py | 2 +- tests/test_replykeyboardmarkup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telegram/_utils/markup.py b/telegram/_utils/markup.py index 50a94fc5c25..26b07a77fde 100644 --- a/telegram/_utils/markup.py +++ b/telegram/_utils/markup.py @@ -40,6 +40,6 @@ def check_keyboard_type(keyboard: object) -> bool: if not isinstance(keyboard, Sequence) or isinstance(keyboard, (str, bytes)): return False for row in keyboard: - if not isinstance(row, Sequence) or isinstance(keyboard, (str, bytes)): + if not isinstance(row, Sequence) or isinstance(row, (str, bytes)): return False return True diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py index 5d602933756..d852f685bf7 100644 --- a/tests/test_inlinekeyboardmarkup.py +++ b/tests/test_inlinekeyboardmarkup.py @@ -98,7 +98,7 @@ def test_wrong_keyboard_inputs(self): with pytest.raises(ValueError): InlineKeyboardMarkup("strings_are_not_allowed") with pytest.raises(ValueError): - InlineKeyboardMarkup(["strings_are_not_allowed"], ["in_the_rows_either"]) + InlineKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"]) with pytest.raises(ValueError): InlineKeyboardMarkup(InlineKeyboardButton("b1", "1")) diff --git a/tests/test_replykeyboardmarkup.py b/tests/test_replykeyboardmarkup.py index 8cd3bb877c8..56b1366e016 100644 --- a/tests/test_replykeyboardmarkup.py +++ b/tests/test_replykeyboardmarkup.py @@ -110,7 +110,7 @@ def test_wrong_keyboard_inputs(self): with pytest.raises(ValueError): ReplyKeyboardMarkup("strings_are_not_allowed") with pytest.raises(ValueError): - ReplyKeyboardMarkup(["strings_are_not_allowed"], ["in_the_rows_either"]) + ReplyKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"]) with pytest.raises(ValueError): ReplyKeyboardMarkup(KeyboardButton("button1")) From 67832569ded7f23a0a5cddf342b41435b416b751 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:03:20 +0100 Subject: [PATCH 79/90] Tuple type hints: arbitrary length --- telegram/_bot.py | 2 +- telegram/_callbackquery.py | 2 +- telegram/_chat.py | 4 ++-- telegram/_message.py | 4 ++-- telegram/_passport/passportdata.py | 2 +- telegram/_user.py | 2 +- telegram/ext/_extbot.py | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index aa85afd9e52..24ea93d4605 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -6940,7 +6940,7 @@ async def get_forum_topic_icon_stickers( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Tuple[Sticker]: + ) -> Tuple[Sticker, ...]: """Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. Requires no parameters. diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index ed1a037f7d7..a6963f46146 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -592,7 +592,7 @@ async def get_game_high_scores( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Tuple["GameHighScore"]: + ) -> Tuple["GameHighScore", ...]: """Shortcut for either:: await update.callback_query.message.get_game_high_score(*args, **kwargs) diff --git a/telegram/_chat.py b/telegram/_chat.py index 13e340a4d02..013e6de1477 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -550,7 +550,7 @@ async def get_administrators( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Tuple["ChatMember"]: + ) -> Tuple["ChatMember", ...]: """Shortcut for:: await bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs) @@ -1296,7 +1296,7 @@ async def send_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> Tuple["Message"]: + ) -> Tuple["Message", ...]: """Shortcut for:: await bot.send_media_group(update.effective_chat.id, *args, **kwargs) diff --git a/telegram/_message.py b/telegram/_message.py index c6fd4b5cb8a..194ee8a671a 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -1137,7 +1137,7 @@ async def reply_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> Tuple["Message"]: + ) -> Tuple["Message", ...]: """Shortcut for:: await bot.send_media_group(update.effective_message.chat_id, *args, **kwargs) @@ -2638,7 +2638,7 @@ async def get_game_high_scores( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Tuple["GameHighScore"]: + ) -> Tuple["GameHighScore", ...]: """Shortcut for:: await bot.get_game_high_scores( diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index cdd68f4944e..676ae57048e 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -93,7 +93,7 @@ def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PassportData return super().de_json(data=data, bot=bot) @property - def decrypted_data(self) -> Tuple[EncryptedPassportElement]: + def decrypted_data(self) -> Tuple[EncryptedPassportElement, ...]: """ Tuple[:class:`telegram.EncryptedPassportElement`]: Lazily decrypt and return information about documents and other Telegram Passport elements which were shared with the bot. diff --git a/telegram/_user.py b/telegram/_user.py index df2811f4950..ba0e2364757 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -484,7 +484,7 @@ async def send_media_group( caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, - ) -> Tuple["Message"]: + ) -> Tuple["Message", ...]: """Shortcut for:: await bot.send_media_group(update.effective_user.id, *args, **kwargs) diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 349578fe2af..3bc7f3c1824 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -1610,7 +1610,7 @@ async def get_forum_topic_icon_stickers( pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, rate_limit_args: RLARGS = None, - ) -> Tuple[Sticker]: + ) -> Tuple[Sticker, ...]: return await super().get_forum_topic_icon_stickers( read_timeout=read_timeout, write_timeout=write_timeout, From d150dabbd9db439ee7a45dcb62d8ea8ab68e0cb7 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:21:47 +0100 Subject: [PATCH 80/90] Filter out Nones in de_list and adapt some type hinting --- telegram/_bot.py | 199 +++++++++++++++-------------- telegram/_passport/passportfile.py | 13 +- telegram/_telegramobject.py | 8 +- telegram/ext/_aioratelimiter.py | 10 +- telegram/ext/_baseratelimiter.py | 6 +- telegram/ext/_extbot.py | 2 +- telegram/request/_baserequest.py | 6 +- tests/test_telegramobject.py | 13 ++ 8 files changed, 140 insertions(+), 117 deletions(-) diff --git a/telegram/_bot.py b/telegram/_bot.py index 24ea93d4605..4cfe836ac1b 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -384,7 +384,10 @@ async def _post( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union[bool, JSONDict, None]: + ) -> Any: + # We know that the return type is Union[bool, JSONDict, List[JSONDict]], but it's hard + # to tell mypy which methods expects which of these return values and `Any` saves us a + # lot of `type: ignore` comments if data is None: data = {} @@ -415,7 +418,7 @@ async def _do_post( write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Union[bool, JSONDict, None]: + ) -> Union[bool, JSONDict, List[JSONDict]]: # This also converts datetimes into timestamps. # We don't do this earlier so that _insert_defaults (see above) has a chance to convert # to the default timezone in case this is called by ExtBot @@ -457,13 +460,15 @@ async def _send_message( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union[bool, Message]: + ) -> Any: """Protected method to send or edit messages of any type. It is here to reduce repetition of if-else closes in the different bot methods, i.e. this method takes care of adding its parameters to `data` if appropriate. Depending on the bot method, returns either `True` or the message. + However, it's hard to tell mypy which methods expects which of these return values and + using `Any` instead saves us a lot of `type: ignore` comments """ # We don't check if (DEFAULT_)None here, so that _post is able to insert the defaults # correctly, if necessary @@ -501,7 +506,7 @@ async def _send_message( if result is True: return result - return Message.de_json(result, self) # type: ignore[return-value, arg-type] + return Message.de_json(result, self) async def initialize(self) -> None: """Initialize resources used by this class. Currently calls :meth:`get_me` to @@ -673,7 +678,7 @@ async def get_me( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - self._bot_user = User.de_json(result, self) # type: ignore[arg-type] + self._bot_user = User.de_json(result, self) return self._bot_user # type: ignore[return-value] @_log @@ -742,7 +747,7 @@ async def send_message( if entities: data["entities"] = entities - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendMessage", data, reply_to_message_id=reply_to_message_id, @@ -812,7 +817,7 @@ async def delete_message( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def forward_message( @@ -872,7 +877,7 @@ async def forward_message( data["from_chat_id"] = from_chat_id if message_id: data["message_id"] = message_id - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "forwardMessage", data, disable_notification=disable_notification, @@ -963,7 +968,7 @@ async def send_photo( "photo": self._parse_file_input(photo, PhotoSize, filename=filename), } - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendPhoto", data, reply_to_message_id=reply_to_message_id, @@ -1095,7 +1100,7 @@ async def send_audio( if thumb: data["thumb"] = self._parse_file_input(thumb, attach=True) - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendAudio", data, reply_to_message_id=reply_to_message_id, @@ -1217,7 +1222,7 @@ async def send_document( if thumb: data["thumb"] = self._parse_file_input(thumb, attach=True) - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendDocument", data, reply_to_message_id=reply_to_message_id, @@ -1296,7 +1301,7 @@ async def send_sticker( """ data: JSONDict = {"chat_id": chat_id, "sticker": self._parse_file_input(sticker, Sticker)} - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendSticker", data, reply_to_message_id=reply_to_message_id, @@ -1432,7 +1437,7 @@ async def send_video( if thumb: data["thumb"] = self._parse_file_input(thumb, attach=True) - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendVideo", data, reply_to_message_id=reply_to_message_id, @@ -1555,7 +1560,7 @@ async def send_video_note( if thumb: data["thumb"] = self._parse_file_input(thumb, attach=True) - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendVideoNote", data, reply_to_message_id=reply_to_message_id, @@ -1683,7 +1688,7 @@ async def send_animation( if thumb: data["thumb"] = self._parse_file_input(thumb, attach=True) - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendAnimation", data, reply_to_message_id=reply_to_message_id, @@ -1798,7 +1803,7 @@ async def send_voice( if duration: data["duration"] = duration - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendVoice", data, reply_to_message_id=reply_to_message_id, @@ -1945,7 +1950,7 @@ async def send_media_group( api_kwargs=api_kwargs, ) - return Message.de_list(result, self) # type: ignore + return Message.de_list(result, self) @_log async def send_location( @@ -2050,7 +2055,7 @@ async def send_location( if proximity_alert_radius: data["proximity_alert_radius"] = proximity_alert_radius - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendLocation", data, reply_to_message_id=reply_to_message_id, @@ -2340,7 +2345,7 @@ async def send_venue( if google_place_type: data["google_place_type"] = google_place_type - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendVenue", data, reply_to_message_id=reply_to_message_id, @@ -2449,7 +2454,7 @@ async def send_contact( if vcard: data["vcard"] = vcard - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendContact", data, reply_to_message_id=reply_to_message_id, @@ -2515,7 +2520,7 @@ async def send_game( """ data: JSONDict = {"chat_id": chat_id, "game_short_name": game_short_name} - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendGame", data, reply_to_message_id=reply_to_message_id, @@ -2575,7 +2580,7 @@ async def send_chat_action( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result def _effective_inline_results( # skipcq: PYL-R0201 self, @@ -2776,7 +2781,7 @@ async def answer_inline_query( if switch_pm_parameter: data["switch_pm_parameter"] = switch_pm_parameter - return await self._post( # type: ignore[return-value] + return await self._post( "answerInlineQuery", data, read_timeout=read_timeout, @@ -2836,7 +2841,7 @@ async def get_user_profile_photos( api_kwargs=api_kwargs, ) - return UserProfilePhotos.de_json(result, self) # type: ignore[arg-type,return-value] + return UserProfilePhotos.de_json(result, self) # type: ignore[return-value] @_log async def get_file( @@ -2897,14 +2902,10 @@ async def get_file( api_kwargs=api_kwargs, ) - if result.get("file_path") and not is_local_file( # type: ignore[union-attr] - result["file_path"] # type: ignore[index] - ): - result[ # type: ignore[index] - "file_path" - ] = f"{self._base_file_url}/{result['file_path']}" # type: ignore[index] + if result.get("file_path") and not is_local_file(result["file_path"]): + result["file_path"] = f"{self._base_file_url}/{result['file_path']}" - return File.de_json(result, self) # type: ignore[return-value, arg-type] + return File.de_json(result, self) # type: ignore[return-value] @_log async def ban_chat_member( @@ -2973,7 +2974,7 @@ async def ban_chat_member( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def ban_chat_sender_chat( @@ -3021,7 +3022,7 @@ async def ban_chat_sender_chat( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def unban_chat_member( @@ -3073,7 +3074,7 @@ async def unban_chat_member( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def unban_chat_sender_chat( @@ -3118,7 +3119,7 @@ async def unban_chat_sender_chat( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def answer_callback_query( @@ -3191,7 +3192,7 @@ async def answer_callback_query( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def edit_message_text( @@ -3562,7 +3563,7 @@ async def get_updates( else: self._logger.debug("No new updates found.") - return Update.de_list(result, self) # type: ignore[return-value] + return Update.de_list(result, self) @_log async def set_webhook( @@ -3688,7 +3689,7 @@ async def set_webhook( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def delete_webhook( @@ -3731,7 +3732,7 @@ async def delete_webhook( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def leave_chat( @@ -3770,7 +3771,7 @@ async def leave_chat( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def get_chat( @@ -3809,7 +3810,7 @@ async def get_chat( api_kwargs=api_kwargs, ) - return Chat.de_json(result, self) # type: ignore[return-value, arg-type] + return Chat.de_json(result, self) # type: ignore[return-value] @_log async def get_chat_administrators( @@ -3853,7 +3854,7 @@ async def get_chat_administrators( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return ChatMember.de_list(result, self) # type: ignore + return ChatMember.de_list(result, self) @_log async def get_chat_member_count( @@ -3892,7 +3893,7 @@ async def get_chat_member_count( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def get_chat_member( @@ -3931,7 +3932,7 @@ async def get_chat_member( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return ChatMember.de_json(result, self) # type: ignore[return-value, arg-type] + return ChatMember.de_json(result, self) # type: ignore[return-value] @_log async def set_chat_sticker_set( @@ -3968,7 +3969,7 @@ async def set_chat_sticker_set( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def delete_chat_sticker_set( @@ -4002,7 +4003,7 @@ async def delete_chat_sticker_set( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result async def get_webhook_info( self, @@ -4031,7 +4032,7 @@ async def get_webhook_info( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return WebhookInfo.de_json(result, self) # type: ignore[return-value, arg-type] + return WebhookInfo.de_json(result, self) # type: ignore[return-value] @_log async def set_game_score( @@ -4164,7 +4165,7 @@ async def get_game_high_scores( api_kwargs=api_kwargs, ) - return GameHighScore.de_list(result, self) # type: ignore + return GameHighScore.de_list(result, self) @_log async def send_invoice( @@ -4344,7 +4345,7 @@ async def send_invoice( if send_email_to_provider is not None: data["send_email_to_provider"] = send_email_to_provider - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendInvoice", data, reply_to_message_id=reply_to_message_id, @@ -4418,7 +4419,7 @@ async def answer_shipping_query( # pylint: disable=invalid-name api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def answer_pre_checkout_query( # pylint: disable=invalid-name @@ -4478,7 +4479,7 @@ async def answer_pre_checkout_query( # pylint: disable=invalid-name api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def answer_web_app_query( @@ -4525,7 +4526,7 @@ async def answer_web_app_query( api_kwargs=api_kwargs, ) - return SentWebAppMessage.de_json(api_result, self) # type: ignore[return-value, arg-type] + return SentWebAppMessage.de_json(api_result, self) # type: ignore[return-value] @_log async def restrict_chat_member( @@ -4587,7 +4588,7 @@ async def restrict_chat_member( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def promote_chat_member( @@ -4708,7 +4709,7 @@ async def promote_chat_member( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_chat_permissions( @@ -4750,7 +4751,7 @@ async def set_chat_permissions( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_chat_administrator_custom_title( @@ -4797,7 +4798,7 @@ async def set_chat_administrator_custom_title( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def export_chat_invite_link( @@ -4844,7 +4845,7 @@ async def export_chat_invite_link( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def create_chat_invite_link( @@ -4922,7 +4923,7 @@ async def create_chat_invite_link( api_kwargs=api_kwargs, ) - return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] @_log async def edit_chat_invite_link( @@ -5011,7 +5012,7 @@ async def edit_chat_invite_link( api_kwargs=api_kwargs, ) - return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] @_log async def revoke_chat_invite_link( @@ -5061,7 +5062,7 @@ async def revoke_chat_invite_link( api_kwargs=api_kwargs, ) - return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] + return ChatInviteLink.de_json(result, self) # type: ignore[return-value] @_log async def approve_chat_join_request( @@ -5107,7 +5108,7 @@ async def approve_chat_join_request( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def decline_chat_join_request( @@ -5153,7 +5154,7 @@ async def decline_chat_join_request( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_chat_photo( @@ -5203,7 +5204,7 @@ async def set_chat_photo( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def delete_chat_photo( @@ -5243,7 +5244,7 @@ async def delete_chat_photo( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_chat_title( @@ -5287,7 +5288,7 @@ async def set_chat_title( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_chat_description( @@ -5334,7 +5335,7 @@ async def set_chat_description( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def pin_chat_message( @@ -5378,7 +5379,7 @@ async def pin_chat_message( "disable_notification": disable_notification, } - return await self._post( # type: ignore[return-value] + return await self._post( "pinChatMessage", data, read_timeout=read_timeout, @@ -5426,7 +5427,7 @@ async def unpin_chat_message( if message_id is not None: data["message_id"] = message_id - return await self._post( # type: ignore[return-value] + return await self._post( "unpinChatMessage", data, read_timeout=read_timeout, @@ -5468,7 +5469,7 @@ async def unpin_all_chat_messages( """ data: JSONDict = {"chat_id": chat_id} - return await self._post( # type: ignore[return-value] + return await self._post( "unpinAllChatMessages", data, read_timeout=read_timeout, @@ -5511,7 +5512,7 @@ async def get_sticker_set( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return StickerSet.de_json(result, self) # type: ignore[return-value, arg-type] + return StickerSet.de_json(result, self) # type: ignore[return-value] @_log async def get_custom_emoji_stickers( @@ -5553,7 +5554,7 @@ async def get_custom_emoji_stickers( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return Sticker.de_list(result, self) # type: ignore[return-value, arg-type] + return Sticker.de_list(result, self) @_log async def upload_sticker_file( @@ -5603,7 +5604,7 @@ async def upload_sticker_file( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return File.de_json(result, self) # type: ignore[return-value, arg-type] + return File.de_json(result, self) # type: ignore[return-value] @_log async def create_new_sticker_set( @@ -5724,7 +5725,7 @@ async def create_new_sticker_set( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def add_sticker_to_set( @@ -5823,7 +5824,7 @@ async def add_sticker_to_set( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_sticker_position_in_set( @@ -5860,7 +5861,7 @@ async def set_sticker_position_in_set( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def delete_sticker_from_set( @@ -5895,7 +5896,7 @@ async def delete_sticker_from_set( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_sticker_set_thumb( @@ -5953,7 +5954,7 @@ async def set_sticker_set_thumb( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def set_passport_data_errors( @@ -5998,7 +5999,7 @@ async def set_passport_data_errors( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def send_poll( @@ -6125,7 +6126,7 @@ async def send_poll( if close_date: data["close_date"] = close_date - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendPoll", data, reply_to_message_id=reply_to_message_id, @@ -6186,7 +6187,7 @@ async def stop_poll( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return Poll.de_json(result, self) # type: ignore[return-value, arg-type] + return Poll.de_json(result, self) # type: ignore[return-value] @_log async def send_dice( @@ -6254,7 +6255,7 @@ async def send_dice( if emoji: data["emoji"] = emoji - return await self._send_message( # type: ignore[return-value] + return await self._send_message( "sendDice", data, reply_to_message_id=reply_to_message_id, @@ -6313,7 +6314,7 @@ async def get_my_default_administrator_rights( api_kwargs=api_kwargs, ) - return ChatAdministratorRights.de_json(result, self) # type: ignore[return-value,arg-type] + return ChatAdministratorRights.de_json(result, self) # type: ignore[return-value] @_log async def set_my_default_administrator_rights( @@ -6367,7 +6368,7 @@ async def set_my_default_administrator_rights( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def get_my_commands( @@ -6425,7 +6426,7 @@ async def get_my_commands( api_kwargs=api_kwargs, ) - return BotCommand.de_list(result, self) # type: ignore[return-value,arg-type] + return BotCommand.de_list(result, self) @_log async def set_my_commands( @@ -6488,7 +6489,7 @@ async def set_my_commands( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def delete_my_commands( @@ -6542,7 +6543,7 @@ async def delete_my_commands( api_kwargs=api_kwargs, ) - return result # type: ignore[return-value] + return result @_log async def log_out( @@ -6568,7 +6569,7 @@ async def log_out( :class:`telegram.error.TelegramError` """ - return await self._post( # type: ignore[return-value] + return await self._post( "logOut", read_timeout=read_timeout, write_timeout=write_timeout, @@ -6600,7 +6601,7 @@ async def close( :class:`telegram.error.TelegramError` """ - return await self._post( # type: ignore[return-value] + return await self._post( "close", read_timeout=read_timeout, write_timeout=write_timeout, @@ -6702,7 +6703,7 @@ async def copy_message( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return MessageId.de_json(result, self) # type: ignore[return-value, arg-type] + return MessageId.de_json(result, self) # type: ignore[return-value] @_log async def set_chat_menu_button( @@ -6741,7 +6742,7 @@ async def set_chat_menu_button( if menu_button is not None: data["menu_button"] = menu_button - return await self._post( # type: ignore[return-value] + return await self._post( "setChatMenuButton", data, read_timeout=read_timeout, @@ -6792,7 +6793,7 @@ async def get_chat_menu_button( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return MenuButton.de_json(result, bot=self) # type: ignore[return-value, arg-type] + return MenuButton.de_json(result, bot=self) # type: ignore[return-value] @_log async def create_invoice_link( @@ -6921,7 +6922,7 @@ async def create_invoice_link( if send_email_to_provider is not None: data["send_email_to_provider"] = send_email_to_provider - return await self._post( # type: ignore[return-value] + return await self._post( "createInvoiceLink", data, read_timeout=read_timeout, @@ -6961,7 +6962,7 @@ async def get_forum_topic_icon_stickers( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return Sticker.de_list(result, self) # type: ignore[return-value, arg-type] + return Sticker.de_list(result, self) @_log async def create_forum_topic( @@ -7023,7 +7024,7 @@ async def create_forum_topic( pool_timeout=pool_timeout, api_kwargs=api_kwargs, ) - return ForumTopic.de_json(result, self) # type: ignore[return-value, arg-type] + return ForumTopic.de_json(result, self) # type: ignore[return-value] @_log async def edit_forum_topic( @@ -7073,7 +7074,7 @@ async def edit_forum_topic( "name": name, "icon_custom_emoji_id": icon_custom_emoji_id, } - return await self._post( # type: ignore[return-value] + return await self._post( "editForumTopic", data, read_timeout=read_timeout, @@ -7121,7 +7122,7 @@ async def close_forum_topic( "chat_id": chat_id, "message_thread_id": message_thread_id, } - return await self._post( # type: ignore[return-value] + return await self._post( "closeForumTopic", data, read_timeout=read_timeout, @@ -7169,7 +7170,7 @@ async def reopen_forum_topic( "chat_id": chat_id, "message_thread_id": message_thread_id, } - return await self._post( # type: ignore[return-value] + return await self._post( "reopenForumTopic", data, read_timeout=read_timeout, @@ -7216,7 +7217,7 @@ async def delete_forum_topic( "chat_id": chat_id, "message_thread_id": message_thread_id, } - return await self._post( # type: ignore[return-value] + return await self._post( "deleteForumTopic", data, read_timeout=read_timeout, @@ -7264,7 +7265,7 @@ async def unpin_all_forum_topic_messages( "chat_id": chat_id, "message_thread_id": message_thread_id, } - return await self._post( # type: ignore[return-value] + return await self._post( "unpinAllForumTopicMessages", data, read_timeout=read_timeout, diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 39deba8cb34..010210c852b 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -122,7 +122,9 @@ def de_list_decrypted( passport credentials. .. versionchanged:: 20.0 - Returns a tuple instead of a list. + + * Returns a tuple instead of a list. + * Filters out any :obj:`None` values Args: data (List[Dict[:obj:`str`, ...]]): The JSON data. @@ -137,8 +139,13 @@ def de_list_decrypted( return () return tuple( - cls.de_json_decrypted(passport_file, bot, credentials[i]) - for i, passport_file in enumerate(data) + filter( + None, + ( + cls.de_json_decrypted(passport_file, bot, credentials[i]) + for i, passport_file in enumerate(data) + ), + ) ) async def get_file( diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index bfe41673994..de212a14ba2 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -467,11 +467,13 @@ def _de_json( @classmethod def de_list( cls: Type[Tele_co], data: Optional[List[JSONDict]], bot: "Bot" - ) -> Tuple[Optional[Tele_co], ...]: + ) -> Tuple[Tele_co, ...]: """Converts a list of JSON objects to a tuple of Telegram objects. .. versionchanged:: 20.0 - Returns a tuple instead of a list. + + * Returns a tuple instead of a list. + * Filters out any :obj:`None` values. Args: data (List[Dict[:obj:`str`, ...]]): The JSON data. @@ -484,7 +486,7 @@ def de_list( if not data: return () - return tuple(cls.de_json(d, bot) for d in data) + return tuple(filter(None, (cls.de_json(d, bot) for d in data))) def to_json(self) -> str: """Gives a JSON representation of object. diff --git a/telegram/ext/_aioratelimiter.py b/telegram/ext/_aioratelimiter.py index d6b8a22938b..300e8fabdd7 100644 --- a/telegram/ext/_aioratelimiter.py +++ b/telegram/ext/_aioratelimiter.py @@ -23,7 +23,7 @@ import contextlib import logging import sys -from typing import Any, AsyncIterator, Callable, Coroutine, Dict, Optional, Union +from typing import Any, AsyncIterator, Callable, Coroutine, Dict, List, Optional, Union try: from aiolimiter import AsyncLimiter @@ -187,10 +187,10 @@ async def _run_request( self, chat: bool, group: Union[str, int, bool], - callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, None]]], + callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], args: Any, kwargs: Dict[str, Any], - ) -> Union[bool, JSONDict, None]: + ) -> Union[bool, JSONDict, List[JSONDict]]: base_context = self._base_limiter if (chat and self._base_limiter) else null_context() group_context = ( self._get_group_limiter(group) if group and self._group_max_rate else null_context() @@ -206,13 +206,13 @@ async def _run_request( # mypy doesn't understand that the last run of the for loop raises an exception async def process_request( # type: ignore[return] self, - callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, None]]], + callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], args: Any, kwargs: Dict[str, Any], endpoint: str, # skipcq: PYL-W0613 data: Dict[str, Any], rate_limit_args: Optional[int], - ) -> Union[bool, JSONDict, None]: + ) -> Union[bool, JSONDict, List[JSONDict]]: """ Processes a request by applying rate limiting. diff --git a/telegram/ext/_baseratelimiter.py b/telegram/ext/_baseratelimiter.py index b64b76da5f9..4726d8e336d 100644 --- a/telegram/ext/_baseratelimiter.py +++ b/telegram/ext/_baseratelimiter.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains a class that allows to rate limit requests to the Bot API.""" from abc import ABC, abstractmethod -from typing import Any, Callable, Coroutine, Dict, Generic, Optional, Union +from typing import Any, Callable, Coroutine, Dict, Generic, List, Optional, Union from telegram._utils.types import JSONDict from telegram.ext._utils.types import RLARGS @@ -58,13 +58,13 @@ async def shutdown(self) -> None: @abstractmethod async def process_request( self, - callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, None]]], + callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], args: Any, kwargs: Dict[str, Any], endpoint: str, data: Dict[str, Any], rate_limit_args: Optional[RLARGS], - ) -> Union[bool, JSONDict, None]: + ) -> Union[bool, JSONDict, List[JSONDict]]: """ Process a request. Must be implemented by a subclass. diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 3bc7f3c1824..594af23335b 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -292,7 +292,7 @@ async def _do_post( write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Union[bool, JSONDict, None]: + ) -> Union[bool, JSONDict, List[JSONDict]]: """Order of method calls is: Bot.some_method -> Bot._post -> Bot._do_post. So we can override Bot._do_post to add rate limiting. """ diff --git a/telegram/request/_baserequest.py b/telegram/request/_baserequest.py index b544e45e09f..d4df2cfb3a0 100644 --- a/telegram/request/_baserequest.py +++ b/telegram/request/_baserequest.py @@ -23,7 +23,7 @@ from contextlib import AbstractAsyncContextManager from http import HTTPStatus from types import TracebackType -from typing import ClassVar, Optional, Tuple, Type, TypeVar, Union +from typing import ClassVar, List, Optional, Tuple, Type, TypeVar, Union from telegram._utils.defaultvalue import DEFAULT_NONE as _DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput @@ -132,7 +132,7 @@ async def post( write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Union[JSONDict, bool]: + ) -> Union[JSONDict, List[JSONDict], bool]: """Makes a request to the Bot API handles the return code and parses the answer. Warning: @@ -161,7 +161,7 @@ async def post( :attr:`DEFAULT_NONE`. Returns: - Dict[:obj:`str`, ...]: The JSON response of the Bot API. + The JSON response of the Bot API. """ result = await self._request_wrapper( diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 67755f157fc..4e8ccfe7955 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -84,6 +84,19 @@ def test_de_json_api_kwargs(self, bot): assert to.api_kwargs == {"foo": "bar"} assert to.get_bot() is bot + def test_de_list(self, bot): + class SubClass(TelegramObject): + def __init__(self, arg: int, **kwargs): + super().__init__(**kwargs) + self.arg = arg + + self._id_attrs = (self.arg,) + + assert SubClass.de_list([{"arg": 1}, None, {"arg": 2}, None], bot) == ( + SubClass(1), + SubClass(2), + ) + def test_api_kwargs_read_only(self): tg_object = TelegramObject(api_kwargs={"foo": "bar"}) tg_object._freeze() From 0ddd3944b55114fc6bd3fe883e61cb5a5c392826 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:23:39 +0100 Subject: [PATCH 81/90] spelling bee --- docs/substitutions/global.rst | 2 +- immutabilize.py | 2 +- telegram/_files/inputmedia.py | 12 ++++++------ telegram/_files/sticker.py | 2 +- telegram/_games/game.py | 4 ++-- telegram/_inline/inlinekeyboardmarkup.py | 2 +- telegram/_inline/inlinequeryresultaudio.py | 2 +- telegram/_inline/inlinequeryresultcachedaudio.py | 2 +- telegram/_inline/inlinequeryresultcacheddocument.py | 2 +- telegram/_inline/inlinequeryresultcachedgif.py | 2 +- telegram/_inline/inlinequeryresultcachedmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultcachedphoto.py | 2 +- telegram/_inline/inlinequeryresultcachedvoice.py | 2 +- telegram/_inline/inlinequeryresultdocument.py | 2 +- telegram/_inline/inlinequeryresultgif.py | 2 +- telegram/_inline/inlinequeryresultmpeg4gif.py | 2 +- telegram/_inline/inlinequeryresultphoto.py | 2 +- telegram/_inline/inlinequeryresultvideo.py | 4 ++-- telegram/_inline/inlinequeryresultvoice.py | 2 +- telegram/_inline/inputinvoicemessagecontent.py | 4 ++-- telegram/_inline/inputtextmessagecontent.py | 2 +- telegram/_message.py | 10 +++++----- telegram/_passport/encryptedpassportelement.py | 4 ++-- telegram/_passport/passportdata.py | 2 +- telegram/_payment/shippingoption.py | 2 +- telegram/_poll.py | 6 +++--- telegram/_userprofilephotos.py | 2 +- telegram/_videochat.py | 2 +- telegram/_webhookinfo.py | 2 +- 29 files changed, 44 insertions(+), 44 deletions(-) diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 68c5bafb02f..c5ffc8d0c05 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -42,6 +42,6 @@ .. |reply_to_msg_id| replace:: If the message is a reply, ID of the original message. -.. |sequenceeclassargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list. The input is converted to a tuple. +.. |sequenceclassargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list. The input is converted to a tuple. .. |tupleclassattrs| replace:: This attribute is now an immutable tuple. diff --git a/immutabilize.py b/immutabilize.py index 25e9c6d78b4..5cb56a5d98e 100644 --- a/immutabilize.py +++ b/immutabilize.py @@ -85,7 +85,7 @@ class_source_lines[j - 1] += ( f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" - " |sequenceeclassargs|" + " |sequenceclassargs|" ) if class_source_lines[j]: class_source_lines[j - 1] += "\n" diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 642a96ee86a..822d727c34c 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -58,7 +58,7 @@ class InputMedia(TelegramObject): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| parse_mode (:obj:`str`, optional): |parse_mode| @@ -136,7 +136,7 @@ class InputMediaAnimation(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| width (:obj:`int`, optional): Animation width. height (:obj:`int`, optional): Animation height. @@ -222,7 +222,7 @@ class InputMediaPhoto(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: type (:obj:`str`): :tg-const:`telegram.constants.InputMediaType.PHOTO`. @@ -293,7 +293,7 @@ class InputMediaVideo(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| width (:obj:`int`, optional): Video width. height (:obj:`int`, optional): Video height. @@ -395,7 +395,7 @@ class InputMediaAudio(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| duration (:obj:`int`): Duration of the audio in seconds as defined by sender. performer (:obj:`str`, optional): Performer of the audio as defined by sender or by audio @@ -487,7 +487,7 @@ class InputMediaDocument(InputMedia): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| thumb (:term:`file object` | :obj:`bytes` | :class:`pathlib.Path` | :obj:`str`, \ optional): |thumbdocstringnopath| diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 75852c431e0..9e1704974c3 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -209,7 +209,7 @@ class StickerSet(TelegramObject): stickers (Sequence[:class:`telegram.Sticker`]): List of all set stickers. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| sticker_type (:obj:`str`): Type of stickers in the set, currently one of :attr:`telegram.Sticker.REGULAR`, :attr:`telegram.Sticker.MASK`, diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 9dbc2b976b2..e1064f0a40b 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -45,7 +45,7 @@ class Game(TelegramObject): message in chats. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| text (:obj:`str`, optional): Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game @@ -56,7 +56,7 @@ class Game(TelegramObject): appear in text, such as usernames, URLs, bot commands, etc. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| animation (:class:`telegram.Animation`, optional): Animation that will be displayed in the game message in chats. Upload via `BotFather `_. diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 133c734dc6a..f4906097bf4 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -45,7 +45,7 @@ class InlineKeyboardMarkup(TelegramObject): objects. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index fe12d5d0513..5e8865a68c5 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -51,7 +51,7 @@ class InlineQueryResultAudio(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index e02b0567fc7..a62b3ab4a1e 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -48,7 +48,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index c984f225d99..46b3675da3e 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -50,7 +50,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index e7a78af35a9..adc1494a415 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -50,7 +50,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index b5b048e4317..bba2b0935e2 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -50,7 +50,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index abf92e2e4cf..c49236bba21 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -51,7 +51,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index 20c5799c71f..f3e6a97a6e3 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -49,7 +49,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`, optional): Content of the diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 6f6ec06415e..9c63e74ae77 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -49,7 +49,7 @@ class InlineQueryResultDocument(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| document_url (:obj:`str`): A valid URL for the file. mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 88243a963bd..618b214be90 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -56,7 +56,7 @@ class InlineQueryResultGif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index c489a821fb7..66981d147d4 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -56,7 +56,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index 01eaa3455e3..b4503bfd0bb 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -54,7 +54,7 @@ class InlineQueryResultPhoto(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached to the message. diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 0f4920a5a1c..464bf7a76cb 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -56,7 +56,7 @@ class InlineQueryResultVideo(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| video_width (:obj:`int`, optional): Video width. video_height (:obj:`int`, optional): Video height. @@ -85,7 +85,7 @@ class InlineQueryResultVideo(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| video_width (:obj:`int`): Optional. Video width. video_height (:obj:`int`): Optional. Video height. diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index 2977a44aa13..618391f4838 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -50,7 +50,7 @@ class InlineQueryResultVoice(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| voice_duration (:obj:`int`, optional): Recording duration in seconds. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): Inline keyboard attached diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 776d2b039ce..f214795e71b 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -56,7 +56,7 @@ class InputInvoiceMessageContent(InputMessageContent): etc.) .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| max_tip_amount (:obj:`int`, optional): The maximum accepted amount for tips in the *smallest* units of the currency (integer, **not** float/double). For example, for a @@ -71,7 +71,7 @@ class InputInvoiceMessageContent(InputMessageContent): :attr:`max_tip_amount`. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| provider_data (:obj:`str`, optional): An object for data about the invoice, which will be shared with the payment provider. A detailed description of the required diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index eba6fc8476c..34290d30a18 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -44,7 +44,7 @@ class InputTextMessageContent(InputMessageContent): entities (Sequence[:class:`telegram.MessageEntity`], optional): |caption_entities| .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| disable_web_page_preview (:obj:`bool`, optional): Disables link previews for links in the sent message. diff --git a/telegram/_message.py b/telegram/_message.py index 194ee8a671a..af890d85134 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -146,7 +146,7 @@ class Message(TelegramObject): This list is empty if the message does not contain entities. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| caption_entities (Sequence[:class:`telegram.MessageEntity`], optional): For messages with a Caption. Special entities like usernames, URLs, bot commands, etc. that appear in the @@ -155,7 +155,7 @@ class Message(TelegramObject): caption entities. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| audio (:class:`telegram.Audio`, optional): Message is an audio file, information about the file. @@ -169,7 +169,7 @@ class Message(TelegramObject): sizes of the photo. This list is empty if the message does not contain a photo. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| sticker (:class:`telegram.Sticker`, optional): Message is a sticker, information about the sticker. @@ -184,7 +184,7 @@ class Message(TelegramObject): these members). This list is empty if the message does not contain new chat members. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| caption (:obj:`str`, optional): Caption for the animation, audio, document, photo, video or voice, 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters. @@ -202,7 +202,7 @@ class Message(TelegramObject): to this value. This list is empty if the message does not contain a new chat photo. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| delete_chat_photo (:obj:`bool`, optional): Service message: The chat photo was deleted. group_chat_created (:obj:`bool`, optional): Service message: The group has been created. diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 2fb3a20c2ee..e99b9987bae 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -64,7 +64,7 @@ class EncryptedPassportElement(TelegramObject): "rental_agreement", "passport_registration" and "temporary_registration" types. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| front_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the front side of the document, provided by the user. Available for "passport", @@ -83,7 +83,7 @@ class EncryptedPassportElement(TelegramObject): "temporary_registration" types. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license", diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 676ae57048e..80e8e78a00d 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -43,7 +43,7 @@ class PassportData(TelegramObject): the bot. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index cae746a7f56..975de7f50ea 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -41,7 +41,7 @@ class ShippingOption(TelegramObject): prices (Sequence[:class:`telegram.LabeledPrice`]): List of price portions. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: id (:obj:`str`): Shipping option identifier. diff --git a/telegram/_poll.py b/telegram/_poll.py index ccbc5266f58..8bc5833675e 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -91,7 +91,7 @@ class PollAnswer(TelegramObject): user. May be empty if the user retracted their vote. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: poll_id (:obj:`str`): Unique poll identifier. @@ -148,7 +148,7 @@ class Poll(TelegramObject): options (Sequence[:class:`PollOption`]): List of poll options. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| is_closed (:obj:`bool`): :obj:`True`, if the poll is closed. is_anonymous (:obj:`bool`): :obj:`True`, if the poll is anonymous. type (:obj:`str`): Poll type, currently can be :attr:`REGULAR` or :attr:`QUIZ`. @@ -167,7 +167,7 @@ class Poll(TelegramObject): .. versionchanged:: 20.0 * This attribute is now always a (possibly empty) list and never :obj:`None`. - * |sequenceeclassargs| + * |sequenceclassargs| open_period (:obj:`int`, optional): Amount of time in seconds the poll will be active after creation. close_date (:obj:`datetime.datetime`, optional): Point in time (Unix timestamp) when the diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index fad8edc2a31..f6cf3dd5a06 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -39,7 +39,7 @@ class UserProfilePhotos(TelegramObject): to 4 sizes each). .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: total_count (:obj:`int`): Total number of profile pictures. diff --git a/telegram/_videochat.py b/telegram/_videochat.py index 23c57abaab3..d4412a65f3e 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -93,7 +93,7 @@ class VideoChatParticipantsInvited(TelegramObject): users (Sequence[:class:`telegram.User`]): New members that were invited to the video chat. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| Attributes: users (Tuple[:class:`telegram.User`]): New members that were invited to the video chat. diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 6e3f53979d3..8ae9e95a3f0 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -59,7 +59,7 @@ class WebhookInfo(TelegramObject): :attr:`telegram.Update.chat_member`. .. versionchanged:: 20.0 - |sequenceeclassargs| + |sequenceclassargs| last_synchronization_error_date (:obj:`int`, optional): Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters. From 78d5bfa7109cd01b64ef53033cbee45ef4f11197 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:32:57 +0100 Subject: [PATCH 82/90] misc review --- telegram/_chat.py | 2 +- telegram/_telegramobject.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/telegram/_chat.py b/telegram/_chat.py index 013e6de1477..b25f2c07dda 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -1304,7 +1304,7 @@ async def send_media_group( For the documentation of the arguments, please see :meth:`telegram.Bot.send_media_group`. Returns: - Tuple[:class:`telegram.Message`:] On success, a tuple of :class:`~telegram.Message` + Tuple[:class:`telegram.Message`]: On success, a tuple of :class:`~telegram.Message` instances that were sent is returned. """ diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index de212a14ba2..2eb8804c6d6 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -143,7 +143,7 @@ def __setattr__(self, key: str, value: object) -> None: :exc:`AttributeError` """ # protected attributes can always be set for convenient internal use - if (key == "_frozen") or (not getattr(self, "_frozen", True)) or key.startswith("_"): + if key[0] == "_" or not getattr(self, "_frozen", True): super().__setattr__(key, value) return @@ -158,7 +158,7 @@ def __delattr__(self, key: str) -> None: :exc:`AttributeError` """ # protected attributes can always be set for convenient internal use - if (key == "_frozen") or (not getattr(self, "_frozen", True)) or key.startswith("_"): + if key[0] == "_" or not getattr(self, "_frozen", True): super().__delattr__(key) return From c09311d25822ecfcd91723551e66b6738e6109b5 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 16:22:42 +0100 Subject: [PATCH 83/90] much tuple --- docs/substitutions/global.rst | 2 + telegram/_bot.py | 5 +-- telegram/_chat.py | 3 +- telegram/_files/inputmedia.py | 27 +++++++++---- telegram/_files/sticker.py | 3 +- telegram/_games/game.py | 5 ++- telegram/_inline/inlinequeryresultaudio.py | 7 +++- .../_inline/inlinequeryresultcachedaudio.py | 7 +++- .../inlinequeryresultcacheddocument.py | 7 +++- .../_inline/inlinequeryresultcachedgif.py | 7 +++- .../inlinequeryresultcachedmpeg4gif.py | 7 +++- .../_inline/inlinequeryresultcachedphoto.py | 7 +++- .../_inline/inlinequeryresultcachedvideo.py | 7 +++- .../_inline/inlinequeryresultcachedvoice.py | 7 +++- telegram/_inline/inlinequeryresultdocument.py | 7 +++- telegram/_inline/inlinequeryresultgif.py | 7 +++- telegram/_inline/inlinequeryresultmpeg4gif.py | 7 +++- telegram/_inline/inlinequeryresultphoto.py | 7 +++- telegram/_inline/inlinequeryresultvideo.py | 7 +++- telegram/_inline/inlinequeryresultvoice.py | 7 +++- .../_inline/inputinvoicemessagecontent.py | 11 ++--- telegram/_inline/inputtextmessagecontent.py | 7 +++- telegram/_message.py | 11 ++--- telegram/_passport/credentials.py | 9 +++-- .../_passport/encryptedpassportelement.py | 13 ++++-- telegram/_passport/passportdata.py | 3 +- telegram/_payment/shippingoption.py | 3 +- telegram/_poll.py | 7 ++-- telegram/_utils/argumentparsing.py | 40 +++++++++++++++++++ telegram/_videochat.py | 3 +- telegram/_webhookinfo.py | 9 +++-- tests/test_encryptedpassportelement.py | 5 +++ tests/test_inlinequeryresultaudio.py | 4 ++ tests/test_inlinequeryresultcachedaudio.py | 4 ++ tests/test_inlinequeryresultcacheddocument.py | 4 ++ tests/test_inlinequeryresultcachedgif.py | 4 ++ tests/test_inlinequeryresultcachedmpeg4gif.py | 4 ++ tests/test_inlinequeryresultcachedphoto.py | 4 ++ tests/test_inlinequeryresultcachedvideo.py | 5 +++ tests/test_inlinequeryresultcachedvoice.py | 4 ++ tests/test_inlinequeryresultdocument.py | 4 ++ tests/test_inlinequeryresultgif.py | 4 ++ tests/test_inlinequeryresultmpeg4gif.py | 4 ++ tests/test_inlinequeryresultphoto.py | 4 ++ tests/test_inlinequeryresultvideo.py | 6 +++ tests/test_inlinequeryresultvoice.py | 9 +++++ tests/test_inputinvoicemessagecontent.py | 38 ++++++++++++++++++ tests/test_inputmedia.py | 20 ++++++++++ tests/test_inputtextmessagecontent.py | 4 ++ tests/test_webhookinfo.py | 6 +++ 50 files changed, 326 insertions(+), 70 deletions(-) create mode 100644 telegram/_utils/argumentparsing.py diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index c5ffc8d0c05..233c2ca27a0 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -45,3 +45,5 @@ .. |sequenceclassargs| replace:: Accepts any :class:`collections.abc.Sequence` as input instead of just a list. The input is converted to a tuple. .. |tupleclassattrs| replace:: This attribute is now an immutable tuple. + +.. |alwaystuple| replace:: This attribute is now always a tuple, that may be empty. diff --git a/telegram/_bot.py b/telegram/_bot.py index 4cfe836ac1b..3d3dc9a4f2f 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -88,6 +88,7 @@ from telegram._update import Update from telegram._user import User from telegram._userprofilephotos import UserProfilePhotos +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue from telegram._utils.files import is_local_file, parse_file_input from telegram._utils.types import DVInput, FileInput, JSONDict, ODVInput, ReplyMarkup @@ -1918,9 +1919,7 @@ async def send_media_group( item_to_get_caption.caption = caption if parse_mode is not DEFAULT_NONE: item_to_get_caption.parse_mode = parse_mode - item_to_get_caption.caption_entities = ( - tuple(caption_entities) if caption_entities else None - ) + item_to_get_caption.caption_entities = parse_sequence_arg(caption_entities) # copy the list (just the references) to avoid mutating the original list media = media[:] diff --git a/telegram/_chat.py b/telegram/_chat.py index b25f2c07dda..77fa665921e 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -30,6 +30,7 @@ from telegram._menubutton import MenuButton from telegram._telegramobject import TelegramObject from telegram._utils import enum +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import DVInput, FileInput, JSONDict, ODVInput, ReplyMarkup from telegram.helpers import escape_markdown @@ -354,7 +355,7 @@ def __init__( self.join_by_request = join_by_request self.has_restricted_voice_and_video_messages = has_restricted_voice_and_video_messages self.is_forum = is_forum - self.active_usernames = tuple(active_usernames) if active_usernames else () + self.active_usernames = parse_sequence_arg(active_usernames) self.emoji_status_custom_emoji_id = emoji_status_custom_emoji_id self._id_attrs = (self.id,) diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index 822d727c34c..bf40b1ea21c 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -27,6 +27,7 @@ from telegram._files.video import Video from telegram._messageentity import MessageEntity from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.files import parse_file_input from telegram._utils.types import FileInput, JSONDict, ODVInput @@ -70,7 +71,9 @@ class InputMedia(TelegramObject): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| """ @@ -90,7 +93,7 @@ def __init__( self.type = media_type self.media = media self.caption = caption - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.parse_mode = parse_mode self._freeze() @@ -150,7 +153,9 @@ class InputMediaAnimation(InputMedia): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. width (:obj:`int`): Optional. Animation width. @@ -232,7 +237,9 @@ class InputMediaPhoto(InputMedia): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| """ @@ -314,7 +321,9 @@ class InputMediaVideo(InputMedia): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| width (:obj:`int`): Optional. Video width. height (:obj:`int`): Optional. Video height. duration (:obj:`int`): Optional. Video duration in seconds. @@ -415,7 +424,9 @@ class InputMediaAudio(InputMedia): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| duration (:obj:`int`): Duration of the audio in seconds. performer (:obj:`str`): Optional. Performer of the audio as defined by sender or by audio tags. @@ -506,7 +517,9 @@ class InputMediaDocument(InputMedia): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| thumb (:class:`telegram.InputFile`): Optional. Thumbnail of the file to send. disable_content_type_detection (:obj:`bool`): Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always true, if diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 9e1704974c3..f024652944b 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -24,6 +24,7 @@ from telegram._files.file import File from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: @@ -266,7 +267,7 @@ def __init__( self.title = title self.is_animated = is_animated self.is_video = is_video - self.stickers = tuple(stickers) + self.stickers = parse_sequence_arg(stickers) self.sticker_type = sticker_type # Optional self.thumb = thumb diff --git a/telegram/_games/game.py b/telegram/_games/game.py index e1064f0a40b..3691d3b1208 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -24,6 +24,7 @@ from telegram._files.photosize import PhotoSize from telegram._messageentity import MessageEntity from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: @@ -110,10 +111,10 @@ def __init__( # Required self.title = title self.description = description - self.photo = tuple(photo) + self.photo = parse_sequence_arg(photo) # Optionals self.text = text - self.text_entities = tuple(text_entities) if text_entities else () + self.text_entities = parse_sequence_arg(text_entities) self.animation = animation self._id_attrs = (self.title, self.description, self.photo) diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 5e8865a68c5..1c1bbe6d24c 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -73,7 +74,9 @@ class InlineQueryResultAudio(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -120,6 +123,6 @@ def __init__( self.audio_duration = audio_duration self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index a62b3ab4a1e..370a9883313 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -68,7 +69,9 @@ class InlineQueryResultCachedAudio(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -105,6 +108,6 @@ def __init__( # Optionals self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index 46b3675da3e..74cda79aca8 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -72,7 +73,9 @@ class InlineQueryResultCachedDocument(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -115,6 +118,6 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index adc1494a415..2f5038d3a3e 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -71,7 +72,9 @@ class InlineQueryResultCachedGif(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -111,6 +114,6 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index bba2b0935e2..1857113832f 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -71,7 +72,9 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -111,6 +114,6 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index c49236bba21..30855e7129c 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -73,7 +74,9 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -116,6 +119,6 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 9c23cf6b4a9..d82ca22a896 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -69,7 +70,9 @@ class InlineQueryResultCachedVideo(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -112,6 +115,6 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index f3e6a97a6e3..19c5f4051bf 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -69,7 +70,9 @@ class InlineQueryResultCachedVoice(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -109,6 +112,6 @@ def __init__( # Optionals self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index 9c63e74ae77..e262b38c4dd 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -76,7 +77,9 @@ class InlineQueryResultDocument(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| document_url (:obj:`str`): A valid URL for the file. mime_type (:obj:`str`): Mime type of the content of the file, either "application/pdf" or "application/zip". @@ -134,7 +137,7 @@ def __init__( # Optionals self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.description = description self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index 618b214be90..9bbc236478c 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -83,7 +84,9 @@ class InlineQueryResultGif(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -138,7 +141,7 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_mime_type = thumb_mime_type diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 66981d147d4..5656da94a24 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -83,7 +84,9 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. @@ -139,7 +142,7 @@ def __init__( self.title = title self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content self.thumb_mime_type = thumb_mime_type diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index b4503bfd0bb..4517e7acfe1 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -80,7 +81,9 @@ class InlineQueryResultPhoto(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. input_message_content (:class:`telegram.InputMessageContent`): Optional. Content of the @@ -132,6 +135,6 @@ def __init__( self.description = description self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index 464bf7a76cb..5fd617f7550 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -85,7 +86,9 @@ class InlineQueryResultVideo(InlineQueryResult): caption_entities (Sequence[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |sequenceclassargs| + + * |tupleclassattrs| + * |alwaystuple| video_width (:obj:`int`): Optional. Video width. video_height (:obj:`int`): Optional. Video height. @@ -147,7 +150,7 @@ def __init__( # Optional self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.video_width = video_width self.video_height = video_height self.video_duration = video_duration diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index 618391f4838..6a4d52109cc 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -22,6 +22,7 @@ from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput from telegram.constants import InlineQueryResultType @@ -72,7 +73,9 @@ class InlineQueryResultVoice(InlineQueryResult): caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| voice_duration (:obj:`int`): Optional. Recording duration in seconds. reply_markup (:class:`telegram.InlineKeyboardMarkup`): Optional. Inline keyboard attached to the message. @@ -117,6 +120,6 @@ def __init__( self.voice_duration = voice_duration self.caption = caption self.parse_mode = parse_mode - self.caption_entities = tuple(caption_entities) if caption_entities else None + self.caption_entities = parse_sequence_arg(caption_entities) self.reply_markup = reply_markup self.input_message_content = input_message_content diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index f214795e71b..cfdf0db980a 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -21,6 +21,7 @@ from telegram._inline.inputmessagecontent import InputMessageContent from telegram._payment.labeledprice import LabeledPrice +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: @@ -71,7 +72,9 @@ class InputInvoiceMessageContent(InputMessageContent): :attr:`max_tip_amount`. .. versionchanged:: 20.0 - |sequenceclassargs| + + * |tupleclassattrs| + * |alwaystuple| provider_data (:obj:`str`, optional): An object for data about the invoice, which will be shared with the payment provider. A detailed description of the required @@ -203,12 +206,10 @@ def __init__( self.payload = payload self.provider_token = provider_token self.currency = currency - self.prices = tuple(prices) + self.prices = parse_sequence_arg(prices) # Optionals self.max_tip_amount = max_tip_amount - self.suggested_tip_amounts = ( - tuple(suggested_tip_amounts) if suggested_tip_amounts else None - ) + self.suggested_tip_amounts = parse_sequence_arg(suggested_tip_amounts) self.provider_data = provider_data self.photo_url = photo_url self.photo_size = photo_size diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index 34290d30a18..e2a7825595a 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -21,6 +21,7 @@ from telegram._inline.inputmessagecontent import InputMessageContent from telegram._messageentity import MessageEntity +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.defaultvalue import DEFAULT_NONE from telegram._utils.types import JSONDict, ODVInput @@ -57,7 +58,9 @@ class InputTextMessageContent(InputMessageContent): entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in the sent message. @@ -79,7 +82,7 @@ def __init__( self.message_text = message_text # Optionals self.parse_mode = parse_mode - self.entities = tuple(entities) if entities else None + self.entities = parse_sequence_arg(entities) self.disable_web_page_preview = disable_web_page_preview self._id_attrs = (self.message_text,) diff --git a/telegram/_message.py b/telegram/_message.py index af890d85134..3f84539f4bf 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -48,6 +48,7 @@ from telegram._proximityalerttriggered import ProximityAlertTriggered from telegram._telegramobject import TelegramObject from telegram._user import User +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.datetime import from_timestamp from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue from telegram._utils.types import DVInput, FileInput, JSONDict, ODVInput, ReplyMarkup @@ -641,12 +642,12 @@ def __init__( self.edit_date = edit_date self.has_protected_content = has_protected_content self.text = text - self.entities = tuple(entities) if entities else () - self.caption_entities = tuple(caption_entities) if caption_entities else () + self.entities = parse_sequence_arg(entities) + self.caption_entities = parse_sequence_arg(caption_entities) self.audio = audio self.game = game self.document = document - self.photo = tuple(photo) if photo else () + self.photo = parse_sequence_arg(photo) self.sticker = sticker self.video = video self.voice = voice @@ -655,10 +656,10 @@ def __init__( self.contact = contact self.location = location self.venue = venue - self.new_chat_members = tuple(new_chat_members) if new_chat_members else () + self.new_chat_members = parse_sequence_arg(new_chat_members) self.left_chat_member = left_chat_member self.new_chat_title = new_chat_title - self.new_chat_photo = tuple(new_chat_photo) if new_chat_photo else () + self.new_chat_photo = parse_sequence_arg(new_chat_photo) self.delete_chat_photo = bool(delete_chat_photo) self.group_chat_created = bool(group_chat_created) self.supergroup_chat_created = bool(supergroup_chat_created) diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 5d2df4277d0..d2d86c24604 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -38,6 +38,7 @@ CRYPTO_INSTALLED = False from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict from telegram.error import PassportDecryptionError @@ -375,7 +376,9 @@ class SecureValue(TelegramObject): "passport_registration" and "temporary_registration" types. .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| """ @@ -397,8 +400,8 @@ def __init__( self.front_side = front_side self.reverse_side = reverse_side self.selfie = selfie - self.files = tuple(files) if files else None - self.translation = tuple(translation) if translation else None + self.files = parse_sequence_arg(files) + self.translation = parse_sequence_arg(translation) self._freeze() diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index e99b9987bae..6e03c7cc760 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -24,6 +24,7 @@ from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress from telegram._passport.passportfile import PassportFile from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: @@ -106,7 +107,9 @@ class EncryptedPassportElement(TelegramObject): "rental_agreement", "passport_registration" and "temporary_registration" types. .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| front_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the front side of the document, provided by the user. Available for "passport", @@ -125,7 +128,9 @@ class EncryptedPassportElement(TelegramObject): "temporary_registration" types. .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| """ @@ -166,11 +171,11 @@ def __init__( self.data = data self.phone_number = phone_number self.email = email - self.files = tuple(files) if files else None + self.files = parse_sequence_arg(files) self.front_side = front_side self.reverse_side = reverse_side self.selfie = selfie - self.translation = tuple(translation) if translation else None + self.translation = parse_sequence_arg(translation) self.hash = hash self._id_attrs = ( diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 80e8e78a00d..35fcf8e8aa4 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -22,6 +22,7 @@ from telegram._passport.credentials import EncryptedCredentials from telegram._passport.encryptedpassportelement import EncryptedPassportElement from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: @@ -71,7 +72,7 @@ def __init__( ): super().__init__(api_kwargs=api_kwargs) - self.data = tuple(data) + self.data = parse_sequence_arg(data) self.credentials = credentials self._decrypted_data: Optional[Tuple[EncryptedPassportElement]] = None diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index 975de7f50ea..3f53bdcbf06 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -20,6 +20,7 @@ from typing import TYPE_CHECKING, Sequence from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.types import JSONDict if TYPE_CHECKING: @@ -67,7 +68,7 @@ def __init__( self.id = id # pylint: disable=invalid-name self.title = title - self.prices = tuple(prices) + self.prices = parse_sequence_arg(prices) self._id_attrs = (self.id,) diff --git a/telegram/_poll.py b/telegram/_poll.py index 8bc5833675e..b9d8eb73a42 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -26,6 +26,7 @@ from telegram._telegramobject import TelegramObject from telegram._user import User from telegram._utils import enum +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.datetime import from_timestamp from telegram._utils.types import JSONDict @@ -112,7 +113,7 @@ def __init__( super().__init__(api_kwargs=api_kwargs) self.poll_id = poll_id self.user = user - self.option_ids = tuple(option_ids) + self.option_ids = parse_sequence_arg(option_ids) self._id_attrs = (self.poll_id, self.user, tuple(self.option_ids)) @@ -245,7 +246,7 @@ def __init__( super().__init__(api_kwargs=api_kwargs) self.id = id # pylint: disable=invalid-name self.question = question - self.options = tuple(options) + self.options = parse_sequence_arg(options) self.total_voter_count = total_voter_count self.is_closed = is_closed self.is_anonymous = is_anonymous @@ -253,7 +254,7 @@ def __init__( self.allows_multiple_answers = allows_multiple_answers self.correct_option_id = correct_option_id self.explanation = explanation - self.explanation_entities = tuple(explanation_entities) if explanation_entities else () + self.explanation_entities = parse_sequence_arg(explanation_entities) self.open_period = open_period self.close_date = close_date diff --git a/telegram/_utils/argumentparsing.py b/telegram/_utils/argumentparsing.py new file mode 100644 index 00000000000..8052574f644 --- /dev/null +++ b/telegram/_utils/argumentparsing.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2022 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains helper functions related to parsing arguments for classes and methods. + +Warning: + Contents of this module are intended to be used internally by the library and *not* by the + user. Changes to this module are not considered breaking changes and may not be documented in + the changelog. +""" +from typing import Optional, Sequence, Tuple, TypeVar + +T = TypeVar("T") + + +def parse_sequence_arg(arg: Optional[Sequence[T]]) -> Tuple[T, ...]: + """Parses an optional sequence into a tuple + + Args: + arg (:obj:`Sequence`): The sequence to parse. + + Returns: + :obj:`Tuple`: The sequence converted to a tuple or an empty tuple. + """ + return tuple(arg) if arg else () diff --git a/telegram/_videochat.py b/telegram/_videochat.py index d4412a65f3e..a32a74785c3 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -22,6 +22,7 @@ from telegram._telegramobject import TelegramObject from telegram._user import User +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.datetime import from_timestamp from telegram._utils.types import JSONDict @@ -112,7 +113,7 @@ def __init__( api_kwargs: JSONDict = None, ) -> None: super().__init__(api_kwargs=api_kwargs) - self.users = tuple(users) + self.users = parse_sequence_arg(users) self._id_attrs = (self.users,) self._freeze() diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 8ae9e95a3f0..78694c48ee4 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -20,6 +20,7 @@ from typing import TYPE_CHECKING, Optional, Sequence from telegram._telegramobject import TelegramObject +from telegram._utils.argumentparsing import parse_sequence_arg from telegram._utils.datetime import from_timestamp from telegram._utils.types import JSONDict @@ -77,12 +78,14 @@ class WebhookInfo(TelegramObject): most recent error that happened when trying to deliver an update via webhook. max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery. - allowed_updates (Tuple[:obj:`str`]): Optional. A list of update types the bot is + allowed_updates (Tuple[:obj:`str`]): A list of update types the bot is subscribed to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. .. versionchanged:: 20.0 - |tupleclassattrs| + + * |tupleclassattrs| + * |alwaystuple| last_synchronization_error_date (:obj:`int`): Optional. Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters. @@ -126,7 +129,7 @@ def __init__( self.last_error_date = last_error_date self.last_error_message = last_error_message self.max_connections = max_connections - self.allowed_updates = tuple(allowed_updates) if allowed_updates else None + self.allowed_updates = parse_sequence_arg(allowed_updates) self.last_synchronization_error_date = last_synchronization_error_date self._id_attrs = ( diff --git a/tests/test_encryptedpassportelement.py b/tests/test_encryptedpassportelement.py index a7077aca7b1..f57193d1a7a 100644 --- a/tests/test_encryptedpassportelement.py +++ b/tests/test_encryptedpassportelement.py @@ -90,6 +90,11 @@ def test_to_dict(self, encrypted_passport_element): == encrypted_passport_element.selfie.to_dict() ) + def test_attributes_always_tuple(self): + element = EncryptedPassportElement(self.type_, self.hash) + assert element.files == () + assert element.translation == () + def test_equality(self): a = EncryptedPassportElement(self.type_, self.hash, data=self.data) b = EncryptedPassportElement(self.type_, self.hash, data=self.data) diff --git a/tests/test_inlinequeryresultaudio.py b/tests/test_inlinequeryresultaudio.py index 04737c626a8..e7056f62465 100644 --- a/tests/test_inlinequeryresultaudio.py +++ b/tests/test_inlinequeryresultaudio.py @@ -107,6 +107,10 @@ def test_to_dict(self, inline_query_result_audio): == inline_query_result_audio.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + inline_query_result_audio = InlineQueryResultAudio(self.id_, self.audio_url, self.title) + assert inline_query_result_audio.caption_entities == () + def test_equality(self): a = InlineQueryResultAudio(self.id_, self.audio_url, self.title) b = InlineQueryResultAudio(self.id_, self.title, self.title) diff --git a/tests/test_inlinequeryresultcachedaudio.py b/tests/test_inlinequeryresultcachedaudio.py index 1c07f259b3b..ac654e915a5 100644 --- a/tests/test_inlinequeryresultcachedaudio.py +++ b/tests/test_inlinequeryresultcachedaudio.py @@ -73,6 +73,10 @@ def test_expected_values(self, inline_query_result_cached_audio): inline_query_result_cached_audio.reply_markup.to_dict() == self.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + audio = InlineQueryResultCachedAudio(self.id_, self.audio_file_id) + assert audio.caption_entities == () + def test_to_dict(self, inline_query_result_cached_audio): inline_query_result_cached_audio_dict = inline_query_result_cached_audio.to_dict() diff --git a/tests/test_inlinequeryresultcacheddocument.py b/tests/test_inlinequeryresultcacheddocument.py index e364f995782..e728d333d17 100644 --- a/tests/test_inlinequeryresultcacheddocument.py +++ b/tests/test_inlinequeryresultcacheddocument.py @@ -80,6 +80,10 @@ def test_expected_values(self, inline_query_result_cached_document): == self.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + test = InlineQueryResultCachedDocument(self.id_, self.title, self.document_file_id) + assert test.caption_entities == () + def test_to_dict(self, inline_query_result_cached_document): inline_query_result_cached_document_dict = inline_query_result_cached_document.to_dict() diff --git a/tests/test_inlinequeryresultcachedgif.py b/tests/test_inlinequeryresultcachedgif.py index 82bbd7fa7f2..7af70a7daa7 100644 --- a/tests/test_inlinequeryresultcachedgif.py +++ b/tests/test_inlinequeryresultcachedgif.py @@ -73,6 +73,10 @@ def test_expected_values(self, inline_query_result_cached_gif): ) assert inline_query_result_cached_gif.reply_markup.to_dict() == self.reply_markup.to_dict() + def test_caption_entities_always_tuple(self): + result = InlineQueryResultCachedGif(self.id_, self.gif_file_id) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_cached_gif): inline_query_result_cached_gif_dict = inline_query_result_cached_gif.to_dict() diff --git a/tests/test_inlinequeryresultcachedmpeg4gif.py b/tests/test_inlinequeryresultcachedmpeg4gif.py index fd1548c84f0..df273eff1af 100644 --- a/tests/test_inlinequeryresultcachedmpeg4gif.py +++ b/tests/test_inlinequeryresultcachedmpeg4gif.py @@ -78,6 +78,10 @@ def test_expected_values(self, inline_query_result_cached_mpeg4_gif): == self.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + result = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_cached_mpeg4_gif): inline_query_result_cached_mpeg4_gif_dict = inline_query_result_cached_mpeg4_gif.to_dict() diff --git a/tests/test_inlinequeryresultcachedphoto.py b/tests/test_inlinequeryresultcachedphoto.py index c5e1289e76a..2ef9fbe62cf 100644 --- a/tests/test_inlinequeryresultcachedphoto.py +++ b/tests/test_inlinequeryresultcachedphoto.py @@ -78,6 +78,10 @@ def test_expected_values(self, inline_query_result_cached_photo): inline_query_result_cached_photo.reply_markup.to_dict() == self.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + result = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_cached_photo): inline_query_result_cached_photo_dict = inline_query_result_cached_photo.to_dict() diff --git a/tests/test_inlinequeryresultcachedvideo.py b/tests/test_inlinequeryresultcachedvideo.py index e2e9ddfcbae..c7861e0ce0c 100644 --- a/tests/test_inlinequeryresultcachedvideo.py +++ b/tests/test_inlinequeryresultcachedvideo.py @@ -78,6 +78,11 @@ def test_expected_values(self, inline_query_result_cached_video): inline_query_result_cached_video.reply_markup.to_dict() == self.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + video = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title) + + assert video.caption_entities == () + def test_to_dict(self, inline_query_result_cached_video): inline_query_result_cached_video_dict = inline_query_result_cached_video.to_dict() diff --git a/tests/test_inlinequeryresultcachedvoice.py b/tests/test_inlinequeryresultcachedvoice.py index 569f0030a41..c4033edc386 100644 --- a/tests/test_inlinequeryresultcachedvoice.py +++ b/tests/test_inlinequeryresultcachedvoice.py @@ -75,6 +75,10 @@ def test_expected_values(self, inline_query_result_cached_voice): inline_query_result_cached_voice.reply_markup.to_dict() == self.reply_markup.to_dict() ) + def test_caption_entities_always_tuple(self): + result = InlineQueryResultCachedVoice(self.id_, self.voice_file_id, self.title) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_cached_voice): inline_query_result_cached_voice_dict = inline_query_result_cached_voice.to_dict() diff --git a/tests/test_inlinequeryresultdocument.py b/tests/test_inlinequeryresultdocument.py index 41044240daa..2915e7451e6 100644 --- a/tests/test_inlinequeryresultdocument.py +++ b/tests/test_inlinequeryresultdocument.py @@ -88,6 +88,10 @@ def test_expected_values(self, inline_query_result_document): ) assert inline_query_result_document.reply_markup.to_dict() == self.reply_markup.to_dict() + def test_caption_entities_always_tuple(self): + result = InlineQueryResultDocument(self.id_, self.document_url, self.title, self.mime_type) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_document): inline_query_result_document_dict = inline_query_result_document.to_dict() diff --git a/tests/test_inlinequeryresultgif.py b/tests/test_inlinequeryresultgif.py index f9972b32792..f0242148155 100644 --- a/tests/test_inlinequeryresultgif.py +++ b/tests/test_inlinequeryresultgif.py @@ -69,6 +69,10 @@ def test_slot_behaviour(self, inline_query_result_gif, mro_slots): assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" + def test_caption_entities_always_tuple(self): + result = InlineQueryResultGif(self.id_, self.gif_url, self.thumb_url) + assert result.caption_entities == () + def test_expected_values(self, inline_query_result_gif): assert inline_query_result_gif.type == self.type_ assert inline_query_result_gif.id == self.id_ diff --git a/tests/test_inlinequeryresultmpeg4gif.py b/tests/test_inlinequeryresultmpeg4gif.py index a602b83f89d..2c564d94fd7 100644 --- a/tests/test_inlinequeryresultmpeg4gif.py +++ b/tests/test_inlinequeryresultmpeg4gif.py @@ -88,6 +88,10 @@ def test_expected_values(self, inline_query_result_mpeg4_gif): ) assert inline_query_result_mpeg4_gif.reply_markup.to_dict() == self.reply_markup.to_dict() + def test_caption_entities_always_tuple(self): + result = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumb_url) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_mpeg4_gif): inline_query_result_mpeg4_gif_dict = inline_query_result_mpeg4_gif.to_dict() diff --git a/tests/test_inlinequeryresultphoto.py b/tests/test_inlinequeryresultphoto.py index b16d8292f0b..0e40fc5a3db 100644 --- a/tests/test_inlinequeryresultphoto.py +++ b/tests/test_inlinequeryresultphoto.py @@ -86,6 +86,10 @@ def test_expected_values(self, inline_query_result_photo): ) assert inline_query_result_photo.reply_markup.to_dict() == self.reply_markup.to_dict() + def test_caption_entities_always_tuple(self): + result = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumb_url) + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_photo): inline_query_result_photo_dict = inline_query_result_photo.to_dict() diff --git a/tests/test_inlinequeryresultvideo.py b/tests/test_inlinequeryresultvideo.py index 46db2808168..dad63f132be 100644 --- a/tests/test_inlinequeryresultvideo.py +++ b/tests/test_inlinequeryresultvideo.py @@ -91,6 +91,12 @@ def test_expected_values(self, inline_query_result_video): ) assert inline_query_result_video.reply_markup.to_dict() == self.reply_markup.to_dict() + def test_caption_entities_always_tuple(self): + video = InlineQueryResultVideo( + self.id_, self.video_url, self.mime_type, self.thumb_url, self.title + ) + assert video.caption_entities == () + def test_to_dict(self, inline_query_result_video): inline_query_result_video_dict = inline_query_result_video.to_dict() diff --git a/tests/test_inlinequeryresultvoice.py b/tests/test_inlinequeryresultvoice.py index 71a2ad130ca..0a42aa86402 100644 --- a/tests/test_inlinequeryresultvoice.py +++ b/tests/test_inlinequeryresultvoice.py @@ -76,6 +76,15 @@ def test_expected_values(self, inline_query_result_voice): ) assert inline_query_result_voice.reply_markup.to_dict() == self.reply_markup.to_dict() + def test_caption_entities_always_tuple(self): + result = InlineQueryResultVoice( + self.id_, + self.voice_url, + self.title, + ) + + assert result.caption_entities == () + def test_to_dict(self, inline_query_result_voice): inline_query_result_voice_dict = inline_query_result_voice.to_dict() diff --git a/tests/test_inputinvoicemessagecontent.py b/tests/test_inputinvoicemessagecontent.py index 522aec386c4..93b79d4cc73 100644 --- a/tests/test_inputinvoicemessagecontent.py +++ b/tests/test_inputinvoicemessagecontent.py @@ -103,6 +103,44 @@ def test_expected_values(self, input_invoice_message_content): assert input_invoice_message_content.send_email_to_provider == self.send_email_to_provider assert input_invoice_message_content.is_flexible == self.is_flexible + def test_suggested_tip_amonuts_always_tuple(self): + input_invoice_message_content = InputInvoiceMessageContent( + title=self.title, + description=self.description, + payload=self.payload, + provider_token=self.provider_token, + currency=self.currency, + prices=self.prices, + max_tip_amount=self.max_tip_amount, + suggested_tip_amounts=self.suggested_tip_amounts, + provider_data=self.provider_data, + photo_url=self.photo_url, + photo_size=self.photo_size, + photo_width=self.photo_width, + photo_height=self.photo_height, + need_name=self.need_name, + need_phone_number=self.need_phone_number, + need_email=self.need_email, + need_shipping_address=self.need_shipping_address, + send_phone_number_to_provider=self.send_phone_number_to_provider, + send_email_to_provider=self.send_email_to_provider, + is_flexible=self.is_flexible, + ) + assert isinstance(input_invoice_message_content.suggested_tip_amounts, tuple) + assert input_invoice_message_content.suggested_tip_amounts == tuple( + int(amount) for amount in self.suggested_tip_amounts + ) + + input_invoice_message_content = InputInvoiceMessageContent( + title=self.title, + description=self.description, + payload=self.payload, + provider_token=self.provider_token, + currency=self.currency, + prices=self.prices, + ) + assert input_invoice_message_content.suggested_tip_amounts == tuple() + def test_to_dict(self, input_invoice_message_content): input_invoice_message_content_dict = input_invoice_message_content.to_dict() diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index 4d397f524a5..d7f0e96789d 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -149,6 +149,10 @@ def test_expected_values(self, input_media_video): assert input_media_video.supports_streaming == self.supports_streaming assert isinstance(input_media_video.thumb, InputFile) + def test_caption_entities_always_tuple(self): + input_media_video = InputMediaVideo(self.media) + assert input_media_video.caption_entities == () + def test_to_dict(self, input_media_video): input_media_video_dict = input_media_video.to_dict() assert input_media_video_dict["type"] == input_media_video.type @@ -208,6 +212,10 @@ def test_expected_values(self, input_media_photo): assert input_media_photo.parse_mode == self.parse_mode assert input_media_photo.caption_entities == tuple(self.caption_entities) + def test_caption_entities_always_tuple(self): + input_media_photo = InputMediaPhoto(self.media) + assert input_media_photo.caption_entities == () + def test_to_dict(self, input_media_photo): input_media_photo_dict = input_media_photo.to_dict() assert input_media_photo_dict["type"] == input_media_photo.type @@ -261,6 +269,10 @@ def test_expected_values(self, input_media_animation): assert input_media_animation.caption_entities == tuple(self.caption_entities) assert isinstance(input_media_animation.thumb, InputFile) + def test_caption_entities_always_tuple(self): + input_media_animation = InputMediaAnimation(self.media) + assert input_media_animation.caption_entities == () + def test_to_dict(self, input_media_animation): input_media_animation_dict = input_media_animation.to_dict() assert input_media_animation_dict["type"] == input_media_animation.type @@ -323,6 +335,10 @@ def test_expected_values(self, input_media_audio): assert input_media_audio.caption_entities == tuple(self.caption_entities) assert isinstance(input_media_audio.thumb, InputFile) + def test_caption_entities_always_tuple(self): + input_media_audio = InputMediaAudio(self.media) + assert input_media_audio.caption_entities == () + def test_to_dict(self, input_media_audio): input_media_audio_dict = input_media_audio.to_dict() assert input_media_audio_dict["type"] == input_media_audio.type @@ -387,6 +403,10 @@ def test_expected_values(self, input_media_document): ) assert isinstance(input_media_document.thumb, InputFile) + def test_caption_entities_always_tuple(self): + input_media_document = InputMediaDocument(self.media) + assert input_media_document.caption_entities == () + def test_to_dict(self, input_media_document): input_media_document_dict = input_media_document.to_dict() assert input_media_document_dict["type"] == input_media_document.type diff --git a/tests/test_inputtextmessagecontent.py b/tests/test_inputtextmessagecontent.py index d50472be272..6f288deebe2 100644 --- a/tests/test_inputtextmessagecontent.py +++ b/tests/test_inputtextmessagecontent.py @@ -50,6 +50,10 @@ def test_expected_values(self, input_text_message_content): assert input_text_message_content.disable_web_page_preview == self.disable_web_page_preview assert input_text_message_content.entities == tuple(self.entities) + def test_entities_always_tuple(self): + input_text_message_content = InputTextMessageContent("text") + assert input_text_message_content.entities == () + def test_to_dict(self, input_text_message_content): input_text_message_content_dict = input_text_message_content.to_dict() diff --git a/tests/test_webhookinfo.py b/tests/test_webhookinfo.py index f75e63424b3..575f11abfb9 100644 --- a/tests/test_webhookinfo.py +++ b/tests/test_webhookinfo.py @@ -99,6 +99,12 @@ def test_de_json(self, bot): none = WebhookInfo.de_json(None, bot) assert none is None + def test_always_tuple_allowed_updates(self): + webhook_info = WebhookInfo( + self.url, self.has_custom_certificate, self.pending_update_count + ) + assert webhook_info.allowed_updates == () + def test_equality(self): a = WebhookInfo( url=self.url, From 3d2125f1ace5520556a9e84e0d90c36a61fd05c5 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 19:00:19 +0100 Subject: [PATCH 84/90] Only filter out None --- telegram/_passport/passportfile.py | 11 +++++------ telegram/_telegramobject.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 010210c852b..376d4470596 100644 --- a/telegram/_passport/passportfile.py +++ b/telegram/_passport/passportfile.py @@ -139,13 +139,12 @@ def de_list_decrypted( return () return tuple( - filter( - None, - ( - cls.de_json_decrypted(passport_file, bot, credentials[i]) - for i, passport_file in enumerate(data) - ), + obj + for obj in ( + cls.de_json_decrypted(passport_file, bot, credentials[i]) + for i, passport_file in enumerate(data) ) + if obj is not None ) async def get_file( diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 2eb8804c6d6..bf5365bbec4 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -486,7 +486,7 @@ def de_list( if not data: return () - return tuple(filter(None, (cls.de_json(d, bot) for d in data))) + return tuple(obj for obj in (cls.de_json(d, bot) for d in data) if obj is not None) def to_json(self) -> str: """Gives a JSON representation of object. From 6301afd9664950c7a9a8cbca835b91c87a97fdf5 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 19:02:01 +0100 Subject: [PATCH 85/90] revert a docstring change --- telegram/_webhookinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index 78694c48ee4..dd878317f35 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -78,7 +78,7 @@ class WebhookInfo(TelegramObject): most recent error that happened when trying to deliver an update via webhook. max_connections (:obj:`int`): Optional. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery. - allowed_updates (Tuple[:obj:`str`]): A list of update types the bot is + allowed_updates (Tuple[:obj:`str`]): Optional. A list of update types the bot is subscribed to. Defaults to all update types, except :attr:`telegram.Update.chat_member`. From bbe7dc6a2da5ff58c01e1ac5567ba3202faa02ed Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 19:27:18 +0100 Subject: [PATCH 86/90] Fix a signature in `ExtBot` --- telegram/ext/_extbot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 594af23335b..514d18f93ef 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -22,6 +22,7 @@ from datetime import datetime from typing import ( TYPE_CHECKING, + Any, Callable, Dict, Generic, @@ -483,7 +484,7 @@ async def _send_message( connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union[bool, Message]: + ) -> Any: # We override this method to call self._replace_keyboard and self._insert_callback_data. # This covers most methods that have a reply_markup result = await super()._send_message( From 24e9f0ec7092db61e5806f3dabf2fd977f5d1757 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Fri, 2 Dec 2022 20:02:46 +0100 Subject: [PATCH 87/90] Drop empty sequences in `to_dict` --- telegram/_telegramobject.py | 15 +++++++++++++-- tests/test_telegramobject.py | 23 +++++++++++++---------- tests/test_videochat.py | 2 +- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index bf5365bbec4..e727798da91 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -503,7 +503,9 @@ def to_dict(self, recursive: bool = True) -> JSONDict: """Gives representation of object as :obj:`dict`. .. versionchanged:: 20.0 - Now includes all entries of :attr:`api_kwargs`. + + * Now includes all entries of :attr:`api_kwargs`. + * Attributes whose values are empty sequences are no longer included. Args: recursive (:obj:`bool`, optional): If :obj:`True`, will convert any TelegramObjects @@ -520,8 +522,14 @@ def to_dict(self, recursive: bool = True) -> JSONDict: # Now we should convert TGObjects to dicts inside objects such as sequences, and convert # datetimes to timestamps. This mostly eliminates the need for subclasses to override # `to_dict` + pop_keys: Set[str] = set() for key, value in out.items(): - if isinstance(value, (tuple, list)) and value: + if isinstance(value, (tuple, list)): + if not value: + # not popping directly to avoid changing the dict size during iteration + pop_keys.add(key) + continue + val = [] # empty list to append our converted values to for item in value: if hasattr(item, "to_dict"): @@ -541,6 +549,9 @@ def to_dict(self, recursive: bool = True) -> JSONDict: elif isinstance(value, datetime.datetime): out[key] = to_timestamp(value) + for key in pop_keys: + out.pop(key) + # Effectively "unpack" api_kwargs into `out`: out.update(out.pop("api_kwargs", {})) # type: ignore[call-overload] return out diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 4e8ccfe7955..132f70770f1 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -59,17 +59,20 @@ class ChangingTO(TelegramObject): pass def test_to_json(self, monkeypatch): - # to_json simply takes whatever comes from to_dict, therefore we only need to test it once - telegram_object = TelegramObject() + class Subclass(TelegramObject): + def __init__(self): + super().__init__() + self.arg = "arg" + self.arg2 = ["arg2", "arg2"] + self.arg3 = {"arg3": "arg3"} + self.empty_tuple = () - # Test that it works with a dict with str keys as well as dicts as lists as values - d = {"str": "str", "str2": ["str", "str"], "str3": {"str": "str"}} - monkeypatch.setattr("telegram.TelegramObject.to_dict", lambda _: d) - json = telegram_object.to_json() + json = Subclass().to_json() # Order isn't guarantied - assert '"str": "str"' in json - assert '"str2": ["str", "str"]' in json - assert '"str3": {"str": "str"}' in json + assert '"arg": "arg"' in json + assert '"arg2": ["arg2", "arg2"]' in json + assert '"arg3": {"arg3": "arg3"}' in json + assert "empty_tuple" not in json # Now make sure that it doesn't work with not json stuff and that it fails loudly # Tuples aren't allowed as keys in json @@ -77,7 +80,7 @@ def test_to_json(self, monkeypatch): monkeypatch.setattr("telegram.TelegramObject.to_dict", lambda _: d) with pytest.raises(TypeError): - telegram_object.to_json() + TelegramObject().to_json() def test_de_json_api_kwargs(self, bot): to = TelegramObject.de_json(data={"foo": "bar"}, bot=bot) diff --git a/tests/test_videochat.py b/tests/test_videochat.py index f144669eed7..81b36f9b852 100644 --- a/tests/test_videochat.py +++ b/tests/test_videochat.py @@ -126,7 +126,7 @@ def test_to_dict(self, user1, user2, use_users): assert video_chat_dict["users"][0]["id"] == user1.id assert video_chat_dict["users"][1]["id"] == user2.id else: - assert video_chat_dict == {"users": ()} + assert video_chat_dict == {} def test_equality(self, user1, user2): a = VideoChatParticipantsInvited([user1]) From fafe1d27b54afb3419a5752a4f15d85422728fc0 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Sun, 4 Dec 2022 21:53:57 +0100 Subject: [PATCH 88/90] Remove transition script --- immutabilize.py | 179 ------------------------------------------------ 1 file changed, 179 deletions(-) delete mode 100644 immutabilize.py diff --git a/immutabilize.py b/immutabilize.py deleted file mode 100644 index 5cb56a5d98e..00000000000 --- a/immutabilize.py +++ /dev/null @@ -1,179 +0,0 @@ -# Loops through all TG classes and converts list-type attributes to tuples. -import inspect -import pickle -import re -from pathlib import Path - -import isort - -import telegram - -changed_files = set() -processed_classes = set() - -try: - processed_classes = pickle.load(open("processed_classes.pickle", "rb")) -except Exception: - print("Could not load pickle file") - processed_classes = set() - -unprocessed_classes = set() - -# loop through all classes in the `telegram` module -classes = inspect.getmembers(telegram, inspect.isclass) -for name, cls in classes: - if cls in processed_classes: - continue - - print("Processing class", name) - # first adjust the __init__ of the class - params = inspect.signature(cls.__init__).parameters - params_to_change = {} - for param in params.values(): - if "List" in str(param.annotation): - print(" Converting list-type parameter", param.name, "to Sequence") - params_to_change[param.name] = param.default - print(" ", param.name, "default value:", repr(param.default)) - - if not params_to_change: - continue - - class_source_file = Path(inspect.getfile(cls)) - if class_source_file not in changed_files: - changed_files.add(class_source_file) - processed_classes.add(cls) - else: - unprocessed_classes.add(cls) - continue - - _, class_start_line = inspect.getsourcelines(cls) - class_source = inspect.getsource(cls) - class_source_lines = class_source.splitlines() - class_length = len(class_source_lines) - - init_source_lines, init_start_line = inspect.getsourcelines(cls.__init__) - init_length = len(init_source_lines) - init_source = inspect.getsource(cls.__init__) - - args_start_line = -1 - attributes_start_line = -1 - # Search "Args:" block in the docstring - for i, line in enumerate(class_source_lines): - if line.strip().startswith("Args:"): - args_start_line = i - if line.strip().startswith("Attributes:"): - attributes_start_line = i - if class_start_line + i == init_start_line: - break - - # In the "Args:" block replace "List[" by "Sequence[" - for i in range(args_start_line + 1, attributes_start_line): - whitespaces = -1 - for param in params_to_change: - if f"{param} (List[" not in class_source_lines[i]: - continue - - class_source_lines[i] = class_source_lines[i].replace( - f"{param} (List[", f"{param} (Sequence[" - ) - whitespaces = re.match(r" +", class_source_lines[i]).end() + 4 - j = i + 1 - while class_source_lines[j] and ( - re.match(r"\s+", class_source_lines[j]).end() >= whitespaces - ): - j = j + 1 - - class_source_lines[j - 1] += ( - f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" - " |sequenceclassargs|" - ) - if class_source_lines[j]: - class_source_lines[j - 1] += "\n" - - # In the "Attributes:" block replace "List[" by "Sequence[" - for i in range(attributes_start_line + 1, class_length): - whitespaces = -1 - for param in params_to_change: - if f"{param} (List[" not in class_source_lines[i]: - continue - - class_source_lines[i] = class_source_lines[i].replace( - f"{param} (List[", f"{param} (Tuple[" - ) - whitespaces = re.match(r" +", class_source_lines[i]).end() + 4 - j = i + 1 - while class_source_lines[j] and ( - re.match(r"\s+", class_source_lines[j]).end() >= whitespaces - ): - j = j + 1 - - class_source_lines[j - 1] += ( - f"\n\n{whitespaces * ' '}.. versionchanged:: 20.0\n{whitespaces * ' '}" - " |tupleclassattrs|" - ) - if class_source_lines[j]: - class_source_lines[j - 1] += "\n" - - # Adjust type annotations in the __init__ and converts to tuples before assigning to - # attributes - for param, default_value in params_to_change.items(): - init_source = init_source.replace(param + ": List", param + ": Sequence") - init_source = re.sub( - rf"{param}: Union\[List\[(\w+)\], Tuple\[\w+, \.\.\.\]\]", - rf"{param}: Sequence[\1]", - init_source, - ) - init_source = re.sub( - rf"{param}: Union\[Tuple\[\w+, \.\.\.\], List\[(\w+)\]\]", - rf"{param}: Sequence[\1]", - init_source, - ) - if default_value is None: - init_source = re.sub( - rf"self\.{param} = (\S*)\n", - rf"self.{param} = tuple(\1) if \1 else None\n", - init_source, - ) - else: - init_source = re.sub( - rf"self\.{param} = (\S*)\n", rf"self.{param} = tuple(\1)\n", init_source - ) - init_source = re.sub( - rf"self\.{param} = (.*) or \[\]\n", - rf"self.{param} = tuple(\1) if \1 else ()\n", - init_source, - ) - - file_contents = class_source_file.read_text(encoding="utf-8") - file_contents = file_contents.splitlines() - - # Insert new __init__ - file_contents[ - init_start_line - 1 : init_start_line + init_length - 1 - ] = init_source.splitlines() - - # Insert new docstring - file_contents[class_start_line - 1 : init_start_line - 2] = class_source_lines[ - : init_start_line - class_start_line - 1 - ] - - i = 0 - while file_contents[i].startswith("#"): - i += 1 - file_contents[i] += "\nfrom typing import Sequence" - - file_contents = "\n".join(file_contents) + "\n" - - # Sort imports - file_contents = isort.code(file_contents) - - class_source_file.write_text(file_contents, encoding="utf-8") - -if unprocessed_classes: - print( - "Rerun the script to finish the conversion. I can't handle files that contain multiple " - "classes. The following classes were not processed:" - f"{', '.join([cls.__name__ for cls in unprocessed_classes])}" - ) - -pickle.dump(processed_classes, open("processed_classes.pickle", "wb")) From d5903f2cce4bea8d01258440c40421ffe2c71131 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 6 Dec 2022 09:56:18 +0100 Subject: [PATCH 89/90] DS fixes --- telegram/_telegramobject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index e727798da91..a15e1d79c86 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -137,7 +137,7 @@ def _apply_api_kwargs(self, api_kwargs: JSONDict) -> None: setattr(self, key, api_kwargs.pop(key)) def __setattr__(self, key: str, value: object) -> None: - """ "Overrides :meth:`object.__setattr__` to prevent the overriding of attributes. + """Overrides :meth:`object.__setattr__` to prevent the overriding of attributes. Raises: :exc:`AttributeError` @@ -152,7 +152,7 @@ def __setattr__(self, key: str, value: object) -> None: ) def __delattr__(self, key: str) -> None: - """ "Overrides :meth:`object.__delattr__` to prevent the deletion of attributes. + """Overrides :meth:`object.__delattr__` to prevent the deletion of attributes. Raises: :exc:`AttributeError` From ac5634c615d02d9424b70164e6c67dbba6751793 Mon Sep 17 00:00:00 2001 From: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com> Date: Tue, 13 Dec 2022 12:43:15 +0100 Subject: [PATCH 90/90] adapt to merge conflicts --- tests/auxil/bot_method_checks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auxil/bot_method_checks.py b/tests/auxil/bot_method_checks.py index 35d750c603e..289ed35747e 100644 --- a/tests/auxil/bot_method_checks.py +++ b/tests/auxil/bot_method_checks.py @@ -277,7 +277,7 @@ async def check_defaults_handling( kwargs["tzinfo"] = pytz.timezone("America/New_York") defaults_custom_defaults = Defaults(**kwargs) - expected_return_values = [None, []] if return_value is None else [return_value] + expected_return_values = [None, ()] if return_value is None else [return_value] async def make_assertion( url, request_data: RequestData, df_value=DEFAULT_NONE, *args, **kwargs