diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 9c59cb8ee..c1fd3df2b 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -20,7 +20,7 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] os: [ubuntu-latest, windows-latest, macos-latest] fail-fast: False steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0da0cea13..99e1312da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -72,7 +72,7 @@ repos: hooks: - id: pyupgrade args: - - --py38-plus + - --py39-plus - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: diff --git a/README.rst b/README.rst index 4c7cba543..035180a3c 100644 --- a/README.rst +++ b/README.rst @@ -70,7 +70,7 @@ Introduction This library provides a pure Python, asynchronous interface for the `Telegram Bot API `_. -It's compatible with Python versions **3.8+**. +It's compatible with Python versions **3.9+**. In addition to the pure API implementation, this library features several convenience methods and shortcuts as well as a number of high-level classes to make the development of bots easy and straightforward. These classes are contained in the diff --git a/docs/auxil/admonition_inserter.py b/docs/auxil/admonition_inserter.py index 945502533..268b8e7e7 100644 --- a/docs/auxil/admonition_inserter.py +++ b/docs/auxil/admonition_inserter.py @@ -20,7 +20,8 @@ import inspect import re import typing from collections import defaultdict -from typing import Any, Iterator, Union +from collections.abc import Iterator +from typing import Any, Union import telegram import telegram.ext diff --git a/docs/auxil/kwargs_insertion.py b/docs/auxil/kwargs_insertion.py index ffb2ada13..7bc4a9e01 100644 --- a/docs/auxil/kwargs_insertion.py +++ b/docs/auxil/kwargs_insertion.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. import inspect -from typing import List keyword_args = [ "Keyword Arguments:", @@ -85,7 +84,7 @@ get_updates_read_timeout_addition = [ ] -def find_insert_pos_for_kwargs(lines: List[str]) -> int: +def find_insert_pos_for_kwargs(lines: list[str]) -> int: """Finds the correct position to insert the keyword arguments and returns the index.""" for idx, value in reversed(list(enumerate(lines))): # reversed since :returns: is at the end if value.startswith("Returns"): diff --git a/docs/auxil/link_code.py b/docs/auxil/link_code.py index 8c20f34b4..f451dc502 100644 --- a/docs/auxil/link_code.py +++ b/docs/auxil/link_code.py @@ -21,7 +21,6 @@ https://github.com/sphinx-doc/sphinx/issues/1556 is closed """ import subprocess from pathlib import Path -from typing import Dict, Tuple from sphinx.util import logging @@ -32,7 +31,7 @@ sphinx_logger = logging.getLogger(__name__) # must be a module-level variable so that it can be written to by the `autodoc-process-docstring` # event handler in `sphinx_hooks.py` -LINE_NUMBERS: Dict[str, Tuple[Path, int, int]] = {} +LINE_NUMBERS: dict[str, tuple[Path, int, int]] = {} def _git_branch() -> str: diff --git a/examples/arbitrarycallbackdatabot.py b/examples/arbitrarycallbackdatabot.py index cf3d46fa9..e11620c16 100644 --- a/examples/arbitrarycallbackdatabot.py +++ b/examples/arbitrarycallbackdatabot.py @@ -12,7 +12,7 @@ To use arbitrary callback data, you must install PTB via `pip install "python-telegram-bot[callback-data]"` """ import logging -from typing import List, Tuple, cast +from typing import cast from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import ( @@ -36,7 +36,7 @@ logger = logging.getLogger(__name__) async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Sends a message with 5 inline buttons attached.""" - number_list: List[int] = [] + number_list: list[int] = [] await update.message.reply_text("Please choose:", reply_markup=build_keyboard(number_list)) @@ -55,7 +55,7 @@ async def clear(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: await update.effective_message.reply_text("All clear!") -def build_keyboard(current_list: List[int]) -> InlineKeyboardMarkup: +def build_keyboard(current_list: list[int]) -> InlineKeyboardMarkup: """Helper function to build the next inline keyboard.""" return InlineKeyboardMarkup.from_column( [InlineKeyboardButton(str(i), callback_data=(i, current_list)) for i in range(1, 6)] @@ -69,7 +69,7 @@ async def list_button(update: Update, context: ContextTypes.DEFAULT_TYPE) -> Non # Get the data from the callback_data. # If you're using a type checker like MyPy, you'll have to use typing.cast # to make the checker get the expected type of the callback_data - number, number_list = cast(Tuple[int, List[int]], query.data) + number, number_list = cast(tuple[int, list[int]], query.data) # append the number to the list number_list.append(number) diff --git a/examples/chatmemberbot.py b/examples/chatmemberbot.py index dd299b73a..34dad2a83 100644 --- a/examples/chatmemberbot.py +++ b/examples/chatmemberbot.py @@ -12,7 +12,7 @@ bot. """ import logging -from typing import Optional, Tuple +from typing import Optional from telegram import Chat, ChatMember, ChatMemberUpdated, Update from telegram.constants import ParseMode @@ -37,7 +37,7 @@ logging.getLogger("httpx").setLevel(logging.WARNING) logger = logging.getLogger(__name__) -def extract_status_change(chat_member_update: ChatMemberUpdated) -> Optional[Tuple[bool, bool]]: +def extract_status_change(chat_member_update: ChatMemberUpdated) -> Optional[tuple[bool, bool]]: """Takes a ChatMemberUpdated instance and extracts whether the 'old_chat_member' was a member of the chat and whether the 'new_chat_member' is a member of the chat. Returns None, if the status didn't change. diff --git a/examples/contexttypesbot.py b/examples/contexttypesbot.py index 9c361772c..b89d8ffc7 100644 --- a/examples/contexttypesbot.py +++ b/examples/contexttypesbot.py @@ -12,7 +12,7 @@ bot. import logging from collections import defaultdict -from typing import DefaultDict, Optional, Set +from typing import Optional from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.constants import ParseMode @@ -40,7 +40,7 @@ class ChatData: """Custom class for chat_data. Here we store data per message.""" def __init__(self) -> None: - self.clicks_per_message: DefaultDict[int, int] = defaultdict(int) + self.clicks_per_message: defaultdict[int, int] = defaultdict(int) # The [ExtBot, dict, ChatData, dict] is for type checkers like mypy @@ -57,7 +57,7 @@ class CustomContext(CallbackContext[ExtBot, dict, ChatData, dict]): self._message_id: Optional[int] = None @property - def bot_user_ids(self) -> Set[int]: + def bot_user_ids(self) -> set[int]: """Custom shortcut to access a value stored in the bot_data dict""" return self.bot_data.setdefault("user_ids", set()) diff --git a/examples/conversationbot2.py b/examples/conversationbot2.py index 6a5e54a8e..af29e0198 100644 --- a/examples/conversationbot2.py +++ b/examples/conversationbot2.py @@ -15,7 +15,6 @@ bot. """ import logging -from typing import Dict from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update from telegram.ext import ( @@ -46,7 +45,7 @@ reply_keyboard = [ markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) -def facts_to_str(user_data: Dict[str, str]) -> str: +def facts_to_str(user_data: dict[str, str]) -> str: """Helper function for formatting the gathered user info.""" facts = [f"{key} - {value}" for key, value in user_data.items()] return "\n".join(facts).join(["\n", "\n"]) diff --git a/examples/nestedconversationbot.py b/examples/nestedconversationbot.py index fdc49de2b..bc940f4cd 100644 --- a/examples/nestedconversationbot.py +++ b/examples/nestedconversationbot.py @@ -15,7 +15,7 @@ bot. """ import logging -from typing import Any, Dict, Tuple +from typing import Any from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.ext import ( @@ -66,7 +66,7 @@ END = ConversationHandler.END # Helper -def _name_switcher(level: str) -> Tuple[str, str]: +def _name_switcher(level: str) -> tuple[str, str]: if level == PARENTS: return "Father", "Mother" return "Brother", "Sister" @@ -122,7 +122,7 @@ async def adding_self(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str async def show_data(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: """Pretty print gathered data.""" - def pretty_print(data: Dict[str, Any], level: str) -> str: + def pretty_print(data: dict[str, Any], level: str) -> str: people = data.get(level) if not people: return "\nNo information yet." diff --git a/examples/persistentconversationbot.py b/examples/persistentconversationbot.py index 19be96f56..4c5322456 100644 --- a/examples/persistentconversationbot.py +++ b/examples/persistentconversationbot.py @@ -15,7 +15,6 @@ bot. """ import logging -from typing import Dict from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update from telegram.ext import ( @@ -47,7 +46,7 @@ reply_keyboard = [ markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) -def facts_to_str(user_data: Dict[str, str]) -> str: +def facts_to_str(user_data: dict[str, str]) -> str: """Helper function for formatting the gathered user info.""" facts = [f"{key} - {value}" for key, value in user_data.items()] return "\n".join(facts).join(["\n", "\n"]) diff --git a/pyproject.toml b/pyproject.toml index 80edfde44..cb98f0057 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] name = "python-telegram-bot" description = "We have made you a wrapper you can't refuse" readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.9" license = "LGPL-3.0-only" license-files = { paths = ["LICENSE", "LICENSE.dual", "LICENSE.lesser"] } authors = [ @@ -31,7 +31,6 @@ classifiers = [ "Topic :: Internet", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -192,7 +191,7 @@ disallow_untyped_defs = true disallow_incomplete_defs = true disallow_untyped_decorators = true show_error_codes = true -python_version = "3.8" +python_version = "3.9" # For some files, it's easier to just disable strict-optional all together instead of # cluttering the code with `# type: ignore`s or stuff like diff --git a/telegram/_bot.py b/telegram/_bot.py index 513e43d16..345dac4ed 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -23,21 +23,15 @@ import asyncio import contextlib import copy import pickle +from collections.abc import Sequence from datetime import datetime from types import TracebackType from typing import ( TYPE_CHECKING, Any, - AsyncContextManager, Callable, - Dict, - List, NoReturn, Optional, - Sequence, - Set, - Tuple, - Type, TypeVar, Union, cast, @@ -130,7 +124,7 @@ if TYPE_CHECKING: BT = TypeVar("BT", bound="Bot") -class Bot(TelegramObject, AsyncContextManager["Bot"]): +class Bot(TelegramObject, contextlib.AbstractAsyncContextManager["Bot"]): """This object represents a Telegram Bot. Instances of this class can be used as asyncio context managers, where @@ -263,7 +257,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): self._private_key: Optional[bytes] = None self._initialized: bool = False - self._request: Tuple[BaseRequest, BaseRequest] = ( + self._request: tuple[BaseRequest, BaseRequest] = ( HTTPXRequest() if get_updates_request is None else get_updates_request, HTTPXRequest() if request is None else request, ) @@ -332,7 +326,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: @@ -352,7 +346,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): """ raise pickle.PicklingError("Bot objects cannot be pickled!") - def __deepcopy__(self, memodict: Dict[int, object]) -> NoReturn: + def __deepcopy__(self, memodict: dict[int, object]) -> NoReturn: """Customizes how :func:`copy.deepcopy` processes objects of this type. Bots can not be deepcopied and this method will always raise an exception. @@ -528,7 +522,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): def _warn( cls, message: Union[str, PTBUserWarning], - category: Type[Warning] = PTBUserWarning, + category: type[Warning] = PTBUserWarning, stacklevel: int = 0, ) -> None: """Convenience method to issue a warning. This method is here mostly to make it easier @@ -539,7 +533,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): def _parse_file_input( self, file_input: Union[FileInput, "TelegramObject"], - tg_type: Optional[Type["TelegramObject"]] = None, + tg_type: Optional[type["TelegramObject"]] = None, filename: Optional[str] = None, attach: bool = False, ) -> Union[str, "InputFile", Any]: @@ -551,7 +545,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): local_mode=self._local_mode, ) - def _insert_defaults(self, data: Dict[str, object]) -> None: + def _insert_defaults(self, data: dict[str, object]) -> None: """This method is here to make ext.Defaults work. Because we need to be able to tell e.g. `send_message(chat_id, text)` from `send_message(chat_id, text, parse_mode=None)`, the default values for `parse_mode` etc are not `None` but `DEFAULT_NONE`. While this *could* @@ -605,7 +599,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, ) -> Any: - # We know that the return type is Union[bool, JSONDict, List[JSONDict]], but it's hard + # 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: @@ -638,7 +632,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Union[bool, JSONDict, List[JSONDict]]: + ) -> 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 @@ -798,7 +792,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): self, endpoint: str, api_kwargs: Optional[JSONDict] = None, - return_type: Optional[Type[TelegramObject]] = None, + return_type: Optional[type[TelegramObject]] = None, *, read_timeout: ODVInput[float] = DEFAULT_NONE, write_timeout: ODVInput[float] = DEFAULT_NONE, @@ -1226,7 +1220,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[MessageId, ...]: + ) -> tuple[MessageId, ...]: """ Use this method to forward messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content @@ -1248,7 +1242,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): message_thread_id (:obj:`int`, optional): |message_thread_id_arg| Returns: - Tuple[:class:`telegram.Message`]: On success, a tuple of ``MessageId`` of sent messages + tuple[:class:`telegram.Message`]: On success, a tuple of ``MessageId`` of sent messages is returned. Raises: @@ -2499,7 +2493,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, - ) -> Tuple[Message, ...]: + ) -> tuple[Message, ...]: """Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. @@ -2581,7 +2575,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): .. versionadded:: 20.0 Returns: - Tuple[:class:`telegram.Message`]: An array of the sent Messages. + tuple[:class:`telegram.Message`]: An array of the sent Messages. Raises: :class:`telegram.error.TelegramError` @@ -3403,7 +3397,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): ], next_offset: Optional[str] = None, current_offset: Optional[str] = None, - ) -> Tuple[Sequence["InlineQueryResult"], Optional[str]]: + ) -> tuple[Sequence["InlineQueryResult"], Optional[str]]: """ Builds the effective results from the results input. We make this a stand-alone method so tg.ext.ExtBot can wrap it. @@ -3526,7 +3520,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): Args: inline_query_id (:obj:`str`): Unique identifier for the answered query. - results (List[:class:`telegram.InlineQueryResult`] | Callable): A list of results for + results (list[:class:`telegram.InlineQueryResult`] | Callable): A list of results for the inline query. In case :paramref:`current_offset` is passed, :paramref:`results` may also be a callable that accepts the current page index starting from 0. It must return @@ -4280,7 +4274,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[Update, ...]: + ) -> tuple[Update, ...]: """Use this method to receive incoming updates using long polling. Note: @@ -4325,7 +4319,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): |sequenceargs| Returns: - Tuple[:class:`telegram.Update`] + tuple[:class:`telegram.Update`] Raises: :class:`telegram.error.TelegramError` @@ -4362,7 +4356,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): # waiting for the server to return and there's no way of knowing the connection had been # dropped in real time. result = cast( - List[JSONDict], + list[JSONDict], await self._post( "getUpdates", data, @@ -4626,7 +4620,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[ChatMember, ...]: + ) -> tuple[ChatMember, ...]: """ Use this method to get a list of administrators in a chat. @@ -4637,7 +4631,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): chat_id (:obj:`int` | :obj:`str`): |chat_id_channel| Returns: - Tuple[:class:`telegram.ChatMember`]: On success, returns a tuple 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. @@ -4901,7 +4895,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[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. @@ -4924,7 +4918,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): :paramref:`message_id` are not specified. Identifier of the inline message. Returns: - Tuple[:class:`telegram.GameHighScore`] + tuple[:class:`telegram.GameHighScore`] Raises: :class:`telegram.error.TelegramError` @@ -6304,7 +6298,7 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[Sticker, ...]: + ) -> tuple[Sticker, ...]: """ Use this method to get information about emoji stickers by their identifiers. @@ -6320,7 +6314,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. |sequenceargs| Returns: - Tuple[:class:`telegram.Sticker`] + tuple[:class:`telegram.Sticker`] Raises: :class:`telegram.error.TelegramError` @@ -7427,7 +7421,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[BotCommand, ...]: + ) -> tuple[BotCommand, ...]: """ Use this method to get the current list of the bot's commands for the given scope and user language. @@ -7449,7 +7443,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. .. versionadded:: 13.7 Returns: - Tuple[:class:`telegram.BotCommand`]: On success, the commands set for the bot. An empty + tuple[:class:`telegram.BotCommand`]: On success, the commands set for the bot. An empty tuple is returned if commands are not set. Raises: @@ -7472,7 +7466,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. async def set_my_commands( self, - commands: Sequence[Union[BotCommand, Tuple[str, str]]], + commands: Sequence[Union[BotCommand, tuple[str, str]]], scope: Optional[BotCommandScope] = None, language_code: Optional[str] = None, *, @@ -7791,7 +7785,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway @@ -7819,7 +7813,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. their captions. Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of the sent messages is returned. Raises: @@ -8072,14 +8066,14 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[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. .. versionadded:: 20.0 Returns: - Tuple[:class:`telegram.Sticker`] + tuple[:class:`telegram.Sticker`] Raises: :class:`telegram.error.TelegramError` @@ -8968,7 +8962,7 @@ CUSTOM_EMOJI_IDENTIFIER_LIMIT` custom emoji identifiers can be specified. Raises: :class:`telegram.error.TelegramError` """ - allowed_reactions: Set[str] = set(ReactionEmoji) + allowed_reactions: set[str] = set(ReactionEmoji) parsed_reaction = ( [ ( diff --git a/telegram/_botcommandscope.py b/telegram/_botcommandscope.py index 73cafd175..8d068802c 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, Dict, Final, Optional, Type, Union +from typing import TYPE_CHECKING, Final, Optional, Union from telegram import constants from telegram._telegramobject import TelegramObject @@ -91,7 +91,7 @@ class BotCommandScope(TelegramObject): care of selecting the correct subclass. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to :obj:`None`, in which case shortcut methods will not be available. @@ -107,7 +107,7 @@ class BotCommandScope(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[BotCommandScope]] = { + _class_mapping: dict[str, type[BotCommandScope]] = { cls.DEFAULT: BotCommandScopeDefault, cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats, cls.ALL_GROUP_CHATS: BotCommandScopeAllGroupChats, diff --git a/telegram/_business.py b/telegram/_business.py index 22c89e024..15512e63d 100644 --- a/telegram/_business.py +++ b/telegram/_business.py @@ -19,8 +19,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/] """This module contains the Telegram Business related classes.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from typing import TYPE_CHECKING, Optional from telegram._chat import Chat from telegram._files.location import Location @@ -145,7 +146,7 @@ class BusinessMessagesDeleted(TelegramObject): business_connection_id (:obj:`str`): Unique identifier of the business connection. chat (:class:`telegram.Chat`): Information about a chat in the business account. The bot may not have access to the chat or the corresponding user. - message_ids (Tuple[:obj:`int`]): A list of identifiers of the deleted messages in the + message_ids (tuple[:obj:`int`]): A list of identifiers of the deleted messages in the chat of the business account. """ @@ -166,7 +167,7 @@ class BusinessMessagesDeleted(TelegramObject): super().__init__(api_kwargs=api_kwargs) self.business_connection_id: str = business_connection_id self.chat: Chat = chat - self.message_ids: Tuple[int, ...] = parse_sequence_arg(message_ids) + self.message_ids: tuple[int, ...] = parse_sequence_arg(message_ids) self._id_attrs = ( self.business_connection_id, @@ -359,37 +360,37 @@ class BusinessOpeningHoursInterval(TelegramObject): self.opening_minute: int = opening_minute self.closing_minute: int = closing_minute - self._opening_time: Optional[Tuple[int, int, int]] = None - self._closing_time: Optional[Tuple[int, int, int]] = None + self._opening_time: Optional[tuple[int, int, int]] = None + self._closing_time: Optional[tuple[int, int, int]] = None self._id_attrs = (self.opening_minute, self.closing_minute) self._freeze() - def _parse_minute(self, minute: int) -> Tuple[int, int, int]: + def _parse_minute(self, minute: int) -> tuple[int, int, int]: return (minute // 1440, minute % 1440 // 60, minute % 1440 % 60) @property - def opening_time(self) -> Tuple[int, int, int]: + def opening_time(self) -> tuple[int, int, int]: """Convenience attribute. A :obj:`tuple` parsed from :attr:`opening_minute`. It contains the `weekday`, `hour` and `minute` in the same ranges as :attr:`datetime.datetime.weekday`, :attr:`datetime.datetime.hour` and :attr:`datetime.datetime.minute` Returns: - Tuple[:obj:`int`, :obj:`int`, :obj:`int`]: + tuple[:obj:`int`, :obj:`int`, :obj:`int`]: """ if self._opening_time is None: self._opening_time = self._parse_minute(self.opening_minute) return self._opening_time @property - def closing_time(self) -> Tuple[int, int, int]: + def closing_time(self) -> tuple[int, int, int]: """Convenience attribute. A :obj:`tuple` parsed from :attr:`closing_minute`. It contains the `weekday`, `hour` and `minute` in the same ranges as :attr:`datetime.datetime.weekday`, :attr:`datetime.datetime.hour` and :attr:`datetime.datetime.minute` Returns: - Tuple[:obj:`int`, :obj:`int`, :obj:`int`]: + tuple[:obj:`int`, :obj:`int`, :obj:`int`]: """ if self._closing_time is None: self._closing_time = self._parse_minute(self.closing_minute) diff --git a/telegram/_callbackquery.py b/telegram/_callbackquery.py index bdfa569db..af44a3243 100644 --- a/telegram/_callbackquery.py +++ b/telegram/_callbackquery.py @@ -18,7 +18,8 @@ # 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, Final, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Optional, Union from telegram import constants from telegram._files.location import Location @@ -676,7 +677,7 @@ class CallbackQuery(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["GameHighScore", ...]: + ) -> tuple["GameHighScore", ...]: """Shortcut for either:: await update.callback_query.message.get_game_high_score(*args, **kwargs) @@ -695,7 +696,7 @@ class CallbackQuery(TelegramObject): Raises :exc:`TypeError` if :attr:`message` is not accessible. Returns: - Tuple[:class:`telegram.GameHighScore`] + tuple[:class:`telegram.GameHighScore`] Raises: :exc:`TypeError` if :attr:`message` is not accessible. diff --git a/telegram/_chat.py b/telegram/_chat.py index 8c5f70524..7e5dc0ad8 100644 --- a/telegram/_chat.py +++ b/telegram/_chat.py @@ -18,9 +18,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 Chat.""" +from collections.abc import Sequence from datetime import datetime from html import escape -from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Final, Optional, Union from telegram import constants from telegram._chatpermissions import ChatPermissions @@ -296,7 +297,7 @@ class _ChatBase(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["ChatMember", ...]: + ) -> tuple["ChatMember", ...]: """Shortcut for:: await bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs) @@ -305,7 +306,7 @@ class _ChatBase(TelegramObject): :meth:`telegram.Bot.get_chat_administrators`. Returns: - Tuple[:class:`telegram.ChatMember`]: A tuple 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. @@ -1140,7 +1141,7 @@ class _ChatBase(TelegramObject): caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, - ) -> Tuple["Message", ...]: + ) -> tuple["Message", ...]: """Shortcut for:: await bot.send_media_group(update.effective_chat.id, *args, **kwargs) @@ -1148,7 +1149,7 @@ class _ChatBase(TelegramObject): 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. """ @@ -2268,7 +2269,7 @@ class _ChatBase(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.copy_messages(chat_id=update.effective_chat.id, *args, **kwargs) @@ -2280,7 +2281,7 @@ class _ChatBase(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of the sent messages is returned. """ @@ -2313,7 +2314,7 @@ class _ChatBase(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.copy_messages(from_chat_id=update.effective_chat.id, *args, **kwargs) @@ -2325,7 +2326,7 @@ class _ChatBase(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of the sent messages is returned. """ @@ -2442,7 +2443,7 @@ class _ChatBase(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.forward_messages(chat_id=update.effective_chat.id, *args, **kwargs) @@ -2454,7 +2455,7 @@ class _ChatBase(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of sent messages is returned. """ @@ -2485,7 +2486,7 @@ class _ChatBase(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.forward_messages(from_chat_id=update.effective_chat.id, *args, **kwargs) @@ -2497,7 +2498,7 @@ class _ChatBase(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of sent messages is returned. """ diff --git a/telegram/_chatbackground.py b/telegram/_chatbackground.py index b33fd4d91..148c62803 100644 --- a/telegram/_chatbackground.py +++ b/telegram/_chatbackground.py @@ -17,7 +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 chat backgrounds.""" -from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._files.document import Document @@ -87,7 +88,7 @@ class BackgroundFill(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[BackgroundFill]] = { + _class_mapping: dict[str, type[BackgroundFill]] = { cls.SOLID: BackgroundFillSolid, cls.GRADIENT: BackgroundFillGradient, cls.FREEFORM_GRADIENT: BackgroundFillFreeformGradient, @@ -212,7 +213,7 @@ class BackgroundFillFreeformGradient(BackgroundFill): super().__init__(type=self.FREEFORM_GRADIENT, api_kwargs=api_kwargs) with self._unfrozen(): - self.colors: Tuple[int, ...] = parse_sequence_arg(colors) + self.colors: tuple[int, ...] = parse_sequence_arg(colors) self._id_attrs = (self.colors,) @@ -278,7 +279,7 @@ class BackgroundType(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[BackgroundType]] = { + _class_mapping: dict[str, type[BackgroundType]] = { cls.FILL: BackgroundTypeFill, cls.WALLPAPER: BackgroundTypeWallpaper, cls.PATTERN: BackgroundTypePattern, diff --git a/telegram/_chatboost.py b/telegram/_chatboost.py index e5e26d2f4..fee5fff9e 100644 --- a/telegram/_chatboost.py +++ b/telegram/_chatboost.py @@ -18,8 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram ChatBoosts.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._chat import Chat @@ -119,7 +120,7 @@ class ChatBoostSource(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[ChatBoostSource]] = { + _class_mapping: dict[str, type[ChatBoostSource]] = { cls.PREMIUM: ChatBoostSourcePremium, cls.GIFT_CODE: ChatBoostSourceGiftCode, cls.GIVEAWAY: ChatBoostSourceGiveaway, @@ -431,7 +432,7 @@ class UserChatBoosts(TelegramObject): user. Attributes: - boosts (Tuple[:class:`telegram.ChatBoost`]): List of boosts added to the chat by the user. + boosts (tuple[:class:`telegram.ChatBoost`]): List of boosts added to the chat by the user. """ __slots__ = ("boosts",) @@ -444,7 +445,7 @@ class UserChatBoosts(TelegramObject): ): super().__init__(api_kwargs=api_kwargs) - self.boosts: Tuple[ChatBoost, ...] = parse_sequence_arg(boosts) + self.boosts: tuple[ChatBoost, ...] = parse_sequence_arg(boosts) self._id_attrs = (self.boosts,) self._freeze() diff --git a/telegram/_chatfullinfo.py b/telegram/_chatfullinfo.py index de26101f3..6778cfae7 100644 --- a/telegram/_chatfullinfo.py +++ b/telegram/_chatfullinfo.py @@ -18,8 +18,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 ChatFullInfo.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from typing import TYPE_CHECKING, Optional from telegram._birthdate import Birthdate from telegram._chat import Chat, _ChatBase @@ -224,7 +225,7 @@ class ChatFullInfo(_ChatBase): .. versionadded:: 20.0 photo (:class:`telegram.ChatPhoto`): Optional. Chat photo. - active_usernames (Tuple[:obj:`str`]): Optional. If set, the list of all `active chat + active_usernames (tuple[:obj:`str`]): Optional. If set, the list of all `active chat usernames `_; for private chats, supergroups and channels. @@ -252,7 +253,7 @@ class ChatFullInfo(_ChatBase): of the user. .. versionadded:: 21.1 - available_reactions (Tuple[:class:`telegram.ReactionType`]): Optional. List of available + available_reactions (tuple[:class:`telegram.ReactionType`]): Optional. List of available reactions allowed in the chat. If omitted, then all of :const:`telegram.constants.ReactionEmoji` are allowed. @@ -483,14 +484,14 @@ class ChatFullInfo(_ChatBase): self.has_restricted_voice_and_video_messages: Optional[bool] = ( has_restricted_voice_and_video_messages ) - self.active_usernames: Tuple[str, ...] = parse_sequence_arg(active_usernames) + self.active_usernames: tuple[str, ...] = parse_sequence_arg(active_usernames) self.emoji_status_custom_emoji_id: Optional[str] = emoji_status_custom_emoji_id self.emoji_status_expiration_date: Optional[datetime] = emoji_status_expiration_date self.has_aggressive_anti_spam_enabled: Optional[bool] = ( has_aggressive_anti_spam_enabled ) self.has_hidden_members: Optional[bool] = has_hidden_members - self.available_reactions: Optional[Tuple[ReactionType, ...]] = parse_sequence_arg( + self.available_reactions: Optional[tuple[ReactionType, ...]] = parse_sequence_arg( available_reactions ) self.accent_color_id: Optional[int] = accent_color_id diff --git a/telegram/_chatmember.py b/telegram/_chatmember.py index da84516b1..99c87dfb0 100644 --- a/telegram/_chatmember.py +++ b/telegram/_chatmember.py @@ -19,7 +19,7 @@ """This module contains an object that represents a Telegram ChatMember.""" import datetime -from typing import TYPE_CHECKING, Dict, Final, Optional, Type +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._telegramobject import TelegramObject @@ -114,7 +114,7 @@ class ChatMember(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[ChatMember]] = { + _class_mapping: dict[str, type[ChatMember]] = { cls.OWNER: ChatMemberOwner, cls.ADMINISTRATOR: ChatMemberAdministrator, cls.MEMBER: ChatMemberMember, diff --git a/telegram/_chatmemberupdated.py b/telegram/_chatmemberupdated.py index 1aacb2185..82f86ef88 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, Dict, Optional, Tuple, Union +from typing import TYPE_CHECKING, Optional, Union from telegram._chat import Chat from telegram._chatinvitelink import ChatInviteLink @@ -162,7 +162,7 @@ class ChatMemberUpdated(TelegramObject): return super().de_json(data=data, bot=bot) - def _get_attribute_difference(self, attribute: str) -> Tuple[object, object]: + def _get_attribute_difference(self, attribute: str) -> tuple[object, object]: try: old = self.old_chat_member[attribute] except KeyError: @@ -177,9 +177,9 @@ class ChatMemberUpdated(TelegramObject): def difference( self, - ) -> Dict[ + ) -> dict[ str, - Tuple[ + tuple[ Union[str, bool, datetime.datetime, User], Union[str, bool, datetime.datetime, User] ], ]: @@ -198,7 +198,7 @@ class ChatMemberUpdated(TelegramObject): .. versionadded:: 13.5 Returns: - Dict[:obj:`str`, Tuple[:class:`object`, :class:`object`]]: A dictionary mapping + dict[:obj:`str`, tuple[:class:`object`, :class:`object`]]: A dictionary mapping attribute names to tuples of the form ``(old_value, new_value)`` """ # we first get the names of the attributes that have changed diff --git a/telegram/_dice.py b/telegram/_dice.py index 621e4b13f..f0e752fda 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 Final, List, Optional +from typing import Final, Optional from telegram import constants from telegram._telegramobject import TelegramObject @@ -114,8 +114,8 @@ class Dice(TelegramObject): .. versionadded:: 13.4 """ - ALL_EMOJI: Final[List[str]] = list(constants.DiceEmoji) - """List[:obj:`str`]: A list of all available dice emoji.""" + ALL_EMOJI: Final[list[str]] = list(constants.DiceEmoji) + """list[:obj:`str`]: A list of all available dice emoji.""" MIN_VALUE: Final[int] = constants.DiceLimit.MIN_VALUE """:const:`telegram.constants.DiceLimit.MIN_VALUE` diff --git a/telegram/_files/_basethumbedmedium.py b/telegram/_files/_basethumbedmedium.py index 20ff82eab..d0b66f35c 100644 --- a/telegram/_files/_basethumbedmedium.py +++ b/telegram/_files/_basethumbedmedium.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/]. """Common base class for media objects with thumbnails""" -from typing import TYPE_CHECKING, Optional, Type, TypeVar +from typing import TYPE_CHECKING, Optional, TypeVar from telegram._files._basemedium import _BaseMedium from telegram._files.photosize import PhotoSize @@ -82,7 +82,7 @@ class _BaseThumbedMedium(_BaseMedium): @classmethod def de_json( - cls: Type[ThumbedMT_co], data: Optional[JSONDict], bot: Optional["Bot"] = None + cls: type[ThumbedMT_co], data: Optional[JSONDict], bot: Optional["Bot"] = None ) -> Optional[ThumbedMT_co]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) diff --git a/telegram/_files/inputfile.py b/telegram/_files/inputfile.py index e7c9cc6c6..d376d16a1 100644 --- a/telegram/_files/inputfile.py +++ b/telegram/_files/inputfile.py @@ -130,7 +130,7 @@ class InputFile: Content may now be a file handle. Returns: - Tuple[:obj:`str`, :obj:`bytes` | :class:`IO`, :obj:`str`]: + tuple[:obj:`str`, :obj:`bytes` | :class:`IO`, :obj:`str`]: """ return self.filename, self.input_file_content, self.mimetype diff --git a/telegram/_files/inputmedia.py b/telegram/_files/inputmedia.py index c33a87a2d..6dcf9d578 100644 --- a/telegram/_files/inputmedia.py +++ b/telegram/_files/inputmedia.py @@ -17,7 +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/]. """Base class for Telegram InputMedia Objects.""" -from typing import Final, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Final, Optional, Union from telegram import constants from telegram._files.animation import Animation @@ -74,7 +75,7 @@ class InputMedia(TelegramObject): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -99,7 +100,7 @@ class InputMedia(TelegramObject): self.type: str = enum.get_member(constants.InputMediaType, media_type, media_type) self.media: Union[str, InputFile, Animation, Audio, Document, PhotoSize, Video] = media self.caption: Optional[str] = caption - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.parse_mode: ODVInput[str] = parse_mode self._freeze() @@ -321,7 +322,7 @@ class InputMediaAnimation(InputMedia): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. The parse mode to use for text formatting. - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -436,7 +437,7 @@ class InputMediaPhoto(InputMedia): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -546,7 +547,7 @@ class InputMediaVideo(InputMedia): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -676,7 +677,7 @@ class InputMediaAudio(InputMedia): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -779,7 +780,7 @@ class InputMediaDocument(InputMedia): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 diff --git a/telegram/_files/inputsticker.py b/telegram/_files/inputsticker.py index 8fc8b8461..59b8e8ba9 100644 --- a/telegram/_files/inputsticker.py +++ b/telegram/_files/inputsticker.py @@ -18,7 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InputSticker.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional, Union from telegram._files.sticker import MaskPosition from telegram._telegramobject import TelegramObject @@ -67,13 +68,13 @@ class InputSticker(TelegramObject): Attributes: sticker (:obj:`str` | :class:`telegram.InputFile`): The added sticker. - emoji_list (Tuple[:obj:`str`]): Tuple of + emoji_list (tuple[:obj:`str`]): Tuple of :tg-const:`telegram.constants.StickerLimit.MIN_STICKER_EMOJI` - :tg-const:`telegram.constants.StickerLimit.MAX_STICKER_EMOJI` emoji associated with the sticker. mask_position (:class:`telegram.MaskPosition`): Optional. Position where the mask should be placed on faces. For ":tg-const:`telegram.constants.StickerType.MASK`" stickers only. - keywords (Tuple[:obj:`str`]): Optional. Tuple of + keywords (tuple[:obj:`str`]): Optional. Tuple of 0-:tg-const:`telegram.constants.StickerLimit.MAX_SEARCH_KEYWORDS` search keywords for the sticker with the total length of up to :tg-const:`telegram.constants.StickerLimit.MAX_KEYWORD_LENGTH` characters. For @@ -110,9 +111,9 @@ class InputSticker(TelegramObject): local_mode=True, attach=True, ) - self.emoji_list: Tuple[str, ...] = parse_sequence_arg(emoji_list) + self.emoji_list: tuple[str, ...] = parse_sequence_arg(emoji_list) self.format: str = format self.mask_position: Optional[MaskPosition] = mask_position - self.keywords: Tuple[str, ...] = parse_sequence_arg(keywords) + self.keywords: tuple[str, ...] = parse_sequence_arg(keywords) self._freeze() diff --git a/telegram/_files/sticker.py b/telegram/_files/sticker.py index 3c3c1cd7e..01ebf37e6 100644 --- a/telegram/_files/sticker.py +++ b/telegram/_files/sticker.py @@ -17,7 +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 that represent stickers.""" -from typing import TYPE_CHECKING, Final, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._files._basethumbedmedium import _BaseThumbedMedium @@ -259,7 +260,7 @@ class StickerSet(TelegramObject): Attributes: name (:obj:`str`): Sticker set name. title (:obj:`str`): Sticker set title. - stickers (Tuple[:class:`telegram.Sticker`]): List of all set stickers. + stickers (tuple[:class:`telegram.Sticker`]): List of all set stickers. .. versionchanged:: 20.0 |tupleclassattrs| @@ -296,7 +297,7 @@ class StickerSet(TelegramObject): super().__init__(api_kwargs=api_kwargs) self.name: str = name self.title: str = title - self.stickers: Tuple[Sticker, ...] = parse_sequence_arg(stickers) + self.stickers: tuple[Sticker, ...] = parse_sequence_arg(stickers) self.sticker_type: str = sticker_type # Optional self.thumbnail: Optional[PhotoSize] = thumbnail diff --git a/telegram/_games/game.py b/telegram/_games/game.py index 1a25d1ad5..efe30ea7f 100644 --- a/telegram/_games/game.py +++ b/telegram/_games/game.py @@ -17,7 +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.""" -from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._files.animation import Animation from telegram._files.photosize import PhotoSize @@ -65,7 +66,7 @@ class Game(TelegramObject): Attributes: title (:obj:`str`): Title of the game. description (:obj:`str`): Description of the game. - photo (Tuple[: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 @@ -76,7 +77,7 @@ class Game(TelegramObject): 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.MAX_TEXT_LENGTH` characters. - text_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that + text_entities (tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc. This tuple is empty if the message does not contain text entities. @@ -112,10 +113,10 @@ class Game(TelegramObject): # Required self.title: str = title self.description: str = description - self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo) + self.photo: tuple[PhotoSize, ...] = parse_sequence_arg(photo) # Optionals self.text: Optional[str] = text - self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) + self.text_entities: tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) self.animation: Optional[Animation] = animation self._id_attrs = (self.title, self.description, self.photo) @@ -163,7 +164,7 @@ class Game(TelegramObject): return entity_text.decode(TextEncoding.UTF_16_LE) - def parse_text_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]: + def parse_text_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities from this message filtered by their @@ -176,13 +177,13 @@ class Game(TelegramObject): See :attr:`parse_text_entity` for more info. Args: - types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as + types (list[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as strings. If the :attr:`~telegram.MessageEntity.type` attribute of an entity is contained in this list, it will be returned. Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. """ diff --git a/telegram/_giveaway.py b/telegram/_giveaway.py index 1e258b477..b3af8ec99 100644 --- a/telegram/_giveaway.py +++ b/telegram/_giveaway.py @@ -18,7 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an objects that are related to Telegram giveaways.""" import datetime -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._chat import Chat from telegram._telegramobject import TelegramObject @@ -41,7 +42,7 @@ class Giveaway(TelegramObject): .. versionadded:: 20.8 Args: - chats (Tuple[:class:`telegram.Chat`]): The list of chats which the user must join to + chats (tuple[:class:`telegram.Chat`]): The list of chats which the user must join to participate in the giveaway. winners_selection_date (:class:`datetime.datetime`): The date when the giveaway winner will be selected. |datetime_localization| @@ -76,7 +77,7 @@ class Giveaway(TelegramObject): has_public_winners (:obj:`True`): Optional. :obj:`True`, if the list of giveaway winners will be visible to everyone prize_description (:obj:`str`): Optional. Description of additional giveaway prize - country_codes (Tuple[:obj:`str`]): Optional. A tuple of two-letter ISO 3166-1 alpha-2 + country_codes (tuple[:obj:`str`]): Optional. A tuple of two-letter ISO 3166-1 alpha-2 country codes indicating the countries from which eligible users for the giveaway must come. If empty, then all users can participate in the giveaway. Users with a phone number that was bought on Fragment can always participate in giveaways. @@ -117,13 +118,13 @@ class Giveaway(TelegramObject): ): super().__init__(api_kwargs=api_kwargs) - self.chats: Tuple[Chat, ...] = tuple(chats) + self.chats: tuple[Chat, ...] = tuple(chats) self.winners_selection_date: datetime.datetime = winners_selection_date self.winner_count: int = winner_count self.only_new_members: Optional[bool] = only_new_members self.has_public_winners: Optional[bool] = has_public_winners self.prize_description: Optional[str] = prize_description - self.country_codes: Tuple[str, ...] = parse_sequence_arg(country_codes) + self.country_codes: tuple[str, ...] = parse_sequence_arg(country_codes) self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count self.prize_star_count: Optional[int] = prize_star_count @@ -222,7 +223,7 @@ class GiveawayWinners(TelegramObject): winners_selection_date (:class:`datetime.datetime`): Point in time when winners of the giveaway were selected. |datetime_localization| winner_count (:obj:`int`): Total number of winners in the giveaway - winners (Tuple[:class:`telegram.User`]): tuple of up to + winners (tuple[:class:`telegram.User`]): tuple of up to :tg-const:`telegram.constants.GiveawayLimit.MAX_WINNERS` winners of the giveaway additional_chat_count (:obj:`int`): Optional. The number of other chats the user had to join in order to be eligible for the giveaway @@ -278,7 +279,7 @@ class GiveawayWinners(TelegramObject): self.giveaway_message_id: int = giveaway_message_id self.winners_selection_date: datetime.datetime = winners_selection_date self.winner_count: int = winner_count - self.winners: Tuple[User, ...] = tuple(winners) + self.winners: tuple[User, ...] = tuple(winners) self.additional_chat_count: Optional[int] = additional_chat_count self.premium_subscription_month_count: Optional[int] = premium_subscription_month_count self.unclaimed_prize_count: Optional[int] = unclaimed_prize_count diff --git a/telegram/_inline/inlinekeyboardmarkup.py b/telegram/_inline/inlinekeyboardmarkup.py index 6857e4d8e..406688f2d 100644 --- a/telegram/_inline/inlinekeyboardmarkup.py +++ b/telegram/_inline/inlinekeyboardmarkup.py @@ -17,7 +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 InlineKeyboardMarkup.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton from telegram._telegramobject import TelegramObject @@ -57,7 +58,7 @@ class InlineKeyboardMarkup(TelegramObject): |sequenceclassargs| Attributes: - inline_keyboard (Tuple[Tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of + inline_keyboard (tuple[tuple[:class:`telegram.InlineKeyboardButton`]]): Tuple of button rows, each represented by a tuple of :class:`~telegram.InlineKeyboardButton` objects. @@ -81,7 +82,7 @@ class InlineKeyboardMarkup(TelegramObject): "InlineKeyboardButtons" ) # Required - self.inline_keyboard: Tuple[Tuple[InlineKeyboardButton, ...], ...] = tuple( + self.inline_keyboard: tuple[tuple[InlineKeyboardButton, ...], ...] = tuple( tuple(row) for row in inline_keyboard ) diff --git a/telegram/_inline/inlinequery.py b/telegram/_inline/inlinequery.py index ba29a8646..f6a94e8f4 100644 --- a/telegram/_inline/inlinequery.py +++ b/telegram/_inline/inlinequery.py @@ -19,7 +19,8 @@ # 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, Callable, Final, Optional, Sequence, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Callable, Final, Optional, Union from telegram import constants from telegram._files.location import Location diff --git a/telegram/_inline/inlinequeryresultaudio.py b/telegram/_inline/inlinequeryresultaudio.py index 69353967a..0b2b822b6 100644 --- a/telegram/_inline/inlinequeryresultaudio.py +++ b/telegram/_inline/inlinequeryresultaudio.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultAudio.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -73,7 +74,7 @@ class InlineQueryResultAudio(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -124,6 +125,6 @@ class InlineQueryResultAudio(InlineQueryResult): self.audio_duration: Optional[int] = audio_duration self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedaudio.py b/telegram/_inline/inlinequeryresultcachedaudio.py index 2fb7cdbb5..933a2b85b 100644 --- a/telegram/_inline/inlinequeryresultcachedaudio.py +++ b/telegram/_inline/inlinequeryresultcachedaudio.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultCachedAudio.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -68,7 +69,7 @@ class InlineQueryResultCachedAudio(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -110,6 +111,6 @@ class InlineQueryResultCachedAudio(InlineQueryResult): # Optionals self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/telegram/_inline/inlinequeryresultcacheddocument.py b/telegram/_inline/inlinequeryresultcacheddocument.py index b5416c274..0ef4e1993 100644 --- a/telegram/_inline/inlinequeryresultcacheddocument.py +++ b/telegram/_inline/inlinequeryresultcacheddocument.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultCachedDocument.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -72,7 +73,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -120,6 +121,6 @@ class InlineQueryResultCachedDocument(InlineQueryResult): self.description: Optional[str] = description self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/telegram/_inline/inlinequeryresultcachedgif.py b/telegram/_inline/inlinequeryresultcachedgif.py index 9f52347a0..c621a814e 100644 --- a/telegram/_inline/inlinequeryresultcachedgif.py +++ b/telegram/_inline/inlinequeryresultcachedgif.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultCachedGif.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -74,7 +75,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -124,7 +125,7 @@ class InlineQueryResultCachedGif(InlineQueryResult): self.title: Optional[str] = title self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py index f750f4df8..fa5be7484 100644 --- a/telegram/_inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultcachedmpeg4gif.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultMpeg4Gif.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -74,7 +75,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -124,7 +125,7 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): self.title: Optional[str] = title self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedphoto.py b/telegram/_inline/inlinequeryresultcachedphoto.py index 75f292d2e..06914934f 100644 --- a/telegram/_inline/inlinequeryresultcachedphoto.py +++ b/telegram/_inline/inlinequeryresultcachedphoto.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultPhoto""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -76,7 +77,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -129,7 +130,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): self.description: Optional[str] = description self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedvideo.py b/telegram/_inline/inlinequeryresultcachedvideo.py index 99a58eebb..a341114d7 100644 --- a/telegram/_inline/inlinequeryresultcachedvideo.py +++ b/telegram/_inline/inlinequeryresultcachedvideo.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultCachedVideo.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -72,7 +73,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -125,7 +126,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): self.description: Optional[str] = description self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultcachedvoice.py b/telegram/_inline/inlinequeryresultcachedvoice.py index dc8bd2ad3..c830264ed 100644 --- a/telegram/_inline/inlinequeryresultcachedvoice.py +++ b/telegram/_inline/inlinequeryresultcachedvoice.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultCachedVoice.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -70,7 +71,7 @@ class InlineQueryResultCachedVoice(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 @@ -115,6 +116,6 @@ class InlineQueryResultCachedVoice(InlineQueryResult): # Optionals self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/telegram/_inline/inlinequeryresultdocument.py b/telegram/_inline/inlinequeryresultdocument.py index e0380440b..aef409ca0 100644 --- a/telegram/_inline/inlinequeryresultdocument.py +++ b/telegram/_inline/inlinequeryresultdocument.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultDocument""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -86,7 +87,7 @@ class InlineQueryResultDocument(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -155,7 +156,7 @@ class InlineQueryResultDocument(InlineQueryResult): # Optionals self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.description: Optional[str] = description self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/telegram/_inline/inlinequeryresultgif.py b/telegram/_inline/inlinequeryresultgif.py index e5694e4f8..f95aec09c 100644 --- a/telegram/_inline/inlinequeryresultgif.py +++ b/telegram/_inline/inlinequeryresultgif.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultGif.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -102,7 +103,7 @@ class InlineQueryResultGif(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -166,7 +167,7 @@ class InlineQueryResultGif(InlineQueryResult): self.title: Optional[str] = title self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type diff --git a/telegram/_inline/inlinequeryresultmpeg4gif.py b/telegram/_inline/inlinequeryresultmpeg4gif.py index 9e27ab949..43b8ae161 100644 --- a/telegram/_inline/inlinequeryresultmpeg4gif.py +++ b/telegram/_inline/inlinequeryresultmpeg4gif.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultMpeg4Gif.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -104,7 +105,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |caption_entities| .. versionchanged:: 20.0 @@ -168,7 +169,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): self.title: Optional[str] = title self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.thumbnail_mime_type: Optional[str] = thumbnail_mime_type diff --git a/telegram/_inline/inlinequeryresultphoto.py b/telegram/_inline/inlinequeryresultphoto.py index b74adf218..ce5a9ab86 100644 --- a/telegram/_inline/inlinequeryresultphoto.py +++ b/telegram/_inline/inlinequeryresultphoto.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultPhoto.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -92,7 +93,7 @@ class InlineQueryResultPhoto(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -154,7 +155,7 @@ class InlineQueryResultPhoto(InlineQueryResult): self.description: Optional[str] = description self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content self.show_caption_above_media: Optional[bool] = show_caption_above_media diff --git a/telegram/_inline/inlinequeryresultvideo.py b/telegram/_inline/inlinequeryresultvideo.py index bb01c1ac1..ce21da455 100644 --- a/telegram/_inline/inlinequeryresultvideo.py +++ b/telegram/_inline/inlinequeryresultvideo.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultVideo.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -99,7 +100,7 @@ class InlineQueryResultVideo(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -171,7 +172,7 @@ class InlineQueryResultVideo(InlineQueryResult): # Optional self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.video_width: Optional[int] = video_width self.video_height: Optional[int] = video_height self.video_duration: Optional[int] = video_duration diff --git a/telegram/_inline/inlinequeryresultvoice.py b/telegram/_inline/inlinequeryresultvoice.py index d33f31b34..de196498f 100644 --- a/telegram/_inline/inlinequeryresultvoice.py +++ b/telegram/_inline/inlinequeryresultvoice.py @@ -17,7 +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 the classes that represent Telegram InlineQueryResultVoice.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inlinekeyboardmarkup import InlineKeyboardMarkup from telegram._inline.inlinequeryresult import InlineQueryResult @@ -72,7 +73,7 @@ class InlineQueryResultVoice(InlineQueryResult): 0-:tg-const:`telegram.constants.MessageLimit.CAPTION_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - caption_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + caption_entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -121,6 +122,6 @@ class InlineQueryResultVoice(InlineQueryResult): self.voice_duration: Optional[int] = voice_duration self.caption: Optional[str] = caption self.parse_mode: ODVInput[str] = parse_mode - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.reply_markup: Optional[InlineKeyboardMarkup] = reply_markup self.input_message_content: Optional[InputMessageContent] = input_message_content diff --git a/telegram/_inline/inputinvoicemessagecontent.py b/telegram/_inline/inputinvoicemessagecontent.py index 101e0184b..2ab896c8a 100644 --- a/telegram/_inline/inputinvoicemessagecontent.py +++ b/telegram/_inline/inputinvoicemessagecontent.py @@ -17,7 +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 a class that represents a Telegram InputInvoiceMessageContent.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inputmessagecontent import InputMessageContent from telegram._payment.labeledprice import LabeledPrice @@ -122,7 +123,7 @@ class InputInvoiceMessageContent(InputMessageContent): currency (:obj:`str`): Three-letter ISO 4217 currency code, see more on `currencies `_. Pass ``XTR`` for payments in |tg_stars|. - prices (Tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of + prices (tuple[:class:`telegram.LabeledPrice`]): Price breakdown, a list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.). Must contain exactly one item for payments in |tg_stars|. @@ -135,7 +136,7 @@ class InputInvoiceMessageContent(InputMessageContent): `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to ``0``. Not supported for payments in |tg_stars|. - suggested_tip_amounts (Tuple[: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). 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 @@ -226,10 +227,10 @@ class InputInvoiceMessageContent(InputMessageContent): self.payload: str = payload self.provider_token: Optional[str] = provider_token self.currency: str = currency - self.prices: Tuple[LabeledPrice, ...] = parse_sequence_arg(prices) + self.prices: tuple[LabeledPrice, ...] = parse_sequence_arg(prices) # Optionals self.max_tip_amount: Optional[int] = max_tip_amount - self.suggested_tip_amounts: Tuple[int, ...] = parse_sequence_arg(suggested_tip_amounts) + self.suggested_tip_amounts: tuple[int, ...] = parse_sequence_arg(suggested_tip_amounts) self.provider_data: Optional[str] = provider_data self.photo_url: Optional[str] = photo_url self.photo_size: Optional[int] = photo_size diff --git a/telegram/_inline/inputtextmessagecontent.py b/telegram/_inline/inputtextmessagecontent.py index 475f9c5bb..09d5e597b 100644 --- a/telegram/_inline/inputtextmessagecontent.py +++ b/telegram/_inline/inputtextmessagecontent.py @@ -17,7 +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 the classes that represent Telegram InputTextMessageContent.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._inline.inputmessagecontent import InputMessageContent from telegram._messageentity import MessageEntity @@ -75,7 +76,7 @@ class InputTextMessageContent(InputMessageContent): :tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters after entities parsing. parse_mode (:obj:`str`): Optional. |parse_mode| - entities (Tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| + entities (tuple[:class:`telegram.MessageEntity`]): Optional. |captionentitiesattr| .. versionchanged:: 20.0 @@ -107,7 +108,7 @@ class InputTextMessageContent(InputMessageContent): self.message_text: str = message_text # Optionals self.parse_mode: ODVInput[str] = parse_mode - self.entities: Tuple[MessageEntity, ...] = parse_sequence_arg(entities) + self.entities: tuple[MessageEntity, ...] = parse_sequence_arg(entities) self.link_preview_options: ODVInput[LinkPreviewOptions] = parse_lpo_and_dwpp( disable_web_page_preview, link_preview_options ) diff --git a/telegram/_menubutton.py b/telegram/_menubutton.py index 50b6511b0..3df50fd3f 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, Dict, Final, Optional, Type +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._telegramobject import TelegramObject @@ -76,7 +76,7 @@ class MenuButton(TelegramObject): care of selecting the correct subclass. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to :obj:`None`, in which case shortcut methods will not be available. @@ -95,7 +95,7 @@ class MenuButton(TelegramObject): if not data and cls is MenuButton: return None - _class_mapping: Dict[str, Type[MenuButton]] = { + _class_mapping: dict[str, type[MenuButton]] = { cls.COMMANDS: MenuButtonCommands, cls.WEB_APP: MenuButtonWebApp, cls.DEFAULT: MenuButtonDefault, diff --git a/telegram/_message.py b/telegram/_message.py index 11bee5724..44490482b 100644 --- a/telegram/_message.py +++ b/telegram/_message.py @@ -21,8 +21,9 @@ import datetime import re +from collections.abc import Sequence from html import escape -from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple, TypedDict, Union +from typing import TYPE_CHECKING, Optional, TypedDict, Union from telegram._chat import Chat from telegram._chatbackground import ChatBackground @@ -629,7 +630,7 @@ class Message(MaybeInaccessibleMessage): message belongs to. text (:obj:`str`): Optional. For text messages, the actual UTF-8 text of the message, 0-:tg-const:`telegram.constants.MessageLimit.MAX_TEXT_LENGTH` characters. - entities (Tuple[: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. @@ -648,7 +649,7 @@ class Message(MaybeInaccessibleMessage): ..versionadded:: 21.3 - caption_entities (Tuple[: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 @@ -675,7 +676,7 @@ class Message(MaybeInaccessibleMessage): .. seealso:: :wiki:`Working with Files and Media ` game (:class:`telegram.Game`): Optional. Message is a game, information about the game. :ref:`More about games >> `. - photo (Tuple[: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. .. seealso:: :wiki:`Working with Files and Media ` @@ -703,7 +704,7 @@ class Message(MaybeInaccessibleMessage): about the video message. .. seealso:: :wiki:`Working with Files and Media ` - new_chat_members (Tuple[: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. @@ -722,7 +723,7 @@ class Message(MaybeInaccessibleMessage): 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 (Tuple[: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 @@ -1118,12 +1119,12 @@ class Message(MaybeInaccessibleMessage): self.edit_date: Optional[datetime.datetime] = edit_date self.has_protected_content: Optional[bool] = has_protected_content self.text: Optional[str] = text - self.entities: Tuple[MessageEntity, ...] = parse_sequence_arg(entities) - self.caption_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) + self.entities: tuple[MessageEntity, ...] = parse_sequence_arg(entities) + self.caption_entities: tuple[MessageEntity, ...] = parse_sequence_arg(caption_entities) self.audio: Optional[Audio] = audio self.game: Optional[Game] = game self.document: Optional[Document] = document - self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo) + self.photo: tuple[PhotoSize, ...] = parse_sequence_arg(photo) self.sticker: Optional[Sticker] = sticker self.video: Optional[Video] = video self.voice: Optional[Voice] = voice @@ -1132,10 +1133,10 @@ class Message(MaybeInaccessibleMessage): self.contact: Optional[Contact] = contact self.location: Optional[Location] = location self.venue: Optional[Venue] = venue - self.new_chat_members: Tuple[User, ...] = parse_sequence_arg(new_chat_members) + self.new_chat_members: tuple[User, ...] = parse_sequence_arg(new_chat_members) self.left_chat_member: Optional[User] = left_chat_member self.new_chat_title: Optional[str] = new_chat_title - self.new_chat_photo: Tuple[PhotoSize, ...] = parse_sequence_arg(new_chat_photo) + self.new_chat_photo: tuple[PhotoSize, ...] = parse_sequence_arg(new_chat_photo) self.delete_chat_photo: Optional[bool] = bool(delete_chat_photo) self.group_chat_created: Optional[bool] = bool(group_chat_created) self.supergroup_chat_created: Optional[bool] = bool(supergroup_chat_created) @@ -1406,7 +1407,7 @@ class Message(MaybeInaccessibleMessage): * :class:`telegram.Invoice` * :class:`telegram.Location` * :class:`telegram.PassportData` - * List[:class:`telegram.PhotoSize`] + * list[:class:`telegram.PhotoSize`] * :class:`telegram.PaidMediaInfo` * :class:`telegram.Poll` * :class:`telegram.Sticker` @@ -1478,7 +1479,7 @@ class Message(MaybeInaccessibleMessage): def compute_quote_position_and_entities( self, quote: str, index: Optional[int] = None - ) -> Tuple[int, Optional[Tuple[MessageEntity, ...]]]: + ) -> tuple[int, Optional[tuple[MessageEntity, ...]]]: """ Use this function to compute position and entities of a quote in the message text or caption. Useful for filling the parameters @@ -1504,7 +1505,7 @@ class Message(MaybeInaccessibleMessage): message. If not specified, the first occurrence is used. Returns: - Tuple[:obj:`int`, :obj:`None` | Tuple[:class:`~telegram.MessageEntity`, ...]]: On + tuple[:obj:`int`, :obj:`None` | tuple[:class:`~telegram.MessageEntity`, ...]]: On success, a tuple containing information about quote position and entities is returned. Raises: @@ -1647,7 +1648,7 @@ class Message(MaybeInaccessibleMessage): quote: Optional[bool], reply_to_message_id: Optional[int], reply_parameters: Optional["ReplyParameters"], - ) -> Tuple[Union[str, int], ReplyParameters]: + ) -> tuple[Union[str, int], ReplyParameters]: if quote and do_quote: raise ValueError("The arguments `quote` and `do_quote` are mutually exclusive") @@ -2048,7 +2049,7 @@ class Message(MaybeInaccessibleMessage): caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, - ) -> Tuple["Message", ...]: + ) -> tuple["Message", ...]: """Shortcut for:: await bot.send_media_group( @@ -2075,7 +2076,7 @@ class Message(MaybeInaccessibleMessage): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.Message`]: An array of the sent Messages. + tuple[:class:`telegram.Message`]: An array of the sent Messages. Raises: :class:`telegram.error.TelegramError` @@ -3989,7 +3990,7 @@ class Message(MaybeInaccessibleMessage): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["GameHighScore", ...]: + ) -> tuple["GameHighScore", ...]: """Shortcut for:: await bot.get_game_high_scores( @@ -4005,7 +4006,7 @@ class Message(MaybeInaccessibleMessage): behaviour is undocumented and might be changed by Telegram. Returns: - Tuple[:class:`telegram.GameHighScore`] + tuple[:class:`telegram.GameHighScore`] """ return await self.get_bot().get_game_high_scores( chat_id=self.chat_id, @@ -4431,7 +4432,7 @@ class Message(MaybeInaccessibleMessage): return parse_message_entity(self.caption, entity) - def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]: + def parse_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities from this message filtered by their @@ -4444,21 +4445,21 @@ class Message(MaybeInaccessibleMessage): See :attr:`parse_entity` for more info. Args: - types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as + types (list[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as strings. If the ``type`` attribute of an entity is contained in this list, it will be returned. Defaults to a list of all types. All types can be found as constants in :class:`telegram.MessageEntity`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. """ return parse_message_entities(self.text, self.entities, types=types) def parse_caption_entities( - self, types: Optional[List[str]] = None - ) -> Dict[MessageEntity, str]: + self, types: Optional[list[str]] = None + ) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities from this message's caption filtered by their @@ -4471,13 +4472,13 @@ class Message(MaybeInaccessibleMessage): codepoints. See :attr:`parse_entity` for more info. Args: - types (List[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as + types (list[:obj:`str`], optional): List of :class:`telegram.MessageEntity` types as strings. If the ``type`` attribute of an entity is contained in this list, it will be returned. Defaults to a list of all types. All types can be found as constants in :class:`telegram.MessageEntity`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. """ @@ -4487,7 +4488,7 @@ class Message(MaybeInaccessibleMessage): def _parse_html( cls, message_text: Optional[str], - entities: Dict[MessageEntity, str], + entities: dict[MessageEntity, str], urled: bool = False, offset: int = 0, ) -> Optional[str]: @@ -4676,7 +4677,7 @@ class Message(MaybeInaccessibleMessage): def _parse_markdown( cls, message_text: Optional[str], - entities: Dict[MessageEntity, str], + entities: dict[MessageEntity, str], urled: bool = False, version: MarkdownVersion = 1, offset: int = 0, diff --git a/telegram/_messageentity.py b/telegram/_messageentity.py index ae675e8e9..4b076d5e5 100644 --- a/telegram/_messageentity.py +++ b/telegram/_messageentity.py @@ -20,7 +20,8 @@ import copy import itertools -from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Optional, Union from telegram import constants from telegram._telegramobject import TelegramObject @@ -200,7 +201,7 @@ class MessageEntity(TelegramObject): accumulated_length = 0 # calculate the length of each slice text[:position] in utf-16 accordingly, # store the position translations - position_translation: Dict[int, int] = {} + position_translation: dict[int, int] = {} for i, position in enumerate(positions): last_position = positions[i - 1] if i > 0 else 0 text_slice = text[last_position:position] @@ -286,8 +287,8 @@ class MessageEntity(TelegramObject): @classmethod def concatenate( cls, - *args: Union[Tuple[str, _SEM], Tuple[str, _SEM, bool]], - ) -> Tuple[str, _SEM]: + *args: Union[tuple[str, _SEM], tuple[str, _SEM, bool]], + ) -> tuple[str, _SEM]: """Utility functionality for concatenating two text along with their formatting entities. Tip: @@ -332,8 +333,8 @@ class MessageEntity(TelegramObject): .. versionadded:: 21.5 Args: - *args (Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]] | \ - Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`], :obj:`bool`]): + *args (tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]] | \ + tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`], :obj:`bool`]): Arbitrary number of tuples containing the text and its entities to concatenate. If the last element of the tuple is a :obj:`bool`, it is used to determine whether to adjust the entities to UTF-16 via @@ -341,11 +342,11 @@ class MessageEntity(TelegramObject): default. Returns: - Tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text + tuple[:obj:`str`, Sequence[:class:`telegram.MessageEntity`]]: The concatenated text and its entities """ output_text = "" - output_entities: List[MessageEntity] = [] + output_entities: list[MessageEntity] = [] for arg in args: text, entities = arg[0], arg[1] @@ -357,8 +358,8 @@ class MessageEntity(TelegramObject): return output_text, output_entities - ALL_TYPES: Final[List[str]] = list(constants.MessageEntityType) - """List[:obj:`str`]: A list of all available message entity types.""" + ALL_TYPES: Final[list[str]] = list(constants.MessageEntityType) + """list[:obj:`str`]: A list of all available message entity types.""" BLOCKQUOTE: Final[str] = constants.MessageEntityType.BLOCKQUOTE """:const:`telegram.constants.MessageEntityType.BLOCKQUOTE` diff --git a/telegram/_messageorigin.py b/telegram/_messageorigin.py index 534583adb..37d80be41 100644 --- a/telegram/_messageorigin.py +++ b/telegram/_messageorigin.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 MessageOigin.""" import datetime -from typing import TYPE_CHECKING, Dict, Final, Optional, Type +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._chat import Chat @@ -105,7 +105,7 @@ class MessageOrigin(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[MessageOrigin]] = { + _class_mapping: dict[str, type[MessageOrigin]] = { cls.USER: MessageOriginUser, cls.HIDDEN_USER: MessageOriginHiddenUser, cls.CHAT: MessageOriginChat, diff --git a/telegram/_messagereactionupdated.py b/telegram/_messagereactionupdated.py index d4d4033a6..a1e28c2bc 100644 --- a/telegram/_messagereactionupdated.py +++ b/telegram/_messagereactionupdated.py @@ -17,8 +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 MessageReaction Update.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from typing import TYPE_CHECKING, Optional from telegram._chat import Chat from telegram._reaction import ReactionCount, ReactionType @@ -54,7 +55,7 @@ class MessageReactionCountUpdated(TelegramObject): message_id (:obj:`int`): Unique message identifier inside the chat. date (:class:`datetime.datetime`): Date of the change in Unix time |datetime_localization| - reactions (Tuple[:class:`telegram.ReactionCount`]): List of reactions that are present on + reactions (tuple[:class:`telegram.ReactionCount`]): List of reactions that are present on the message """ @@ -79,7 +80,7 @@ class MessageReactionCountUpdated(TelegramObject): self.chat: Chat = chat self.message_id: int = message_id self.date: datetime = date - self.reactions: Tuple[ReactionCount, ...] = parse_sequence_arg(reactions) + self.reactions: tuple[ReactionCount, ...] = parse_sequence_arg(reactions) self._id_attrs = (self.chat, self.message_id, self.date, self.reactions) self._freeze() @@ -132,9 +133,9 @@ class MessageReactionUpdated(TelegramObject): message_id (:obj:`int`): Unique message identifier inside the chat. date (:class:`datetime.datetime`): Date of the change in Unix time. |datetime_localization| - old_reaction (Tuple[:class:`telegram.ReactionType`]): Previous list of reaction types + old_reaction (tuple[:class:`telegram.ReactionType`]): Previous list of reaction types that were set by the user. - new_reaction (Tuple[:class:`telegram.ReactionType`]): New list of reaction types that + new_reaction (tuple[:class:`telegram.ReactionType`]): New list of reaction types that were set by the user. user (:class:`telegram.User`): Optional. The user that changed the reaction, if the user isn't anonymous. @@ -169,8 +170,8 @@ class MessageReactionUpdated(TelegramObject): self.chat: Chat = chat self.message_id: int = message_id self.date: datetime = date - self.old_reaction: Tuple[ReactionType, ...] = parse_sequence_arg(old_reaction) - self.new_reaction: Tuple[ReactionType, ...] = parse_sequence_arg(new_reaction) + self.old_reaction: tuple[ReactionType, ...] = parse_sequence_arg(old_reaction) + self.new_reaction: tuple[ReactionType, ...] = parse_sequence_arg(new_reaction) # Optional self.user: Optional[User] = user diff --git a/telegram/_paidmedia.py b/telegram/_paidmedia.py index 1c2cc4091..c3ab1e22e 100644 --- a/telegram/_paidmedia.py +++ b/telegram/_paidmedia.py @@ -18,7 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects that represent paid media in Telegram.""" -from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._files.photosize import PhotoSize @@ -81,7 +82,7 @@ class PaidMedia(TelegramObject): care of selecting the correct subclass. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot`, optional): The bot associated with this object. Returns: @@ -96,7 +97,7 @@ class PaidMedia(TelegramObject): if not data and cls is PaidMedia: return None - _class_mapping: Dict[str, Type[PaidMedia]] = { + _class_mapping: dict[str, type[PaidMedia]] = { cls.PREVIEW: PaidMediaPreview, cls.PHOTO: PaidMediaPhoto, cls.VIDEO: PaidMediaVideo, @@ -165,7 +166,7 @@ class PaidMediaPhoto(PaidMedia): Attributes: type (:obj:`str`): Type of the paid media, always :tg-const:`telegram.PaidMedia.PHOTO`. - photo (Tuple[:class:`telegram.PhotoSize`]): The photo. + photo (tuple[:class:`telegram.PhotoSize`]): The photo. """ __slots__ = ("photo",) @@ -179,7 +180,7 @@ class PaidMediaPhoto(PaidMedia): super().__init__(type=PaidMedia.PHOTO, api_kwargs=api_kwargs) with self._unfrozen(): - self.photo: Tuple[PhotoSize, ...] = parse_sequence_arg(photo) + self.photo: tuple[PhotoSize, ...] = parse_sequence_arg(photo) self._id_attrs = (self.type, self.photo) @@ -259,7 +260,7 @@ class PaidMediaInfo(TelegramObject): Attributes: star_count (:obj:`int`): The number of Telegram Stars that must be paid to buy access to the media. - paid_media (Tuple[:class:`telegram.PaidMedia`]): Information about the paid media. + paid_media (tuple[:class:`telegram.PaidMedia`]): Information about the paid media. """ __slots__ = ("paid_media", "star_count") @@ -273,7 +274,7 @@ class PaidMediaInfo(TelegramObject): ) -> None: super().__init__(api_kwargs=api_kwargs) self.star_count: int = star_count - self.paid_media: Tuple[PaidMedia, ...] = parse_sequence_arg(paid_media) + self.paid_media: tuple[PaidMedia, ...] = parse_sequence_arg(paid_media) self._id_attrs = (self.star_count, self.paid_media) self._freeze() diff --git a/telegram/_passport/credentials.py b/telegram/_passport/credentials.py index 7345991a5..17e44595a 100644 --- a/telegram/_passport/credentials.py +++ b/telegram/_passport/credentials.py @@ -19,7 +19,8 @@ # pylint: disable=missing-module-docstring, redefined-builtin import json from base64 import b64decode -from typing import TYPE_CHECKING, Optional, Sequence, Tuple, no_type_check +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional, no_type_check try: from cryptography.hazmat.backends import default_backend @@ -390,11 +391,11 @@ 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 (list[: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 + files (list[:class:`telegram.FileCredentials`], optional): Credentials for encrypted files. Available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. @@ -410,7 +411,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 (Tuple[: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". @@ -418,7 +419,7 @@ class SecureValue(TelegramObject): .. versionchanged:: 20.0 |tupleclassattrs| - files (Tuple[: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. @@ -447,8 +448,8 @@ class SecureValue(TelegramObject): self.front_side: Optional[FileCredentials] = front_side self.reverse_side: Optional[FileCredentials] = reverse_side self.selfie: Optional[FileCredentials] = selfie - self.files: Tuple[FileCredentials, ...] = parse_sequence_arg(files) - self.translation: Tuple[FileCredentials, ...] = parse_sequence_arg(translation) + self.files: tuple[FileCredentials, ...] = parse_sequence_arg(files) + self.translation: tuple[FileCredentials, ...] = parse_sequence_arg(translation) self._freeze() diff --git a/telegram/_passport/encryptedpassportelement.py b/telegram/_passport/encryptedpassportelement.py index 9f16d81e0..5bb764c9f 100644 --- a/telegram/_passport/encryptedpassportelement.py +++ b/telegram/_passport/encryptedpassportelement.py @@ -18,7 +18,8 @@ # 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, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional, Union from telegram._passport.credentials import decrypt_json from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress @@ -100,7 +101,7 @@ class EncryptedPassportElement(TelegramObject): "phone_number" type. email (:obj:`str`): Optional. User's verified email address; available only for "email" type. - files (Tuple[: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 only for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. @@ -119,7 +120,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 if requested for "passport", "driver_license", "identity_card" and "internal_passport". - translation (Tuple[: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", "utility_bill", "bank_statement", "rental_agreement", @@ -172,11 +173,11 @@ class EncryptedPassportElement(TelegramObject): self.data: Optional[Union[PersonalDetails, IdDocumentData, ResidentialAddress]] = data self.phone_number: Optional[str] = phone_number self.email: Optional[str] = email - self.files: Tuple[PassportFile, ...] = parse_sequence_arg(files) + self.files: tuple[PassportFile, ...] = parse_sequence_arg(files) self.front_side: Optional[PassportFile] = front_side self.reverse_side: Optional[PassportFile] = reverse_side self.selfie: Optional[PassportFile] = selfie - self.translation: Tuple[PassportFile, ...] = parse_sequence_arg(translation) + self.translation: tuple[PassportFile, ...] = parse_sequence_arg(translation) self.hash: str = hash self._id_attrs = ( @@ -218,7 +219,7 @@ class EncryptedPassportElement(TelegramObject): passport credentials. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot` | :obj:`None`): The bot associated with these object. May be :obj:`None`, in which case shortcut methods will not be available. diff --git a/telegram/_passport/passportdata.py b/telegram/_passport/passportdata.py index 32e3879bc..8b4db028a 100644 --- a/telegram/_passport/passportdata.py +++ b/telegram/_passport/passportdata.py @@ -17,7 +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/]. """Contains information about Telegram Passport data shared with the bot by the user.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._passport.credentials import EncryptedCredentials from telegram._passport.encryptedpassportelement import EncryptedPassportElement @@ -49,7 +50,7 @@ class PassportData(TelegramObject): credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials. Attributes: - data (Tuple[: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. @@ -72,10 +73,10 @@ class PassportData(TelegramObject): ): super().__init__(api_kwargs=api_kwargs) - self.data: Tuple[EncryptedPassportElement, ...] = parse_sequence_arg(data) + self.data: tuple[EncryptedPassportElement, ...] = parse_sequence_arg(data) self.credentials: EncryptedCredentials = credentials - self._decrypted_data: Optional[Tuple[EncryptedPassportElement]] = None + self._decrypted_data: Optional[tuple[EncryptedPassportElement]] = None self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) self._freeze() @@ -96,9 +97,9 @@ class PassportData(TelegramObject): 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 + 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 diff --git a/telegram/_passport/passportelementerrors.py b/telegram/_passport/passportelementerrors.py index 8d6911439..097d60856 100644 --- a/telegram/_passport/passportelementerrors.py +++ b/telegram/_passport/passportelementerrors.py @@ -19,7 +19,7 @@ # pylint: disable=redefined-builtin """This module contains the classes that represent Telegram PassportElementError.""" -from typing import List, Optional +from typing import Optional from telegram._telegramobject import TelegramObject from telegram._utils.types import JSONDict @@ -168,7 +168,7 @@ class PassportElementErrorFiles(PassportElementError): 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. + file_hashes (list[:obj:`str`]): List of base64-encoded file hashes. message (:obj:`str`): Error message. Attributes: @@ -184,7 +184,7 @@ class PassportElementErrorFiles(PassportElementError): def __init__( self, type: str, - file_hashes: List[str], + file_hashes: list[str], message: str, *, api_kwargs: Optional[JSONDict] = None, @@ -192,7 +192,7 @@ class PassportElementErrorFiles(PassportElementError): # Required super().__init__("files", type, message, api_kwargs=api_kwargs) with self._unfrozen(): - self._file_hashes: List[str] = file_hashes + self._file_hashes: list[str] = file_hashes self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes)) @@ -203,7 +203,7 @@ class PassportElementErrorFiles(PassportElementError): return data @property - def file_hashes(self) -> List[str]: + def file_hashes(self) -> list[str]: """List of base64-encoded file hashes. .. deprecated:: 20.6 @@ -386,7 +386,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): one of ``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``. - file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. + file_hashes (list[:obj:`str`]): List of base64-encoded file hashes. message (:obj:`str`): Error message. Attributes: @@ -403,7 +403,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): def __init__( self, type: str, - file_hashes: List[str], + file_hashes: list[str], message: str, *, api_kwargs: Optional[JSONDict] = None, @@ -411,7 +411,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): # Required super().__init__("translation_files", type, message, api_kwargs=api_kwargs) with self._unfrozen(): - self._file_hashes: List[str] = file_hashes + self._file_hashes: list[str] = file_hashes self._id_attrs = (self.source, self.type, self.message, *tuple(file_hashes)) @@ -422,7 +422,7 @@ class PassportElementErrorTranslationFiles(PassportElementError): return data @property - def file_hashes(self) -> List[str]: + def file_hashes(self) -> list[str]: """List of base64-encoded file hashes. .. deprecated:: 20.6 diff --git a/telegram/_passport/passportfile.py b/telegram/_passport/passportfile.py index 84a1ce201..e023457f6 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, Tuple +from typing import TYPE_CHECKING, Optional from telegram._telegramobject import TelegramObject from telegram._utils.defaultvalue import DEFAULT_NONE @@ -124,7 +124,7 @@ class PassportFile(TelegramObject): passport credentials. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot` | :obj:`None`): The bot associated with these object. May be :obj:`None`, in which case shortcut methods will not be available. @@ -151,10 +151,10 @@ class PassportFile(TelegramObject): @classmethod def de_list_decrypted( cls, - data: Optional[List[JSONDict]], + data: Optional[list[JSONDict]], bot: Optional["Bot"], - credentials: List["FileCredentials"], - ) -> Tuple[Optional["PassportFile"], ...]: + credentials: list["FileCredentials"], + ) -> tuple[Optional["PassportFile"], ...]: """Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account passport credentials. @@ -164,7 +164,7 @@ class PassportFile(TelegramObject): * Filters out any :obj:`None` values Args: - data (List[Dict[:obj:`str`, ...]]): The JSON data. + data (list[dict[:obj:`str`, ...]]): The JSON data. bot (:class:`telegram.Bot` | :obj:`None`): The bot associated with these object. May be :obj:`None`, in which case shortcut methods will not be available. @@ -176,7 +176,7 @@ class PassportFile(TelegramObject): credentials (:class:`telegram.FileCredentials`): The credentials Returns: - Tuple[:class:`telegram.PassportFile`]: + tuple[:class:`telegram.PassportFile`]: """ if not data: diff --git a/telegram/_payment/shippingoption.py b/telegram/_payment/shippingoption.py index 15047a00b..b41af9879 100644 --- a/telegram/_payment/shippingoption.py +++ b/telegram/_payment/shippingoption.py @@ -17,7 +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 ShippingOption.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._telegramobject import TelegramObject from telegram._utils.argumentparsing import parse_sequence_arg @@ -47,7 +48,7 @@ class ShippingOption(TelegramObject): Attributes: id (:obj:`str`): Shipping option identifier. title (:obj:`str`): Option title. - prices (Tuple[:class:`telegram.LabeledPrice`]): List of price portions. + prices (tuple[:class:`telegram.LabeledPrice`]): List of price portions. .. versionchanged:: 20.0 |tupleclassattrs| @@ -68,7 +69,7 @@ class ShippingOption(TelegramObject): self.id: str = id self.title: str = title - self.prices: Tuple[LabeledPrice, ...] = parse_sequence_arg(prices) + self.prices: tuple[LabeledPrice, ...] = parse_sequence_arg(prices) self._id_attrs = (self.id,) diff --git a/telegram/_payment/shippingquery.py b/telegram/_payment/shippingquery.py index cf81b4ecf..24b4e0a66 100644 --- a/telegram/_payment/shippingquery.py +++ b/telegram/_payment/shippingquery.py @@ -18,7 +18,8 @@ # 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, Optional, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._payment.shippingaddress import ShippingAddress from telegram._telegramobject import TelegramObject diff --git a/telegram/_payment/stars.py b/telegram/_payment/stars.py index dfeb832e2..32678915a 100644 --- a/telegram/_payment/stars.py +++ b/telegram/_payment/stars.py @@ -19,8 +19,9 @@ # pylint: disable=redefined-builtin """This module contains the classes for Telegram Stars transactions.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Dict, Final, Optional, Sequence, Tuple, Type +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._paidmedia import PaidMedia @@ -79,7 +80,7 @@ class RevenueWithdrawalState(TelegramObject): care of selecting the correct subclass. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot`): The bot associated with this object. Returns: @@ -91,7 +92,7 @@ class RevenueWithdrawalState(TelegramObject): if not data: return None - _class_mapping: Dict[str, Type[RevenueWithdrawalState]] = { + _class_mapping: dict[str, type[RevenueWithdrawalState]] = { cls.PENDING: RevenueWithdrawalStatePending, cls.SUCCEEDED: RevenueWithdrawalStateSucceeded, cls.FAILED: RevenueWithdrawalStateFailed, @@ -239,7 +240,7 @@ class TransactionPartner(TelegramObject): care of selecting the correct subclass. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot`): The bot associated with this object. Returns: @@ -254,7 +255,7 @@ class TransactionPartner(TelegramObject): if not data and cls is TransactionPartner: return None - _class_mapping: Dict[str, Type[TransactionPartner]] = { + _class_mapping: dict[str, type[TransactionPartner]] = { cls.FRAGMENT: TransactionPartnerFragment, cls.USER: TransactionPartnerUser, cls.OTHER: TransactionPartnerOther, @@ -337,7 +338,7 @@ class TransactionPartnerUser(TransactionPartner): always :tg-const:`telegram.TransactionPartner.USER`. user (:class:`telegram.User`): Information about the user. invoice_payload (:obj:`str`): Optional. Bot-specified invoice payload. - paid_media (Tuple[:class:`telegram.PaidMedia`]): Optional. Information about the paid + paid_media (tuple[:class:`telegram.PaidMedia`]): Optional. Information about the paid media bought by the user. .. versionadded:: 21.5 @@ -363,7 +364,7 @@ class TransactionPartnerUser(TransactionPartner): with self._unfrozen(): self.user: User = user self.invoice_payload: Optional[str] = invoice_payload - self.paid_media: Optional[Tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media) + self.paid_media: Optional[tuple[PaidMedia, ...]] = parse_sequence_arg(paid_media) self.paid_media_payload: Optional[str] = paid_media_payload self._id_attrs = ( self.type, @@ -516,7 +517,7 @@ class StarTransactions(TelegramObject): transactions (Sequence[:class:`telegram.StarTransaction`]): The list of transactions. Attributes: - transactions (Tuple[:class:`telegram.StarTransaction`]): The list of transactions. + transactions (tuple[:class:`telegram.StarTransaction`]): The list of transactions. """ __slots__ = ("transactions",) @@ -525,7 +526,7 @@ class StarTransactions(TelegramObject): self, transactions: Sequence[StarTransaction], *, api_kwargs: Optional[JSONDict] = None ): super().__init__(api_kwargs=api_kwargs) - self.transactions: Tuple[StarTransaction, ...] = parse_sequence_arg(transactions) + self.transactions: tuple[StarTransaction, ...] = parse_sequence_arg(transactions) self._id_attrs = (self.transactions,) self._freeze() diff --git a/telegram/_poll.py b/telegram/_poll.py index 8ea387a09..59b4032fb 100644 --- a/telegram/_poll.py +++ b/telegram/_poll.py @@ -18,7 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Poll.""" import datetime -from typing import TYPE_CHECKING, Dict, Final, List, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Final, Optional from telegram import constants from telegram._chat import Chat @@ -83,7 +84,7 @@ class InputPollOption(TelegramObject): super().__init__(api_kwargs=api_kwargs) self.text: str = text self.text_parse_mode: ODVInput[str] = text_parse_mode - self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) + self.text_entities: tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) self._id_attrs = (self.text,) @@ -127,7 +128,7 @@ class PollOption(TelegramObject): :tg-const:`telegram.PollOption.MIN_LENGTH`-:tg-const:`telegram.PollOption.MAX_LENGTH` characters. voter_count (:obj:`int`): Number of users that voted for this option. - text_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities + text_entities (tuple[:class:`telegram.MessageEntity`]): Special entities that appear in the option text. Currently, only custom emoji entities are allowed in poll option texts. This list is empty if the question does not contain entities. @@ -149,7 +150,7 @@ class PollOption(TelegramObject): super().__init__(api_kwargs=api_kwargs) self.text: str = text self.voter_count: int = voter_count - self.text_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) + self.text_entities: tuple[MessageEntity, ...] = parse_sequence_arg(text_entities) self._id_attrs = (self.text, self.voter_count) @@ -189,7 +190,7 @@ class PollOption(TelegramObject): """ return parse_message_entity(self.text, entity) - def parse_entities(self, types: Optional[List[str]] = None) -> Dict[MessageEntity, str]: + def parse_entities(self, types: Optional[list[str]] = None) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities from this polls question filtered by their ``type`` attribute as @@ -203,12 +204,12 @@ class PollOption(TelegramObject): .. versionadded:: 21.2 Args: - types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + types (list[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the ``type`` attribute of an entity is contained in this list, it will be returned. Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. """ return parse_message_entities(self.text, self.text_entities, types) @@ -260,7 +261,7 @@ class PollAnswer(TelegramObject): Attributes: poll_id (:obj:`str`): Unique poll identifier. - option_ids (Tuple[:obj:`int`]): Identifiers of answer options, chosen by the user. May + 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 @@ -292,7 +293,7 @@ class PollAnswer(TelegramObject): super().__init__(api_kwargs=api_kwargs) self.poll_id: str = poll_id self.voter_chat: Optional[Chat] = voter_chat - self.option_ids: Tuple[int, ...] = parse_sequence_arg(option_ids) + self.option_ids: tuple[int, ...] = parse_sequence_arg(option_ids) self.user: Optional[User] = user self._id_attrs = ( @@ -374,7 +375,7 @@ class Poll(TelegramObject): id (:obj:`str`): Unique poll identifier. question (:obj:`str`): Poll question, :tg-const:`telegram.Poll.MIN_QUESTION_LENGTH`- :tg-const:`telegram.Poll.MAX_QUESTION_LENGTH` characters. - options (Tuple[:class:`~telegram.PollOption`]): List of poll options. + options (tuple[:class:`~telegram.PollOption`]): List of poll options. .. versionchanged:: 20.0 |tupleclassattrs| @@ -389,7 +390,7 @@ 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 (Tuple[: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. @@ -405,7 +406,7 @@ class Poll(TelegramObject): .. versionchanged:: 20.3 |datetime_localization| - question_entities (Tuple[:class:`telegram.MessageEntity`]): Special entities + question_entities (tuple[:class:`telegram.MessageEntity`]): Special entities that appear in the :attr:`question`. Currently, only custom emoji entities are allowed in poll questions. This list is empty if the question does not contain entities. @@ -453,7 +454,7 @@ class Poll(TelegramObject): super().__init__(api_kwargs=api_kwargs) self.id: str = id self.question: str = question - self.options: Tuple[PollOption, ...] = parse_sequence_arg(options) + self.options: tuple[PollOption, ...] = parse_sequence_arg(options) self.total_voter_count: int = total_voter_count self.is_closed: bool = is_closed self.is_anonymous: bool = is_anonymous @@ -461,12 +462,12 @@ class Poll(TelegramObject): self.allows_multiple_answers: bool = allows_multiple_answers self.correct_option_id: Optional[int] = correct_option_id self.explanation: Optional[str] = explanation - self.explanation_entities: Tuple[MessageEntity, ...] = parse_sequence_arg( + self.explanation_entities: tuple[MessageEntity, ...] = parse_sequence_arg( explanation_entities ) self.open_period: Optional[int] = open_period self.close_date: Optional[datetime.datetime] = close_date - self.question_entities: Tuple[MessageEntity, ...] = parse_sequence_arg(question_entities) + self.question_entities: tuple[MessageEntity, ...] = parse_sequence_arg(question_entities) self._id_attrs = (self.id,) @@ -516,8 +517,8 @@ class Poll(TelegramObject): return parse_message_entity(self.explanation, entity) def parse_explanation_entities( - self, types: Optional[List[str]] = None - ) -> Dict[MessageEntity, str]: + self, types: Optional[list[str]] = None + ) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities from this polls explanation filtered by their ``type`` attribute as @@ -529,12 +530,12 @@ class Poll(TelegramObject): UTF-16 codepoints. See :attr:`parse_explanation_entity` for more info. Args: - types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + types (list[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the ``type`` attribute of an entity is contained in this list, it will be returned. Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. Raises: @@ -567,8 +568,8 @@ class Poll(TelegramObject): return parse_message_entity(self.question, entity) def parse_question_entities( - self, types: Optional[List[str]] = None - ) -> Dict[MessageEntity, str]: + self, types: Optional[list[str]] = None + ) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities from this polls question filtered by their ``type`` attribute as @@ -582,12 +583,12 @@ class Poll(TelegramObject): UTF-16 codepoints. See :attr:`parse_question_entity` for more info. Args: - types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + types (list[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the ``type`` attribute of an entity is contained in this list, it will be returned. Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. """ diff --git a/telegram/_reaction.py b/telegram/_reaction.py index 90de7823d..ca0f37fb0 100644 --- a/telegram/_reaction.py +++ b/telegram/_reaction.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects that represents a Telegram ReactionType.""" -from typing import TYPE_CHECKING, Dict, Final, Literal, Optional, Type, Union +from typing import TYPE_CHECKING, Final, Literal, Optional, Union from telegram import constants from telegram._telegramobject import TelegramObject @@ -89,7 +89,7 @@ class ReactionType(TelegramObject): if not data and cls is ReactionType: return None - _class_mapping: Dict[str, Type[ReactionType]] = { + _class_mapping: dict[str, type[ReactionType]] = { cls.EMOJI: ReactionTypeEmoji, cls.CUSTOM_EMOJI: ReactionTypeCustomEmoji, cls.PAID: ReactionTypePaid, diff --git a/telegram/_reply.py b/telegram/_reply.py index 65e426657..afaa379ca 100644 --- a/telegram/_reply.py +++ b/telegram/_reply.py @@ -17,7 +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 modules contains objects that represents Telegram Replies""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional, Union from telegram._chat import Chat from telegram._dice import Dice @@ -124,7 +125,7 @@ class ExternalReplyInfo(TelegramObject): file. document (:class:`telegram.Document`): Optional. Message is a general file, information about the file. - photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available sizes + photo (tuple[:class:`telegram.PhotoSize`]): Optional. Message is a photo, available sizes of the photo. sticker (:class:`telegram.Sticker`): Optional. Message is a sticker, information about the sticker. @@ -224,7 +225,7 @@ class ExternalReplyInfo(TelegramObject): self.animation: Optional[Animation] = animation self.audio: Optional[Audio] = audio self.document: Optional[Document] = document - self.photo: Optional[Tuple[PhotoSize, ...]] = parse_sequence_arg(photo) + self.photo: Optional[tuple[PhotoSize, ...]] = parse_sequence_arg(photo) self.sticker: Optional[Sticker] = sticker self.story: Optional[Story] = story self.video: Optional[Video] = video @@ -311,7 +312,7 @@ class TextQuote(TelegramObject): message. position (:obj:`int`): Approximate quote position in the original message in UTF-16 code units as specified by the sender. - entities (Tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear + entities (tuple[:class:`telegram.MessageEntity`]): Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes. is_manual (:obj:`bool`): Optional. :obj:`True`, if the quote was chosen manually by the @@ -338,7 +339,7 @@ class TextQuote(TelegramObject): self.text: str = text self.position: int = position - self.entities: Optional[Tuple[MessageEntity, ...]] = parse_sequence_arg(entities) + self.entities: Optional[tuple[MessageEntity, ...]] = parse_sequence_arg(entities) self.is_manual: Optional[bool] = is_manual self._id_attrs = ( @@ -411,7 +412,7 @@ class ReplyParameters(TelegramObject): quote_parse_mode (:obj:`str`): Optional. Mode for parsing entities in the quote. See :wiki:`formatting options ` for more details. - quote_entities (Tuple[:class:`telegram.MessageEntity`]): Optional. A JSON-serialized list + quote_entities (tuple[:class:`telegram.MessageEntity`]): Optional. A JSON-serialized list of special entities that appear in the quote. It can be specified instead of :paramref:`quote_parse_mode`. quote_position (:obj:`int`): Optional. Position of the quote in the original message in @@ -447,7 +448,7 @@ class ReplyParameters(TelegramObject): self.allow_sending_without_reply: ODVInput[bool] = allow_sending_without_reply self.quote: Optional[str] = quote self.quote_parse_mode: ODVInput[str] = quote_parse_mode - self.quote_entities: Optional[Tuple[MessageEntity, ...]] = parse_sequence_arg( + self.quote_entities: Optional[tuple[MessageEntity, ...]] = parse_sequence_arg( quote_entities ) self.quote_position: Optional[int] = quote_position diff --git a/telegram/_replykeyboardmarkup.py b/telegram/_replykeyboardmarkup.py index 1b410ebc7..3abecc586 100644 --- a/telegram/_replykeyboardmarkup.py +++ b/telegram/_replykeyboardmarkup.py @@ -18,7 +18,8 @@ # 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 Final, Optional, Sequence, Tuple, Union +from collections.abc import Sequence +from typing import Final, Optional, Union from telegram import constants from telegram._keyboardbutton import KeyboardButton @@ -85,7 +86,7 @@ class ReplyKeyboardMarkup(TelegramObject): .. versionadded:: 20.0 Attributes: - keyboard (Tuple[Tuple[:class:`telegram.KeyboardButton`]]): Array of button rows, + keyboard (tuple[tuple[: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 @@ -148,7 +149,7 @@ class ReplyKeyboardMarkup(TelegramObject): ) # Required - self.keyboard: Tuple[Tuple[KeyboardButton, ...], ...] = tuple( + self.keyboard: tuple[tuple[KeyboardButton, ...], ...] = tuple( tuple(KeyboardButton(button) if isinstance(button, str) else button for button in row) for row in keyboard ) diff --git a/telegram/_shared.py b/telegram/_shared.py index b4ce2c4d5..60d8ef3b9 100644 --- a/telegram/_shared.py +++ b/telegram/_shared.py @@ -17,7 +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 two objects used for request chats/users service messages.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject @@ -59,7 +60,7 @@ class UsersShared(TelegramObject): Attributes: request_id (:obj:`int`): Identifier of the request. - users (Tuple[:class:`telegram.SharedUser`]): Information about users shared with the + users (tuple[:class:`telegram.SharedUser`]): Information about users shared with the bot. .. versionadded:: 21.1 @@ -76,7 +77,7 @@ class UsersShared(TelegramObject): ): super().__init__(api_kwargs=api_kwargs) self.request_id: int = request_id - self.users: Tuple[SharedUser, ...] = parse_sequence_arg(users) + self.users: tuple[SharedUser, ...] = parse_sequence_arg(users) self._id_attrs = (self.request_id, self.users) @@ -144,7 +145,7 @@ class ChatShared(TelegramObject): the bot and available. .. versionadded:: 21.1 - photo (Tuple[:class:`telegram.PhotoSize`]): Optional. Available sizes of the chat photo, + photo (tuple[:class:`telegram.PhotoSize`]): Optional. Available sizes of the chat photo, if the photo was requested by the bot .. versionadded:: 21.1 @@ -167,7 +168,7 @@ class ChatShared(TelegramObject): self.chat_id: int = chat_id self.title: Optional[str] = title self.username: Optional[str] = username - self.photo: Optional[Tuple[PhotoSize, ...]] = parse_sequence_arg(photo) + self.photo: Optional[tuple[PhotoSize, ...]] = parse_sequence_arg(photo) self._id_attrs = (self.request_id, self.chat_id) @@ -226,7 +227,7 @@ class SharedUser(TelegramObject): bot. username (:obj:`str`): Optional. Username of the user, if the username was requested by the bot. - photo (Tuple[:class:`telegram.PhotoSize`]): Available sizes of the chat photo, if + photo (tuple[:class:`telegram.PhotoSize`]): Available sizes of the chat photo, if the photo was requested by the bot. This list is empty if the photo was not requsted. """ @@ -247,7 +248,7 @@ class SharedUser(TelegramObject): self.first_name: Optional[str] = first_name self.last_name: Optional[str] = last_name self.username: Optional[str] = username - self.photo: Optional[Tuple[PhotoSize, ...]] = parse_sequence_arg(photo) + self.photo: Optional[tuple[PhotoSize, ...]] = parse_sequence_arg(photo) self._id_attrs = (self.user_id,) diff --git a/telegram/_telegramobject.py b/telegram/_telegramobject.py index 504075532..6f5038da4 100644 --- a/telegram/_telegramobject.py +++ b/telegram/_telegramobject.py @@ -21,27 +21,12 @@ import contextlib import datetime import inspect import json -from collections.abc import Sized +from collections.abc import Iterator, Mapping, Sized from contextlib import contextmanager from copy import deepcopy from itertools import chain from types import MappingProxyType -from typing import ( - TYPE_CHECKING, - Any, - ClassVar, - Dict, - Iterator, - List, - Mapping, - Optional, - Set, - Tuple, - Type, - TypeVar, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, ClassVar, Optional, TypeVar, Union, cast from telegram._utils.datetime import to_timestamp from telegram._utils.defaultvalue import DefaultValue @@ -80,7 +65,7 @@ class TelegramObject: :obj:`list` are now of type :obj:`tuple`. Arguments: - api_kwargs (Dict[:obj:`str`, any], optional): |toapikwargsarg| + api_kwargs (dict[:obj:`str`, any], optional): |toapikwargsarg| .. versionadded:: 20.0 @@ -95,11 +80,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: ClassVar[Set[str]] = set() + __INIT_PARAMS: ClassVar[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 + __INIT_PARAMS_CHECK: Optional[type["TelegramObject"]] = None def __init__(self, *, api_kwargs: Optional[JSONDict] = None) -> None: # Setting _frozen to `False` here means that classes without arguments still need to @@ -107,7 +92,7 @@ class TelegramObject: # `with self._unfrozen()` in the `__init__` of subclasses and we have fewer empty # classes than classes with arguments. self._frozen: bool = False - self._id_attrs: Tuple[object, ...] = () + 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: Mapping[str, Any] = MappingProxyType(api_kwargs or {}) @@ -263,7 +248,7 @@ class TelegramObject: f"`{item}`." ) from exc - def __getstate__(self) -> Dict[str, Union[str, object]]: + def __getstate__(self) -> dict[str, Union[str, object]]: """ Overrides :meth:`object.__getstate__` to customize the pickling process of objects of this type. @@ -271,7 +256,7 @@ class TelegramObject: :meth:`set_bot` (if any), as it can't be pickled. Returns: - state (Dict[:obj:`str`, :obj:`object`]): The state of the object. + state (dict[:obj:`str`, :obj:`object`]): The state of the object. """ out = self._get_attrs( include_private=True, recursive=False, remove_bot=True, convert_default_vault=False @@ -281,7 +266,7 @@ class TelegramObject: out["api_kwargs"] = dict(self.api_kwargs) return out - def __setstate__(self, state: Dict[str, object]) -> None: + def __setstate__(self, state: dict[str, object]) -> None: """ Overrides :meth:`object.__setstate__` to customize the unpickling process of objects of this type. Modifies the object in-place. @@ -305,7 +290,7 @@ class TelegramObject: self._bot = None # get api_kwargs first because we may need to add entries to it (see try-except below) - api_kwargs = cast(Dict[str, object], state.pop("api_kwargs", {})) + api_kwargs = cast(dict[str, object], state.pop("api_kwargs", {})) # get _frozen before the loop to avoid setting it to True in the loop frozen = state.pop("_frozen", False) @@ -341,7 +326,7 @@ class TelegramObject: if frozen: self._freeze() - def __deepcopy__(self: Tele_co, memodict: Dict[int, object]) -> Tele_co: + def __deepcopy__(self: Tele_co, memodict: dict[int, object]) -> Tele_co: """ Customizes how :func:`copy.deepcopy` processes objects of this type. The only difference to the default implementation is that the :class:`telegram.Bot` @@ -401,7 +386,7 @@ class TelegramObject: @classmethod def _de_json( - cls: Type[Tele_co], + cls: type[Tele_co], data: Optional[JSONDict], bot: Optional["Bot"], api_kwargs: Optional[JSONDict] = None, @@ -433,12 +418,12 @@ class TelegramObject: @classmethod def de_json( - cls: Type[Tele_co], data: Optional[JSONDict], bot: Optional["Bot"] = None + cls: type[Tele_co], data: Optional[JSONDict], bot: Optional["Bot"] = None ) -> Optional[Tele_co]: """Converts JSON data to a Telegram object. Args: - data (Dict[:obj:`str`, ...]): The JSON data. + data (dict[:obj:`str`, ...]): The JSON data. bot (:class:`telegram.Bot`, optional): The bot associated with this object. Defaults to :obj:`None`, in which case shortcut methods will not be available. @@ -453,8 +438,8 @@ class TelegramObject: @classmethod def de_list( - cls: Type[Tele_co], data: Optional[List[JSONDict]], bot: Optional["Bot"] = None - ) -> Tuple[Tele_co, ...]: + cls: type[Tele_co], data: Optional[list[JSONDict]], bot: Optional["Bot"] = None + ) -> tuple[Tele_co, ...]: """Converts a list of JSON objects to a tuple of Telegram objects. .. versionchanged:: 20.0 @@ -463,7 +448,7 @@ class TelegramObject: * Filters out any :obj:`None` values. Args: - data (List[Dict[:obj:`str`, ...]]): The JSON data. + data (list[dict[:obj:`str`, ...]]): The JSON data. bot (:class:`telegram.Bot`, optional): The bot associated with these object. Defaults to :obj:`None`, in which case shortcut methods will not be available. @@ -552,7 +537,7 @@ class TelegramObject: recursive: bool = False, remove_bot: bool = False, convert_default_vault: bool = True, - ) -> Dict[str, Union[str, object]]: + ) -> dict[str, Union[str, object]]: """This method is used for obtaining the attributes of the object. Args: @@ -625,7 +610,7 @@ class TelegramObject: # 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() + pop_keys: set[str] = set() for key, value in out.items(): if isinstance(value, (tuple, list)): if not value: @@ -637,7 +622,7 @@ class TelegramObject: for item in value: if hasattr(item, "to_dict"): val.append(item.to_dict(recursive=recursive)) - # This branch is useful for e.g. Tuple[Tuple[PhotoSize|KeyboardButton]] + # This branch is useful for e.g. tuple[tuple[PhotoSize|KeyboardButton]] elif isinstance(item, (tuple, list)): val.append( [ diff --git a/telegram/_update.py b/telegram/_update.py index 5db7b9a55..abacce72c 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, Final, List, Optional, Union +from typing import TYPE_CHECKING, Final, Optional, Union from telegram import constants from telegram._business import BusinessConnection, BusinessMessagesDeleted @@ -402,8 +402,8 @@ class Update(TelegramObject): .. versionadded:: 21.6 """ - ALL_TYPES: Final[List[str]] = list(constants.UpdateType) - """List[:obj:`str`]: A list of all available update types. + ALL_TYPES: Final[list[str]] = list(constants.UpdateType) + """list[:obj:`str`]: A list of all available update types. .. versionadded:: 13.5""" diff --git a/telegram/_user.py b/telegram/_user.py index 075c4f128..9e8e1f0ea 100644 --- a/telegram/_user.py +++ b/telegram/_user.py @@ -18,8 +18,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 User.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Optional, Sequence, Tuple, Union +from typing import TYPE_CHECKING, Optional, Union from telegram._inline.inlinekeyboardbutton import InlineKeyboardButton from telegram._menubutton import MenuButton @@ -621,7 +622,7 @@ class User(TelegramObject): caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, - ) -> Tuple["Message", ...]: + ) -> tuple["Message", ...]: """Shortcut for:: await bot.send_media_group(update.effective_user.id, *args, **kwargs) @@ -632,7 +633,7 @@ class User(TelegramObject): |user_chat_id_note| 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. """ @@ -1739,7 +1740,7 @@ class User(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.copy_messages(chat_id=update.effective_user.id, *argss, **kwargs) @@ -1751,7 +1752,7 @@ class User(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of the sent messages is returned. """ @@ -1784,7 +1785,7 @@ class User(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.copy_messages(from_chat_id=update.effective_user.id, *argss, **kwargs) @@ -1796,7 +1797,7 @@ class User(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of the sent messages is returned. """ @@ -1913,7 +1914,7 @@ class User(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.forward_messages(chat_id=update.effective_user.id, *argss, **kwargs) @@ -1925,7 +1926,7 @@ class User(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of sent messages is returned. """ @@ -1956,7 +1957,7 @@ class User(TelegramObject): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: """Shortcut for:: await bot.forward_messages(from_chat_id=update.effective_user.id, *argss, **kwargs) @@ -1968,7 +1969,7 @@ class User(TelegramObject): .. versionadded:: 20.8 Returns: - Tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` + tuple[:class:`telegram.MessageId`]: On success, a tuple of :class:`~telegram.MessageId` of sent messages is returned. """ diff --git a/telegram/_userprofilephotos.py b/telegram/_userprofilephotos.py index 9a5e4a066..c7355bb87 100644 --- a/telegram/_userprofilephotos.py +++ b/telegram/_userprofilephotos.py @@ -17,7 +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 UserProfilePhotos.""" -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._files.photosize import PhotoSize from telegram._telegramobject import TelegramObject @@ -43,7 +44,7 @@ class UserProfilePhotos(TelegramObject): Attributes: total_count (:obj:`int`): Total number of profile pictures. - photos (Tuple[Tuple[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 + photos (tuple[tuple[:class:`telegram.PhotoSize`]]): Requested profile pictures (in up to 4 sizes each). .. versionchanged:: 20.0 @@ -63,7 +64,7 @@ class UserProfilePhotos(TelegramObject): super().__init__(api_kwargs=api_kwargs) # Required self.total_count: int = total_count - self.photos: Tuple[Tuple[PhotoSize, ...], ...] = tuple(tuple(sizes) for sizes in photos) + self.photos: tuple[tuple[PhotoSize, ...], ...] = tuple(tuple(sizes) for sizes in photos) self._id_attrs = (self.total_count, self.photos) diff --git a/telegram/_utils/argumentparsing.py b/telegram/_utils/argumentparsing.py index c3613ecdd..22485512f 100644 --- a/telegram/_utils/argumentparsing.py +++ b/telegram/_utils/argumentparsing.py @@ -23,7 +23,8 @@ Warning: 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 +from collections.abc import Sequence +from typing import Optional, TypeVar from telegram._linkpreviewoptions import LinkPreviewOptions from telegram._utils.types import ODVInput @@ -31,7 +32,7 @@ from telegram._utils.types import ODVInput T = TypeVar("T") -def parse_sequence_arg(arg: Optional[Sequence[T]]) -> Tuple[T, ...]: +def parse_sequence_arg(arg: Optional[Sequence[T]]) -> tuple[T, ...]: """Parses an optional sequence into a tuple Args: diff --git a/telegram/_utils/entities.py b/telegram/_utils/entities.py index 34901c3d6..c97560e8f 100644 --- a/telegram/_utils/entities.py +++ b/telegram/_utils/entities.py @@ -23,7 +23,8 @@ Warning: user. Changes to this module are not considered breaking changes and may not be documented in the changelog. """ -from typing import Dict, Optional, Sequence +from collections.abc import Sequence +from typing import Optional from telegram._messageentity import MessageEntity from telegram._utils.strings import TextEncoding @@ -47,7 +48,7 @@ def parse_message_entity(text: str, entity: MessageEntity) -> str: def parse_message_entities( text: str, entities: Sequence[MessageEntity], types: Optional[Sequence[str]] = None -) -> Dict[MessageEntity, str]: +) -> dict[MessageEntity, str]: """ Returns a :obj:`dict` that maps :class:`telegram.MessageEntity` to :obj:`str`. It contains entities filtered by their ``type`` attribute as @@ -55,13 +56,13 @@ def parse_message_entities( Args: text (:obj:`str`): The text to extract the entity from. - entities (List[:class:`telegram.MessageEntity`]): The entities to extract the text from. - types (List[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the + entities (list[:class:`telegram.MessageEntity`]): The entities to extract the text from. + types (list[:obj:`str`], optional): List of ``MessageEntity`` types as strings. If the ``type`` attribute of an entity is contained in this list, it will be returned. Defaults to :attr:`telegram.MessageEntity.ALL_TYPES`. Returns: - Dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to + dict[:class:`telegram.MessageEntity`, :obj:`str`]: A dictionary of entities mapped to the text that belongs to them, calculated based on UTF-16 codepoints. """ if types is None: diff --git a/telegram/_utils/enum.py b/telegram/_utils/enum.py index 20a045c02..e58d3c0cb 100644 --- a/telegram/_utils/enum.py +++ b/telegram/_utils/enum.py @@ -25,14 +25,14 @@ Warning: """ import enum as _enum import sys -from typing import Type, TypeVar, Union +from typing import TypeVar, Union _A = TypeVar("_A") _B = TypeVar("_B") _Enum = TypeVar("_Enum", bound=_enum.Enum) -def get_member(enum_cls: Type[_Enum], value: _A, default: _B) -> Union[_Enum, _A, _B]: +def get_member(enum_cls: type[_Enum], value: _A, default: _B) -> Union[_Enum, _A, _B]: """Tries to call ``enum_cls(value)`` to convert the value into an enumeration member. If that fails, the ``default`` is returned. """ diff --git a/telegram/_utils/files.py b/telegram/_utils/files.py index 121c7b339..7f7e29fc1 100644 --- a/telegram/_utils/files.py +++ b/telegram/_utils/files.py @@ -29,7 +29,7 @@ Warning: """ from pathlib import Path -from typing import IO, TYPE_CHECKING, Any, Optional, Tuple, Type, TypeVar, Union, cast, overload +from typing import IO, TYPE_CHECKING, Any, Optional, TypeVar, Union, cast, overload from telegram._utils.types import FileInput, FilePathInput @@ -40,16 +40,16 @@ _T = TypeVar("_T", bound=Union[bytes, "InputFile", str, Path, None]) @overload -def load_file(obj: IO[bytes]) -> Tuple[Optional[str], bytes]: ... +def load_file(obj: IO[bytes]) -> tuple[Optional[str], bytes]: ... @overload -def load_file(obj: _T) -> Tuple[None, _T]: ... +def load_file(obj: _T) -> tuple[None, _T]: ... def load_file( obj: Optional[FileInput], -) -> Tuple[Optional[str], Union[bytes, "InputFile", str, Path, None]]: +) -> tuple[Optional[str], Union[bytes, "InputFile", str, Path, None]]: """If the input is a file handle, read the data and name and return it. Otherwise, return the input unchanged. """ @@ -95,7 +95,7 @@ def is_local_file(obj: Optional[FilePathInput]) -> bool: def parse_file_input( # pylint: disable=too-many-return-statements file_input: Union[FileInput, "TelegramObject"], - tg_type: Optional[Type["TelegramObject"]] = None, + tg_type: Optional[type["TelegramObject"]] = None, filename: Optional[str] = None, attach: bool = False, local_mode: bool = False, diff --git a/telegram/_utils/types.py b/telegram/_utils/types.py index 8a01fdc2d..f97487732 100644 --- a/telegram/_utils/types.py +++ b/telegram/_utils/types.py @@ -23,19 +23,9 @@ Warning: user. Changes to this module are not considered breaking changes and may not be documented in the changelog. """ +from collections.abc import Collection from pathlib import Path -from typing import ( - IO, - TYPE_CHECKING, - Any, - Collection, - Dict, - Literal, - Optional, - Tuple, - TypeVar, - Union, -) +from typing import IO, TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union if TYPE_CHECKING: from telegram import ( @@ -57,7 +47,7 @@ FileInput = Union[FilePathInput, FileLike, bytes, str] """Valid input for passing files to Telegram. Either a file id as string, a file like object, a local file path as string, :class:`pathlib.Path` or the file contents as :obj:`bytes`.""" -JSONDict = Dict[str, Any] +JSONDict = dict[str, Any] """Dictionary containing response from Telegram or data to send to the API.""" DVValueType = TypeVar("DVValueType") # pylint: disable=invalid-name @@ -82,9 +72,9 @@ ReplyMarkup = Union[ .. versionadded:: 20.0 """ -FieldTuple = Tuple[str, Union[bytes, IO[bytes]], str] +FieldTuple = tuple[str, Union[bytes, IO[bytes]], str] """Alias for return type of `InputFile.field_tuple`.""" -UploadFileDict = Dict[str, FieldTuple] +UploadFileDict = dict[str, FieldTuple] """Dictionary containing file data to be uploaded to the API.""" HTTPVersion = Literal["1.1", "2.0", "2"] @@ -97,7 +87,7 @@ CorrectOptionID = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] MarkdownVersion = Literal[1, 2] SocketOpt = Union[ - Tuple[int, int, int], - Tuple[int, int, Union[bytes, bytearray]], - Tuple[int, int, None, int], + tuple[int, int, int], + tuple[int, int, Union[bytes, bytearray]], + tuple[int, int, None, int], ] diff --git a/telegram/_utils/warnings.py b/telegram/_utils/warnings.py index dcc3fc150..2ddb2317d 100644 --- a/telegram/_utils/warnings.py +++ b/telegram/_utils/warnings.py @@ -26,14 +26,14 @@ Warning: the changelog. """ import warnings -from typing import Type, Union +from typing import Union from telegram.warnings import PTBUserWarning def warn( message: Union[str, PTBUserWarning], - category: Type[Warning] = PTBUserWarning, + category: type[Warning] = PTBUserWarning, stacklevel: int = 0, ) -> None: """ @@ -48,7 +48,7 @@ def warn( .. versionchanged:: 21.2 Now also accepts a :obj:`PTBUserWarning` instance. - category (:obj:`Type[Warning]`, optional): Specify the Warning class to pass to + category (:obj:`type[Warning]`, optional): Specify the Warning class to pass to ``warnings.warn()``. Defaults to :class:`telegram.warnings.PTBUserWarning`. stacklevel (:obj:`int`, optional): Specify the stacklevel to pass to ``warnings.warn()``. Pass the same value as you'd pass directly to ``warnings.warn()``. Defaults to ``0``. diff --git a/telegram/_utils/warnings_transition.py b/telegram/_utils/warnings_transition.py index a135ee5e6..43bb6c225 100644 --- a/telegram/_utils/warnings_transition.py +++ b/telegram/_utils/warnings_transition.py @@ -23,7 +23,7 @@ inside warnings.py. .. versionadded:: 20.2 """ -from typing import Any, Callable, Type, Union +from typing import Any, Callable, Union from telegram._utils.warnings import warn from telegram.warnings import PTBDeprecationWarning, PTBUserWarning @@ -56,7 +56,7 @@ def warn_about_deprecated_arg_return_new_arg( bot_api_version: str, ptb_version: str, stacklevel: int = 2, - warn_callback: Callable[[Union[str, PTBUserWarning], Type[Warning], int], None] = warn, + warn_callback: Callable[[Union[str, PTBUserWarning], type[Warning], int], None] = warn, ) -> Any: """A helper function for the transition in API when argument is renamed. diff --git a/telegram/_videochat.py b/telegram/_videochat.py index b392fa6d6..916d8f9ef 100644 --- a/telegram/_videochat.py +++ b/telegram/_videochat.py @@ -18,7 +18,8 @@ # 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, Optional, Sequence, Tuple +from collections.abc import Sequence +from typing import TYPE_CHECKING, Optional from telegram._telegramobject import TelegramObject from telegram._user import User @@ -102,7 +103,7 @@ class VideoChatParticipantsInvited(TelegramObject): |sequenceclassargs| Attributes: - users (Tuple[: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| @@ -118,7 +119,7 @@ class VideoChatParticipantsInvited(TelegramObject): api_kwargs: Optional[JSONDict] = None, ) -> None: super().__init__(api_kwargs=api_kwargs) - self.users: Tuple[User, ...] = parse_sequence_arg(users) + self.users: tuple[User, ...] = parse_sequence_arg(users) self._id_attrs = (self.users,) self._freeze() diff --git a/telegram/_webhookinfo.py b/telegram/_webhookinfo.py index a6f309a93..607a6f6b1 100644 --- a/telegram/_webhookinfo.py +++ b/telegram/_webhookinfo.py @@ -17,8 +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 WebhookInfo.""" +from collections.abc import Sequence from datetime import datetime -from typing import TYPE_CHECKING, Optional, Sequence, Tuple +from typing import TYPE_CHECKING, Optional from telegram._telegramobject import TelegramObject from telegram._utils.argumentparsing import parse_sequence_arg @@ -89,7 +90,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`]): 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`. @@ -144,7 +145,7 @@ class WebhookInfo(TelegramObject): self.last_error_date: Optional[datetime] = last_error_date self.last_error_message: Optional[str] = last_error_message self.max_connections: Optional[int] = max_connections - self.allowed_updates: Tuple[str, ...] = parse_sequence_arg(allowed_updates) + self.allowed_updates: tuple[str, ...] = parse_sequence_arg(allowed_updates) self.last_synchronization_error_date: Optional[datetime] = last_synchronization_error_date self._id_attrs = ( diff --git a/telegram/constants.py b/telegram/constants.py index 582de62c4..cffe79880 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -108,7 +108,7 @@ __all__ = [ import datetime import sys from enum import Enum -from typing import Final, List, NamedTuple, Optional, Tuple +from typing import Final, NamedTuple, Optional from telegram._utils.datetime import UTC from telegram._utils.enum import IntEnum, StringEnum @@ -141,8 +141,8 @@ class _AccentColor(NamedTuple): identifier: int name: Optional[str] = None - light_colors: Tuple[int, ...] = () - dark_colors: Tuple[int, ...] = () + light_colors: tuple[int, ...] = () + dark_colors: tuple[int, ...] = () #: :class:`typing.NamedTuple`: A tuple containing the two components of the version number: @@ -162,9 +162,9 @@ BOT_API_VERSION: Final[str] = str(BOT_API_VERSION_INFO) # constants above this line are tested -#: List[:obj:`int`]: Ports supported by +#: list[:obj:`int`]: Ports supported by #: :paramref:`telegram.Bot.set_webhook.url`. -SUPPORTED_WEBHOOK_PORTS: Final[List[int]] = [443, 80, 88, 8443] +SUPPORTED_WEBHOOK_PORTS: Final[list[int]] = [443, 80, 88, 8443] #: :obj:`datetime.datetime`, value of unix 0. #: This date literal is used in :class:`telegram.InaccessibleMessage` @@ -180,9 +180,9 @@ class AccentColor(Enum): - ``identifier`` (:obj:`int`): The identifier of the accent color. - ``name`` (:obj:`str`): Optional. The name of the accent color. - - ``light_colors`` (Tuple[:obj:`str`]): Optional. The light colors of the accent color as HEX + - ``light_colors`` (tuple[:obj:`str`]): Optional. The light colors of the accent color as HEX value. - - ``dark_colors`` (Tuple[:obj:`str`]): Optional. The dark colors of the accent color as HEX + - ``dark_colors`` (tuple[:obj:`str`]): Optional. The dark colors of the accent color as HEX value. Since Telegram gives no exact specification for the accent colors, future accent colors might @@ -2083,9 +2083,9 @@ class ProfileAccentColor(Enum): - ``identifier`` (:obj:`int`): The identifier of the accent color. - ``name`` (:obj:`str`): Optional. The name of the accent color. - - ``light_colors`` (Tuple[:obj:`str`]): Optional. The light colors of the accent color as HEX + - ``light_colors`` (tuple[:obj:`str`]): Optional. The light colors of the accent color as HEX value. - - ``dark_colors`` (Tuple[:obj:`str`]): Optional. The dark colors of the accent color as HEX + - ``dark_colors`` (tuple[:obj:`str`]): Optional. The dark colors of the accent color as HEX value. Since Telegram gives no exact specification for the accent colors, future accent colors might diff --git a/telegram/error.py b/telegram/error.py index 6dcc509a8..6dae43d05 100644 --- a/telegram/error.py +++ b/telegram/error.py @@ -36,20 +36,7 @@ __all__ = ( "TimedOut", ) -from typing import Optional, Tuple, Union - - -def _lstrip_str(in_s: str, lstr: str) -> str: - """ - Args: - in_s (:obj:`str`): in string - lstr (:obj:`str`): substr to strip from left side - - Returns: - :obj:`str`: The stripped string. - - """ - return in_s[len(lstr) :] if in_s.startswith(lstr) else in_s +from typing import Optional, Union class TelegramError(Exception): @@ -70,9 +57,9 @@ class TelegramError(Exception): def __init__(self, message: str): super().__init__() - msg = _lstrip_str(message, "Error: ") - msg = _lstrip_str(msg, "[Error]: ") - msg = _lstrip_str(msg, "Bad Request: ") + msg = message.removeprefix("Error: ") + msg = msg.removeprefix("[Error]: ") + msg = msg.removeprefix("Bad Request: ") if msg != message: # api_error - capitalize the msg... msg = msg.capitalize() @@ -94,7 +81,7 @@ class TelegramError(Exception): """ return f"{self.__class__.__name__}('{self.message}')" - def __reduce__(self) -> Tuple[type, Tuple[str]]: + def __reduce__(self) -> tuple[type, tuple[str]]: """Defines how to serialize the exception for pickle. .. seealso:: @@ -209,7 +196,7 @@ class ChatMigrated(TelegramError): super().__init__(f"Group migrated to supergroup. New chat id: {new_chat_id}") self.new_chat_id: int = new_chat_id - def __reduce__(self) -> Tuple[type, Tuple[int]]: # type: ignore[override] + def __reduce__(self) -> tuple[type, tuple[int]]: # type: ignore[override] return self.__class__, (self.new_chat_id,) @@ -234,7 +221,7 @@ class RetryAfter(TelegramError): super().__init__(f"Flood control exceeded. Retry in {retry_after} seconds") self.retry_after: int = retry_after - def __reduce__(self) -> Tuple[type, Tuple[float]]: # type: ignore[override] + def __reduce__(self) -> tuple[type, tuple[float]]: # type: ignore[override] return self.__class__, (self.retry_after,) @@ -243,7 +230,7 @@ class Conflict(TelegramError): __slots__ = () - def __reduce__(self) -> Tuple[type, Tuple[str]]: + def __reduce__(self) -> tuple[type, tuple[str]]: return self.__class__, (self.message,) @@ -261,5 +248,5 @@ class PassportDecryptionError(TelegramError): super().__init__(f"PassportDecryptionError: {message}") self._msg = str(message) - def __reduce__(self) -> Tuple[type, Tuple[str]]: + def __reduce__(self) -> tuple[type, tuple[str]]: return self.__class__, (self._msg,) diff --git a/telegram/ext/_aioratelimiter.py b/telegram/ext/_aioratelimiter.py index 3a5af9b85..e690037ed 100644 --- a/telegram/ext/_aioratelimiter.py +++ b/telegram/ext/_aioratelimiter.py @@ -22,7 +22,8 @@ library. import asyncio import contextlib import sys -from typing import Any, AsyncIterator, Callable, Coroutine, Dict, List, Optional, Union +from collections.abc import AsyncIterator, Coroutine +from typing import Any, Callable, Optional, Union try: from aiolimiter import AsyncLimiter @@ -152,7 +153,7 @@ class AIORateLimiter(BaseRateLimiter[int]): self._group_max_rate = 0 self._group_time_period = 0 - self._group_limiters: Dict[Union[str, int], AsyncLimiter] = {} + self._group_limiters: dict[Union[str, int], AsyncLimiter] = {} self._max_retries: int = max_retries self._retry_after_event = asyncio.Event() self._retry_after_event.set() @@ -187,10 +188,10 @@ class AIORateLimiter(BaseRateLimiter[int]): self, chat: bool, group: Union[str, int, bool], - callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], + callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, list[JSONDict]]]], args: Any, - kwargs: Dict[str, Any], - ) -> Union[bool, JSONDict, List[JSONDict]]: + kwargs: dict[str, Any], + ) -> 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() @@ -205,13 +206,13 @@ class AIORateLimiter(BaseRateLimiter[int]): # mypy doesn't understand that the last run of the for loop raises an exception async def process_request( self, - callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], + callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, list[JSONDict]]]], args: Any, - kwargs: Dict[str, Any], + kwargs: dict[str, Any], endpoint: str, # noqa: ARG002 - data: Dict[str, Any], + data: dict[str, Any], rate_limit_args: Optional[int], - ) -> Union[bool, JSONDict, List[JSONDict]]: + ) -> Union[bool, JSONDict, list[JSONDict]]: """ Processes a request by applying rate limiting. diff --git a/telegram/ext/_application.py b/telegram/ext/_application.py index 904119c18..d0997a1fc 100644 --- a/telegram/ext/_application.py +++ b/telegram/ext/_application.py @@ -26,30 +26,11 @@ import platform import signal import sys from collections import defaultdict +from collections.abc import Awaitable, Coroutine, Generator, Mapping, Sequence from copy import deepcopy from pathlib import Path from types import MappingProxyType, TracebackType -from typing import ( - TYPE_CHECKING, - Any, - AsyncContextManager, - Awaitable, - Callable, - Coroutine, - DefaultDict, - Dict, - Generator, - Generic, - List, - Mapping, - NoReturn, - Optional, - Sequence, - Set, - Type, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Generic, NoReturn, Optional, TypeVar, Union from telegram._update import Update from telegram._utils.defaultvalue import ( @@ -132,7 +113,10 @@ class ApplicationHandlerStop(Exception): self.state: Optional[object] = state -class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Application"]): +class Application( + Generic[BT, CCT, UD, CD, BD, JQ], + contextlib.AbstractAsyncContextManager["Application"], +): """This class dispatches all kinds of updates to its registered handlers, and is the entry point to a PTB application. @@ -224,12 +208,12 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica bot_data (:obj:`dict`): A dictionary handlers can use to store data for the bot. persistence (:class:`telegram.ext.BasePersistence`): The persistence class to store data that should be persistent over restarts. - handlers (Dict[:obj:`int`, List[:class:`telegram.ext.BaseHandler`]]): A dictionary mapping + handlers (dict[:obj:`int`, list[:class:`telegram.ext.BaseHandler`]]): A dictionary mapping each handler group to the list of handlers registered to that group. .. seealso:: :meth:`add_handler`, :meth:`add_handlers`. - error_handlers (Dict[:term:`coroutine function`, :obj:`bool`]): A dictionary where the keys + error_handlers (dict[:term:`coroutine function`, :obj:`bool`]): A dictionary where the keys are error handlers and the values indicate whether they are to be run blocking. .. seealso:: @@ -323,8 +307,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica self.update_queue: asyncio.Queue[object] = update_queue self.context_types: ContextTypes[CCT, UD, CD, BD] = context_types self.updater: Optional[Updater] = updater - self.handlers: Dict[int, List[BaseHandler[Any, CCT, Any]]] = {} - self.error_handlers: Dict[ + self.handlers: dict[int, list[BaseHandler[Any, CCT, Any]]] = {} + self.error_handlers: dict[ HandlerCallback[object, CCT, None], Union[bool, DefaultValue[bool]] ] = {} self.post_init: Optional[ @@ -338,8 +322,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica ] = post_stop self._update_processor = update_processor self.bot_data: BD = self.context_types.bot_data() - self._user_data: DefaultDict[int, UD] = defaultdict(self.context_types.user_data) - self._chat_data: DefaultDict[int, CD] = defaultdict(self.context_types.chat_data) + self._user_data: defaultdict[int, UD] = defaultdict(self.context_types.user_data) + self._chat_data: defaultdict[int, CD] = defaultdict(self.context_types.chat_data) # Read only mapping self.user_data: Mapping[int, UD] = MappingProxyType(self._user_data) self.chat_data: Mapping[int, CD] = MappingProxyType(self._chat_data) @@ -350,14 +334,14 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica self.persistence = persistence # Some bookkeeping for persistence logic - self._chat_ids_to_be_updated_in_persistence: Set[int] = set() - self._user_ids_to_be_updated_in_persistence: Set[int] = set() - self._chat_ids_to_be_deleted_in_persistence: Set[int] = set() - self._user_ids_to_be_deleted_in_persistence: Set[int] = set() + self._chat_ids_to_be_updated_in_persistence: set[int] = set() + self._user_ids_to_be_updated_in_persistence: set[int] = set() + self._chat_ids_to_be_deleted_in_persistence: set[int] = set() + self._user_ids_to_be_deleted_in_persistence: set[int] = set() # This attribute will hold references to the conversation dicts of all conversation # handlers so that we can extract the changed states during `update_persistence` - self._conversation_handler_conversations: Dict[ + self._conversation_handler_conversations: dict[ str, TrackingDict[ConversationKey, object] ] = {} @@ -369,7 +353,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica self.__update_persistence_task: Optional[asyncio.Task] = None self.__update_persistence_event = asyncio.Event() self.__update_persistence_lock = asyncio.Lock() - self.__create_task_tasks: Set[asyncio.Task] = set() # Used for awaiting tasks upon exit + self.__create_task_tasks: set[asyncio.Task] = set() # Used for awaiting tasks upon exit self.__stop_running_marker = asyncio.Event() async def __aenter__(self: _AppType) -> _AppType: # noqa: PYI019 @@ -391,7 +375,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: @@ -762,7 +746,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - allowed_updates: Optional[List[str]] = None, + allowed_updates: Optional[list[str]] = None, drop_pending_updates: Optional[bool] = None, close_loop: bool = True, stop_signals: ODVInput[Sequence[int]] = DEFAULT_NONE, @@ -839,7 +823,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica :meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout`. drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on Telegram servers before actually starting to poll. Default is :obj:`False`. - allowed_updates (List[:obj:`str`], optional): Passed to + allowed_updates (list[:obj:`str`], optional): Passed to :meth:`telegram.Bot.get_updates`. close_loop (:obj:`bool`, optional): If :obj:`True`, the current event loop will be closed upon shutdown. Defaults to :obj:`True`. @@ -904,7 +888,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica key: Optional[Union[str, Path]] = None, bootstrap_retries: int = 0, webhook_url: Optional[str] = None, - allowed_updates: Optional[List[str]] = None, + allowed_updates: Optional[list[str]] = None, drop_pending_updates: Optional[bool] = None, ip_address: Optional[str] = None, max_connections: int = 40, @@ -970,7 +954,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica webhook_url (:obj:`str`, optional): Explicitly specify the webhook url. Useful behind NAT, reverse proxy, etc. Default is derived from :paramref:`listen`, :paramref:`port`, :paramref:`url_path`, :paramref:`cert`, and :paramref:`key`. - allowed_updates (List[:obj:`str`], optional): Passed to + allowed_updates (list[:obj:`str`], optional): Passed to :meth:`telegram.Bot.set_webhook`. drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on Telegram servers before actually starting to poll. Default is :obj:`False`. @@ -1421,7 +1405,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica self, handlers: Union[ Sequence[BaseHandler[Any, CCT, Any]], - Dict[int, Sequence[BaseHandler[Any, CCT, Any]]], + dict[int, Sequence[BaseHandler[Any, CCT, Any]]], ], group: Union[int, DefaultValue[int]] = _DEFAULT_0, ) -> None: @@ -1432,7 +1416,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica Args: handlers (Sequence[:class:`telegram.ext.BaseHandler`] | \ - Dict[int, Sequence[:class:`telegram.ext.BaseHandler`]]): + dict[int, Sequence[:class:`telegram.ext.BaseHandler`]]): Specify a sequence of handlers *or* a dictionary where the keys are groups and values are handlers. @@ -1693,7 +1677,7 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica _LOGGER.debug("Starting next run of updating the persistence.") - coroutines: Set[Coroutine] = set() + coroutines: set[Coroutine] = set() # 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 ... diff --git a/telegram/ext/_applicationbuilder.py b/telegram/ext/_applicationbuilder.py index 838c47495..f4bfee761 100644 --- a/telegram/ext/_applicationbuilder.py +++ b/telegram/ext/_applicationbuilder.py @@ -18,20 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the Builder classes for the telegram.ext module.""" from asyncio import Queue +from collections.abc import Collection, Coroutine from pathlib import Path -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Collection, - Coroutine, - Dict, - Generic, - Optional, - Type, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, TypeVar, Union import httpx @@ -212,8 +201,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]): self._persistence: ODVInput[BasePersistence] = DEFAULT_NONE self._context_types: DVType[ContextTypes] = DefaultValue(ContextTypes()) - self._application_class: DVType[Type[Application]] = DefaultValue(Application) - self._application_kwargs: Dict[str, object] = {} + self._application_class: DVType[type[Application]] = DefaultValue(Application) + self._application_kwargs: dict[str, object] = {} self._update_processor: BaseUpdateProcessor = SimpleUpdateProcessor( max_concurrent_updates=1 ) @@ -350,8 +339,8 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]): def application_class( self: BuilderType, - application_class: Type[Application[Any, Any, Any, Any, Any, Any]], - kwargs: Optional[Dict[str, object]] = None, + application_class: type[Application[Any, Any, Any, Any, Any, Any]], + kwargs: Optional[dict[str, object]] = None, ) -> BuilderType: """Sets a custom subclass instead of :class:`telegram.ext.Application`. The subclass's ``__init__`` should look like this @@ -365,7 +354,7 @@ class ApplicationBuilder(Generic[BT, CCT, UD, CD, BD, JQ]): Args: application_class (:obj:`type`): A subclass of :class:`telegram.ext.Application` - kwargs (Dict[:obj:`str`, :obj:`object`], optional): Keyword arguments for the + kwargs (dict[:obj:`str`, :obj:`object`], optional): Keyword arguments for the initialization. Defaults to an empty dict. Returns: @@ -1397,9 +1386,9 @@ InitApplicationBuilder = ( # This is defined all the way down here so that its ApplicationBuilder[ # by Pylance correctly. ExtBot[None], ContextTypes.DEFAULT_TYPE, - Dict[Any, Any], - Dict[Any, Any], - Dict[Any, Any], + dict[Any, Any], + dict[Any, Any], + dict[Any, Any], JobQueue[ContextTypes.DEFAULT_TYPE], ] ) diff --git a/telegram/ext/_basepersistence.py b/telegram/ext/_basepersistence.py index 126437a0a..9f919b14a 100644 --- a/telegram/ext/_basepersistence.py +++ b/telegram/ext/_basepersistence.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the BasePersistence class.""" from abc import ABC, abstractmethod -from typing import Dict, Generic, NamedTuple, NoReturn, Optional +from typing import Generic, NamedTuple, NoReturn, Optional from telegram._bot import Bot from telegram.ext._extbot import ExtBot @@ -184,7 +184,7 @@ class BasePersistence(Generic[UD, CD, BD], ABC): self.bot = bot @abstractmethod - async def get_user_data(self) -> Dict[int, UD]: + async def get_user_data(self) -> dict[int, UD]: """Will be called by :class:`telegram.ext.Application` upon creation with a persistence object. It should return the ``user_data`` if stored, or an empty :obj:`dict`. In the latter case, the dictionary should produce values @@ -198,12 +198,12 @@ class BasePersistence(Generic[UD, CD, BD], ABC): This method may now return a :obj:`dict` instead of a :obj:`collections.defaultdict` Returns: - Dict[:obj:`int`, :obj:`dict` | :attr:`telegram.ext.ContextTypes.user_data`]: + dict[:obj:`int`, :obj:`dict` | :attr:`telegram.ext.ContextTypes.user_data`]: The restored user data. """ @abstractmethod - async def get_chat_data(self) -> Dict[int, CD]: + async def get_chat_data(self) -> dict[int, CD]: """Will be called by :class:`telegram.ext.Application` upon creation with a persistence object. It should return the ``chat_data`` if stored, or an empty :obj:`dict`. In the latter case, the dictionary should produce values @@ -217,7 +217,7 @@ class BasePersistence(Generic[UD, CD, BD], ABC): This method may now return a :obj:`dict` instead of a :obj:`collections.defaultdict` Returns: - Dict[:obj:`int`, :obj:`dict` | :attr:`telegram.ext.ContextTypes.chat_data`]: + dict[:obj:`int`, :obj:`dict` | :attr:`telegram.ext.ContextTypes.chat_data`]: The restored chat data. """ @@ -233,7 +233,7 @@ class BasePersistence(Generic[UD, CD, BD], ABC): if :class:`telegram.ext.ContextTypes` are used. Returns: - Dict[:obj:`int`, :obj:`dict` | :attr:`telegram.ext.ContextTypes.bot_data`]: + dict[:obj:`int`, :obj:`dict` | :attr:`telegram.ext.ContextTypes.bot_data`]: The restored bot data. """ @@ -248,8 +248,8 @@ class BasePersistence(Generic[UD, CD, BD], ABC): Changed this method into an :external:func:`~abc.abstractmethod`. Returns: - Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], - Dict[:obj:`str`, :obj:`str`]] | :obj:`None`: The restored metadata or :obj:`None`, + tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], + dict[:obj:`str`, :obj:`str`]] | :obj:`None`: The restored metadata or :obj:`None`, if no data was stored. """ @@ -324,8 +324,8 @@ class BasePersistence(Generic[UD, CD, BD], ABC): Changed this method into an :external:func:`~abc.abstractmethod`. Args: - data (Tuple[List[Tuple[:obj:`str`, :obj:`float`, \ - Dict[:obj:`str`, :obj:`Any`]]], Dict[:obj:`str`, :obj:`str`]] | :obj:`None`): + data (tuple[list[tuple[:obj:`str`, :obj:`float`, \ + dict[:obj:`str`, :obj:`Any`]]], dict[:obj:`str`, :obj:`str`]] | :obj:`None`): The relevant data to restore :class:`telegram.ext.CallbackDataCache`. """ diff --git a/telegram/ext/_baseratelimiter.py b/telegram/ext/_baseratelimiter.py index 3d7a6afb1..df2478f2b 100644 --- a/telegram/ext/_baseratelimiter.py +++ b/telegram/ext/_baseratelimiter.py @@ -18,7 +18,8 @@ # 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, List, Optional, Union +from collections.abc import Coroutine +from typing import Any, Callable, Generic, Optional, Union from telegram._utils.types import JSONDict from telegram.ext._utils.types import RLARGS @@ -56,13 +57,13 @@ class BaseRateLimiter(ABC, Generic[RLARGS]): @abstractmethod async def process_request( self, - callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, List[JSONDict]]]], + callback: Callable[..., Coroutine[Any, Any, Union[bool, JSONDict, list[JSONDict]]]], args: Any, - kwargs: Dict[str, Any], + kwargs: dict[str, Any], endpoint: str, - data: Dict[str, Any], + data: dict[str, Any], rate_limit_args: Optional[RLARGS], - ) -> Union[bool, JSONDict, List[JSONDict]]: + ) -> Union[bool, JSONDict, list[JSONDict]]: """ Process a request. Must be implemented by a subclass. @@ -107,13 +108,13 @@ class BaseRateLimiter(ABC, Generic[RLARGS]): Args: callback (Callable[..., :term:`coroutine`]): The coroutine function that must be called to make the request. - args (Tuple[:obj:`object`]): The positional arguments for the :paramref:`callback` + args (tuple[:obj:`object`]): The positional arguments for the :paramref:`callback` function. - kwargs (Dict[:obj:`str`, :obj:`object`]): The keyword arguments for the + kwargs (dict[:obj:`str`, :obj:`object`]): The keyword arguments for the :paramref:`callback` function. endpoint (:obj:`str`): The endpoint that the request is made for, e.g. ``"sendMessage"``. - data (Dict[:obj:`str`, :obj:`object`]): The parameters that were passed to the method + data (dict[:obj:`str`, :obj:`object`]): The parameters that were passed to the method of :class:`~telegram.ext.ExtBot`. Any ``api_kwargs`` are included in this and any :paramref:`~telegram.ext.ExtBot.defaults` are already applied. @@ -136,6 +137,6 @@ class BaseRateLimiter(ABC, Generic[RLARGS]): the request. Returns: - :obj:`bool` | Dict[:obj:`str`, :obj:`object`] | :obj:`None`: The result of the + :obj:`bool` | dict[:obj:`str`, :obj:`object`] | :obj:`None`: The result of the callback function. """ diff --git a/telegram/ext/_baseupdateprocessor.py b/telegram/ext/_baseupdateprocessor.py index 8e9af01fc..38228b6c8 100644 --- a/telegram/ext/_baseupdateprocessor.py +++ b/telegram/ext/_baseupdateprocessor.py @@ -19,13 +19,17 @@ """This module contains the BaseProcessor class.""" from abc import ABC, abstractmethod from asyncio import BoundedSemaphore +from contextlib import AbstractAsyncContextManager from types import TracebackType -from typing import Any, AsyncContextManager, Awaitable, Optional, Type, TypeVar, final +from typing import TYPE_CHECKING, Any, Optional, TypeVar, final + +if TYPE_CHECKING: + from collections.abc import Awaitable _BUPT = TypeVar("_BUPT", bound="BaseUpdateProcessor") -class BaseUpdateProcessor(AsyncContextManager["BaseUpdateProcessor"], ABC): +class BaseUpdateProcessor(AbstractAsyncContextManager["BaseUpdateProcessor"], ABC): """An abstract base class for update processors. You can use this class to implement your own update processor. @@ -88,7 +92,7 @@ class BaseUpdateProcessor(AsyncContextManager["BaseUpdateProcessor"], ABC): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: diff --git a/telegram/ext/_callbackcontext.py b/telegram/ext/_callbackcontext.py index dfd2c3cc8..4a2bc6eb6 100644 --- a/telegram/ext/_callbackcontext.py +++ b/telegram/ext/_callbackcontext.py @@ -17,21 +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 the CallbackContext class.""" -from typing import ( - TYPE_CHECKING, - Any, - Awaitable, - Dict, - Generator, - Generic, - List, - Match, - NoReturn, - Optional, - Type, - TypeVar, - Union, -) + +from collections.abc import Awaitable, Generator +from re import Match +from typing import TYPE_CHECKING, Any, Generic, NoReturn, Optional, TypeVar, Union from telegram._callbackquery import CallbackQuery from telegram._update import Update @@ -105,11 +94,11 @@ class CallbackContext(Generic[BT, UD, CD, BD]): coroutine (:term:`awaitable`): Optional. Only present in error handlers if the error was caused by an awaitable run with :meth:`Application.create_task` or a handler callback with :attr:`block=False `. - matches (List[:meth:`re.Match `]): Optional. If the associated update + matches (list[:meth:`re.Match `]): Optional. If the associated update originated from a :class:`filters.Regex`, this will contain a list of match objects for every pattern where ``re.search(pattern, string)`` returned a match. Note that filters short circuit, so combined regex filters will not always be evaluated. - args (List[:obj:`str`]): Optional. Arguments passed to a command if the associated update + args (list[:obj:`str`]): Optional. Arguments passed to a command if the associated update is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler` or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the text after the command, using any whitespace string as a delimiter. @@ -145,8 +134,8 @@ class CallbackContext(Generic[BT, UD, CD, BD]): self._application: Application[BT, ST, UD, CD, BD, Any] = application self._chat_id: Optional[int] = chat_id self._user_id: Optional[int] = user_id - self.args: Optional[List[str]] = None - self.matches: Optional[List[Match[str]]] = None + self.args: Optional[list[str]] = None + self.matches: Optional[list[Match[str]]] = None self.error: Optional[Exception] = None self.job: Optional[Job[Any]] = None self.coroutine: Optional[ @@ -282,7 +271,7 @@ class CallbackContext(Generic[BT, UD, CD, BD]): @classmethod def from_error( - cls: Type["CCT"], + cls: type["CCT"], update: object, error: Exception, application: "Application[BT, CCT, UD, CD, BD, Any]", @@ -335,7 +324,7 @@ class CallbackContext(Generic[BT, UD, CD, BD]): @classmethod def from_update( - cls: Type["CCT"], + cls: type["CCT"], update: object, application: "Application[Any, CCT, Any, Any, Any, Any]", ) -> "CCT": @@ -365,7 +354,7 @@ class CallbackContext(Generic[BT, UD, CD, BD]): @classmethod def from_job( - cls: Type["CCT"], + cls: type["CCT"], job: "Job[CCT]", application: "Application[Any, CCT, Any, Any, Any, Any]", ) -> "CCT": @@ -387,11 +376,11 @@ class CallbackContext(Generic[BT, UD, CD, BD]): self.job = job return self - def update(self, data: Dict[str, object]) -> None: + def update(self, data: dict[str, object]) -> None: """Updates ``self.__slots__`` with the passed data. Args: - data (Dict[:obj:`str`, :obj:`object`]): The data. + data (dict[:obj:`str`, :obj:`object`]): The data. """ for key, value in data.items(): setattr(self, key, value) diff --git a/telegram/ext/_callbackdatacache.py b/telegram/ext/_callbackdatacache.py index 02aebde5c..97649d2eb 100644 --- a/telegram/ext/_callbackdatacache.py +++ b/telegram/ext/_callbackdatacache.py @@ -18,8 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the CallbackDataCache class.""" import time +from collections.abc import MutableMapping from datetime import datetime -from typing import TYPE_CHECKING, Any, Dict, MutableMapping, Optional, Tuple, Union, cast +from typing import TYPE_CHECKING, Any, Optional, Union, cast from uuid import uuid4 try: @@ -70,7 +71,7 @@ class InvalidCallbackData(TelegramError): ) self.callback_data: Optional[str] = callback_data - def __reduce__(self) -> Tuple[type, Tuple[Optional[str]]]: # type: ignore[override] + def __reduce__(self) -> tuple[type, tuple[Optional[str]]]: # type: ignore[override] """Defines how to serialize the exception for pickle. See :py:meth:`object.__reduce__` for more info. @@ -87,7 +88,7 @@ class _KeyboardData: self, keyboard_uuid: str, access_time: Optional[float] = None, - button_data: Optional[Dict[str, object]] = None, + button_data: Optional[dict[str, object]] = None, ): self.keyboard_uuid = keyboard_uuid self.button_data = button_data or {} @@ -97,7 +98,7 @@ class _KeyboardData: """Updates the access time with the current time.""" self.access_time = time.time() - def to_tuple(self) -> Tuple[str, float, Dict[str, object]]: + def to_tuple(self) -> tuple[str, float, dict[str, object]]: """Gives a tuple representation consisting of the keyboard uuid, the access time and the button data. """ @@ -140,8 +141,8 @@ class CallbackDataCache: maxsize (:obj:`int`, optional): Maximum number of items in each of the internal mappings. Defaults to ``1024``. - persistent_data (Tuple[List[Tuple[:obj:`str`, :obj:`float`, \ - Dict[:obj:`str`, :class:`object`]]], Dict[:obj:`str`, :obj:`str`]], optional): \ + persistent_data (tuple[list[tuple[:obj:`str`, :obj:`float`, \ + dict[:obj:`str`, :class:`object`]]], dict[:obj:`str`, :obj:`str`]], optional): \ Data to initialize the cache with, as returned by \ :meth:`telegram.ext.BasePersistence.get_callback_data`. @@ -181,8 +182,8 @@ class CallbackDataCache: .. versionadded:: 20.0 Args: - persistent_data (Tuple[List[Tuple[:obj:`str`, :obj:`float`, \ - Dict[:obj:`str`, :class:`object`]]], Dict[:obj:`str`, :obj:`str`]], optional): \ + persistent_data (tuple[list[tuple[:obj:`str`, :obj:`float`, \ + dict[:obj:`str`, :class:`object`]]], dict[:obj:`str`, :obj:`str`]], optional): \ Data to load, as returned by \ :meth:`telegram.ext.BasePersistence.get_callback_data`. """ @@ -205,8 +206,8 @@ class CallbackDataCache: @property def persistence_data(self) -> CDCData: - """Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], - Dict[:obj:`str`, :obj:`str`]]: The data that needs to be persisted to allow + """tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], + dict[:obj:`str`, :obj:`str`]]: The data that needs to be persisted to allow caching callback data across bot reboots. """ # While building a list/dict from the LRUCaches has linear runtime (in the number of @@ -269,7 +270,7 @@ class CallbackDataCache: def __get_keyboard_uuid_and_button_data( self, callback_data: str - ) -> Union[Tuple[str, object], Tuple[None, InvalidCallbackData]]: + ) -> Union[tuple[str, object], tuple[None, InvalidCallbackData]]: keyboard, button = self.extract_uuids(callback_data) try: # we get the values before calling update() in case KeyErrors are raised @@ -283,7 +284,7 @@ class CallbackDataCache: return keyboard, button_data @staticmethod - def extract_uuids(callback_data: str) -> Tuple[str, str]: + def extract_uuids(callback_data: str) -> tuple[str, str]: """Extracts the keyboard uuid and the button uuid from the given :paramref:`callback_data`. Args: diff --git a/telegram/ext/_contexttypes.py b/telegram/ext/_contexttypes.py index 3754ff429..03495531d 100644 --- a/telegram/ext/_contexttypes.py +++ b/telegram/ext/_contexttypes.py @@ -17,13 +17,13 @@ # 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 auxiliary class ContextTypes.""" -from typing import Any, Dict, Generic, Type, overload +from typing import Any, Generic, overload from telegram.ext._callbackcontext import CallbackContext from telegram.ext._extbot import ExtBot from telegram.ext._utils.types import BD, CCT, CD, UD -ADict = Dict[Any, Any] +ADict = dict[Any, Any] class ContextTypes(Generic[CCT, UD, CD, BD]): @@ -83,109 +83,109 @@ class ContextTypes(Generic[CCT, UD, CD, BD]): ): ... @overload - def __init__(self: "ContextTypes[CCT, ADict, ADict, ADict]", context: Type[CCT]): ... + def __init__(self: "ContextTypes[CCT, ADict, ADict, ADict]", context: type[CCT]): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], UD, ADict, ADict], UD, ADict, ADict]", - user_data: Type[UD], + user_data: type[UD], ): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], ADict, CD, ADict], ADict, CD, ADict]", - chat_data: Type[CD], + chat_data: type[CD], ): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], ADict, ADict, BD], ADict, ADict, BD]", - bot_data: Type[BD], + bot_data: type[BD], ): ... @overload def __init__( - self: "ContextTypes[CCT, UD, ADict, ADict]", context: Type[CCT], user_data: Type[UD] + self: "ContextTypes[CCT, UD, ADict, ADict]", context: type[CCT], user_data: type[UD] ): ... @overload def __init__( - self: "ContextTypes[CCT, ADict, CD, ADict]", context: Type[CCT], chat_data: Type[CD] + self: "ContextTypes[CCT, ADict, CD, ADict]", context: type[CCT], chat_data: type[CD] ): ... @overload def __init__( - self: "ContextTypes[CCT, ADict, ADict, BD]", context: Type[CCT], bot_data: Type[BD] + self: "ContextTypes[CCT, ADict, ADict, BD]", context: type[CCT], bot_data: type[BD] ): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], UD, CD, ADict], UD, CD, ADict]", - user_data: Type[UD], - chat_data: Type[CD], + user_data: type[UD], + chat_data: type[CD], ): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], UD, ADict, BD], UD, ADict, BD]", - user_data: Type[UD], - bot_data: Type[BD], + user_data: type[UD], + bot_data: type[BD], ): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], ADict, CD, BD], ADict, CD, BD]", - chat_data: Type[CD], - bot_data: Type[BD], + chat_data: type[CD], + bot_data: type[BD], ): ... @overload def __init__( self: "ContextTypes[CCT, UD, CD, ADict]", - context: Type[CCT], - user_data: Type[UD], - chat_data: Type[CD], + context: type[CCT], + user_data: type[UD], + chat_data: type[CD], ): ... @overload def __init__( self: "ContextTypes[CCT, UD, ADict, BD]", - context: Type[CCT], - user_data: Type[UD], - bot_data: Type[BD], + context: type[CCT], + user_data: type[UD], + bot_data: type[BD], ): ... @overload def __init__( self: "ContextTypes[CCT, ADict, CD, BD]", - context: Type[CCT], - chat_data: Type[CD], - bot_data: Type[BD], + context: type[CCT], + chat_data: type[CD], + bot_data: type[BD], ): ... @overload def __init__( self: "ContextTypes[CallbackContext[ExtBot[Any], UD, CD, BD], UD, CD, BD]", - user_data: Type[UD], - chat_data: Type[CD], - bot_data: Type[BD], + user_data: type[UD], + chat_data: type[CD], + bot_data: type[BD], ): ... @overload def __init__( self: "ContextTypes[CCT, UD, CD, BD]", - context: Type[CCT], - user_data: Type[UD], - chat_data: Type[CD], - bot_data: Type[BD], + context: type[CCT], + user_data: type[UD], + chat_data: type[CD], + bot_data: type[BD], ): ... def __init__( # type: ignore[misc] self, - context: "Type[CallbackContext[ExtBot[Any], ADict, ADict, ADict]]" = CallbackContext, - bot_data: Type[ADict] = dict, - chat_data: Type[ADict] = dict, - user_data: Type[ADict] = dict, + context: "type[CallbackContext[ExtBot[Any], ADict, ADict, ADict]]" = CallbackContext, + bot_data: type[ADict] = dict, + chat_data: type[ADict] = dict, + user_data: type[ADict] = dict, ): if not issubclass(context, CallbackContext): raise TypeError("context must be a subclass of CallbackContext.") @@ -198,28 +198,28 @@ class ContextTypes(Generic[CCT, UD, CD, BD]): self._user_data = user_data @property - def context(self) -> Type[CCT]: + def context(self) -> type[CCT]: """The type of the ``context`` argument of all (error-)handler callbacks and job callbacks. """ return self._context # type: ignore[return-value] @property - def bot_data(self) -> Type[BD]: + def bot_data(self) -> type[BD]: """The type of :attr:`context.bot_data ` of all (error-)handler callbacks and job callbacks. """ return self._bot_data # type: ignore[return-value] @property - def chat_data(self) -> Type[CD]: + def chat_data(self) -> type[CD]: """The type of :attr:`context.chat_data ` of all (error-)handler callbacks and job callbacks. """ return self._chat_data # type: ignore[return-value] @property - def user_data(self) -> Type[UD]: + def user_data(self) -> type[UD]: """The type of :attr:`context.user_data ` of all (error-)handler callbacks and job callbacks. """ diff --git a/telegram/ext/_defaults.py b/telegram/ext/_defaults.py index 100d54e6e..93e7e3748 100644 --- a/telegram/ext/_defaults.py +++ b/telegram/ext/_defaults.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the class Defaults, which allows passing default values to Application.""" import datetime -from typing import Any, Dict, NoReturn, Optional, final +from typing import Any, NoReturn, Optional, final from telegram import LinkPreviewOptions from telegram._utils.datetime import UTC @@ -228,7 +228,7 @@ class Defaults: return False @property - def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003 + def api_defaults(self) -> dict[str, Any]: # skip-cq: PY-D0003 return self._api_defaults @property diff --git a/telegram/ext/_dictpersistence.py b/telegram/ext/_dictpersistence.py index d046561a2..1efce417e 100644 --- a/telegram/ext/_dictpersistence.py +++ b/telegram/ext/_dictpersistence.py @@ -19,7 +19,7 @@ """This module contains the DictPersistence class.""" import json from copy import deepcopy -from typing import TYPE_CHECKING, Any, Dict, Optional, cast +from typing import TYPE_CHECKING, Any, Optional, cast from telegram.ext import BasePersistence, PersistenceInput from telegram.ext._utils.types import CDCData, ConversationDict, ConversationKey @@ -28,7 +28,7 @@ if TYPE_CHECKING: from telegram._utils.types import JSONDict -class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, Any]]): +class DictPersistence(BasePersistence[dict[Any, Any], dict[Any, Any], dict[Any, Any]]): """Using Python's :obj:`dict` and :mod:`json` for making your bot persistent. Attention: @@ -170,7 +170,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, ) from exc @property - def user_data(self) -> Optional[Dict[int, Dict[Any, Any]]]: + def user_data(self) -> Optional[dict[int, dict[Any, Any]]]: """:obj:`dict`: The user_data as a dict.""" return self._user_data @@ -182,7 +182,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, return json.dumps(self.user_data) @property - def chat_data(self) -> Optional[Dict[int, Dict[Any, Any]]]: + def chat_data(self) -> Optional[dict[int, dict[Any, Any]]]: """:obj:`dict`: The chat_data as a dict.""" return self._chat_data @@ -194,7 +194,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, return json.dumps(self.chat_data) @property - def bot_data(self) -> Optional[Dict[Any, Any]]: + def bot_data(self) -> Optional[dict[Any, Any]]: """:obj:`dict`: The bot_data as a dict.""" return self._bot_data @@ -207,8 +207,8 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, @property def callback_data(self) -> Optional[CDCData]: - """Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], \ - Dict[:obj:`str`, :obj:`str`]]: The metadata on the stored callback data. + """tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], \ + dict[:obj:`str`, :obj:`str`]]: The metadata on the stored callback data. .. versionadded:: 13.6 """ @@ -225,7 +225,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, return json.dumps(self.callback_data) @property - def conversations(self) -> Optional[Dict[str, ConversationDict]]: + def conversations(self) -> Optional[dict[str, ConversationDict]]: """:obj:`dict`: The conversations as a dict.""" return self._conversations @@ -238,7 +238,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, return self._encode_conversations_to_json(self.conversations) return json.dumps(self.conversations) - async def get_user_data(self) -> Dict[int, Dict[object, object]]: + async def get_user_data(self) -> dict[int, dict[object, object]]: """Returns the user_data created from the ``user_data_json`` or an empty :obj:`dict`. Returns: @@ -248,7 +248,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, self._user_data = {} return deepcopy(self.user_data) # type: ignore[arg-type] - async def get_chat_data(self) -> Dict[int, Dict[object, object]]: + async def get_chat_data(self) -> dict[int, dict[object, object]]: """Returns the chat_data created from the ``chat_data_json`` or an empty :obj:`dict`. Returns: @@ -258,7 +258,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, self._chat_data = {} return deepcopy(self.chat_data) # type: ignore[arg-type] - async def get_bot_data(self) -> Dict[object, object]: + async def get_bot_data(self) -> dict[object, object]: """Returns the bot_data created from the ``bot_data_json`` or an empty :obj:`dict`. Returns: @@ -274,8 +274,8 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, .. versionadded:: 13.6 Returns: - Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], \ - Dict[:obj:`str`, :obj:`str`]]: The restored metadata or :obj:`None`, \ + tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], \ + dict[:obj:`str`, :obj:`str`]]: The restored metadata or :obj:`None`, \ if no data was stored. """ if self.callback_data is None: @@ -311,7 +311,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, self._conversations[name][key] = new_state self._conversations_json = None - async def update_user_data(self, user_id: int, data: Dict[Any, Any]) -> None: + async def update_user_data(self, user_id: int, data: dict[Any, Any]) -> None: """Will update the user_data (if changed). Args: @@ -325,7 +325,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, self._user_data[user_id] = data self._user_data_json = None - async def update_chat_data(self, chat_id: int, data: Dict[Any, Any]) -> None: + async def update_chat_data(self, chat_id: int, data: dict[Any, Any]) -> None: """Will update the chat_data (if changed). Args: @@ -339,7 +339,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, self._chat_data[chat_id] = data self._chat_data_json = None - async def update_bot_data(self, data: Dict[Any, Any]) -> None: + async def update_bot_data(self, data: dict[Any, Any]) -> None: """Will update the bot_data (if changed). Args: @@ -356,8 +356,8 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, .. versionadded:: 13.6 Args: - data (Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], \ - Dict[:obj:`str`, :obj:`str`]]): The relevant data to restore + data (tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], \ + dict[:obj:`str`, :obj:`str`]]): The relevant data to restore :class:`telegram.ext.CallbackDataCache`. """ if self._callback_data == data: @@ -391,21 +391,21 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, self._user_data.pop(user_id, None) self._user_data_json = None - async def refresh_user_data(self, user_id: int, user_data: Dict[Any, Any]) -> None: + async def refresh_user_data(self, user_id: int, user_data: dict[Any, Any]) -> None: """Does nothing. .. versionadded:: 13.6 .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_user_data` """ - async def refresh_chat_data(self, chat_id: int, chat_data: Dict[Any, Any]) -> None: + async def refresh_chat_data(self, chat_id: int, chat_data: dict[Any, Any]) -> None: """Does nothing. .. versionadded:: 13.6 .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_chat_data` """ - async def refresh_bot_data(self, bot_data: Dict[Any, Any]) -> None: + async def refresh_bot_data(self, bot_data: dict[Any, Any]) -> None: """Does nothing. .. versionadded:: 13.6 @@ -420,7 +420,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, """ @staticmethod - def _encode_conversations_to_json(conversations: Dict[str, ConversationDict]) -> str: + def _encode_conversations_to_json(conversations: dict[str, ConversationDict]) -> str: """Helper method to encode a conversations dict (that uses tuples as keys) to a JSON-serializable way. Use :meth:`self._decode_conversations_from_json` to decode. @@ -430,7 +430,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, Returns: :obj:`str`: The JSON-serialized conversations dict """ - tmp: Dict[str, JSONDict] = {} + tmp: dict[str, JSONDict] = {} for handler, states in conversations.items(): tmp[handler] = {} for key, state in states.items(): @@ -438,7 +438,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, return json.dumps(tmp) @staticmethod - def _decode_conversations_from_json(json_string: str) -> Dict[str, ConversationDict]: + def _decode_conversations_from_json(json_string: str) -> dict[str, ConversationDict]: """Helper method to decode a conversations dict (that uses tuples as keys) from a JSON-string created with :meth:`self._encode_conversations_to_json`. @@ -449,7 +449,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, :obj:`dict`: The conversations dict after decoding """ tmp = json.loads(json_string) - conversations: Dict[str, ConversationDict] = {} + conversations: dict[str, ConversationDict] = {} for handler, states in tmp.items(): conversations[handler] = {} for key, state in states.items(): @@ -457,7 +457,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, return conversations @staticmethod - def _decode_user_chat_data_from_json(data: str) -> Dict[int, Dict[object, object]]: + def _decode_user_chat_data_from_json(data: str) -> dict[int, dict[object, object]]: """Helper method to decode chat or user data (that uses ints as keys) from a JSON-string. @@ -467,7 +467,7 @@ class DictPersistence(BasePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, Returns: :obj:`dict`: The user/chat_data defaultdict after decoding """ - tmp: Dict[int, Dict[object, object]] = {} + tmp: dict[int, dict[object, object]] = {} decoded_data = json.loads(data) for user, user_data in decoded_data.items(): int_user_id = int(user) diff --git a/telegram/ext/_extbot.py b/telegram/ext/_extbot.py index 3d91acf0d..2a4cdb2e4 100644 --- a/telegram/ext/_extbot.py +++ b/telegram/ext/_extbot.py @@ -18,19 +18,15 @@ # 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 with convenience extensions.""" +from collections.abc import Sequence from copy import copy from datetime import datetime from typing import ( TYPE_CHECKING, Any, Callable, - Dict, Generic, - List, Optional, - Sequence, - Tuple, - Type, TypeVar, Union, cast, @@ -267,7 +263,7 @@ class ExtBot(Bot, Generic[RLARGS]): def _warn( cls, message: Union[str, PTBUserWarning], - category: Type[Warning] = PTBUserWarning, + category: type[Warning] = PTBUserWarning, stacklevel: int = 0, ) -> None: """We override this method to add one more level to the stacklevel, so that the warning @@ -340,7 +336,7 @@ class ExtBot(Bot, Generic[RLARGS]): write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Union[bool, JSONDict, List[JSONDict]]: + ) -> 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. """ @@ -421,7 +417,7 @@ class ExtBot(Bot, Generic[RLARGS]): } ) - def _insert_defaults(self, data: Dict[str, object]) -> None: + def _insert_defaults(self, data: dict[str, object]) -> None: """Inserts the defaults values for optional kwargs for which tg.ext.Defaults provides convenience functionality, i.e. the kwargs with a tg.utils.helpers.DefaultValue default @@ -645,7 +641,7 @@ class ExtBot(Bot, Generic[RLARGS]): connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, - ) -> Tuple[Update, ...]: + ) -> tuple[Update, ...]: updates = await super().get_updates( offset=offset, limit=limit, @@ -670,7 +666,7 @@ class ExtBot(Bot, Generic[RLARGS]): ], next_offset: Optional[str] = None, current_offset: Optional[str] = None, - ) -> Tuple[Sequence["InlineQueryResult"], Optional[str]]: + ) -> tuple[Sequence["InlineQueryResult"], Optional[str]]: """This method is called by Bot.answer_inline_query to build the actual results list. Overriding this to call self._replace_keyboard suffices """ @@ -746,7 +742,7 @@ class ExtBot(Bot, Generic[RLARGS]): self, endpoint: str, api_kwargs: Optional[JSONDict] = None, - return_type: Optional[Type[TelegramObject]] = None, + return_type: Optional[type[TelegramObject]] = None, *, read_timeout: ODVInput[float] = DEFAULT_NONE, write_timeout: ODVInput[float] = DEFAULT_NONE, @@ -854,7 +850,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple["MessageId", ...]: + ) -> tuple["MessageId", ...]: # We override this method to call self._replace_keyboard return await super().copy_messages( chat_id=chat_id, @@ -1744,7 +1740,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple[MessageId, ...]: + ) -> tuple[MessageId, ...]: return await super().forward_messages( chat_id=chat_id, from_chat_id=from_chat_id, @@ -1769,7 +1765,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple[ChatMember, ...]: + ) -> tuple[ChatMember, ...]: return await super().get_chat_administrators( chat_id=chat_id, read_timeout=read_timeout, @@ -1872,7 +1868,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple[Sticker, ...]: + ) -> tuple[Sticker, ...]: return await super().get_forum_topic_icon_stickers( read_timeout=read_timeout, write_timeout=write_timeout, @@ -1894,7 +1890,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple[GameHighScore, ...]: + ) -> tuple[GameHighScore, ...]: return await super().get_game_high_scores( user_id=user_id, chat_id=chat_id, @@ -1936,7 +1932,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple[BotCommand, ...]: + ) -> tuple[BotCommand, ...]: return await super().get_my_commands( scope=scope, language_code=language_code, @@ -1997,7 +1993,7 @@ class ExtBot(Bot, Generic[RLARGS]): pool_timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: Optional[JSONDict] = None, rate_limit_args: Optional[RLARGS] = None, - ) -> Tuple[Sticker, ...]: + ) -> tuple[Sticker, ...]: return await super().get_custom_emoji_stickers( custom_emoji_ids=custom_emoji_ids, read_timeout=read_timeout, @@ -2859,7 +2855,7 @@ class ExtBot(Bot, Generic[RLARGS]): caption: Optional[str] = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Optional[Sequence["MessageEntity"]] = None, - ) -> Tuple[Message, ...]: + ) -> tuple[Message, ...]: return await super().send_media_group( chat_id=chat_id, media=media, @@ -3497,7 +3493,7 @@ class ExtBot(Bot, Generic[RLARGS]): async def set_my_commands( self, - commands: Sequence[Union[BotCommand, Tuple[str, str]]], + commands: Sequence[Union[BotCommand, tuple[str, str]]], scope: Optional[BotCommandScope] = None, language_code: Optional[str] = None, *, diff --git a/telegram/ext/_handlers/callbackqueryhandler.py b/telegram/ext/_handlers/callbackqueryhandler.py index 5e0a0a12a..4aa36c287 100644 --- a/telegram/ext/_handlers/callbackqueryhandler.py +++ b/telegram/ext/_handlers/callbackqueryhandler.py @@ -19,7 +19,8 @@ """This module contains the CallbackQueryHandler class.""" import asyncio import re -from typing import TYPE_CHECKING, Any, Callable, Match, Optional, Pattern, TypeVar, Union, cast +from re import Match, Pattern +from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, cast from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE diff --git a/telegram/ext/_handlers/choseninlineresulthandler.py b/telegram/ext/_handlers/choseninlineresulthandler.py index feac28ba6..538bc4308 100644 --- a/telegram/ext/_handlers/choseninlineresulthandler.py +++ b/telegram/ext/_handlers/choseninlineresulthandler.py @@ -18,7 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the ChosenInlineResultHandler class.""" import re -from typing import TYPE_CHECKING, Any, Match, Optional, Pattern, TypeVar, Union, cast +from re import Match, Pattern +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union, cast from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE diff --git a/telegram/ext/_handlers/commandhandler.py b/telegram/ext/_handlers/commandhandler.py index 54d01acd6..894f89709 100644 --- a/telegram/ext/_handlers/commandhandler.py +++ b/telegram/ext/_handlers/commandhandler.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the CommandHandler class.""" import re -from typing import TYPE_CHECKING, Any, FrozenSet, List, Optional, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union from telegram import MessageEntity, Update from telegram._utils.defaultvalue import DEFAULT_TRUE @@ -101,7 +101,7 @@ class CommandHandler(BaseHandler[Update, CCT, RT]): :exc:`ValueError`: When the command is too long or has illegal chars. Attributes: - commands (FrozenSet[:obj:`str`]): The set of commands this handler should listen for. + commands (frozenset[:obj:`str`]): The set of commands this handler should listen for. callback (:term:`coroutine function`): The callback function for this handler. filters (:class:`telegram.ext.filters.BaseFilter`): Optional. Only allow updates with these filters. @@ -134,7 +134,7 @@ class CommandHandler(BaseHandler[Update, CCT, RT]): for comm in commands: if not re.match(r"^[\da-z_]{1,32}$", comm): raise ValueError(f"Command `{comm}` is not a valid bot command") - self.commands: FrozenSet[str] = commands + self.commands: frozenset[str] = commands self.filters: filters_module.BaseFilter = ( filters if filters is not None else filters_module.UpdateType.MESSAGES @@ -145,7 +145,7 @@ class CommandHandler(BaseHandler[Update, CCT, RT]): if (isinstance(self.has_args, int)) and (self.has_args < 0): raise ValueError("CommandHandler argument has_args cannot be a negative integer") - def _check_correct_args(self, args: List[str]) -> Optional[bool]: + def _check_correct_args(self, args: list[str]) -> Optional[bool]: """Determines whether the args are correct for this handler. Implemented in check_update(). Args: args (:obj:`list`): The args for the handler. @@ -161,7 +161,7 @@ class CommandHandler(BaseHandler[Update, CCT, RT]): def check_update( self, update: object - ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, FilterDataDict]]]]]: + ) -> Optional[Union[bool, tuple[list[str], Optional[Union[bool, FilterDataDict]]]]]: """Determines whether an update should be passed to this handler's :attr:`callback`. Args: @@ -206,7 +206,7 @@ class CommandHandler(BaseHandler[Update, CCT, RT]): context: CCT, update: Update, # noqa: ARG002 application: "Application[Any, CCT, Any, Any, Any, Any]", # noqa: ARG002 - check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]], + check_result: Optional[Union[bool, tuple[list[str], Optional[bool]]]], ) -> None: """Add text after the command to :attr:`CallbackContext.args` as list, split on single whitespaces and add output of data filters to :attr:`CallbackContext` as well. diff --git a/telegram/ext/_handlers/conversationhandler.py b/telegram/ext/_handlers/conversationhandler.py index 83fa373fc..1cb9564ea 100644 --- a/telegram/ext/_handlers/conversationhandler.py +++ b/telegram/ext/_handlers/conversationhandler.py @@ -20,20 +20,7 @@ import asyncio import datetime from dataclasses import dataclass -from typing import ( - TYPE_CHECKING, - Any, - Dict, - Final, - Generic, - List, - NoReturn, - Optional, - Set, - Tuple, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, Final, Generic, NoReturn, Optional, Union, cast from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE, DefaultValue @@ -55,7 +42,7 @@ from telegram.ext._utils.types import CCT, ConversationDict, ConversationKey if TYPE_CHECKING: from telegram.ext import Application, Job, JobQueue -_CheckUpdateType = Tuple[object, ConversationKey, BaseHandler[Update, CCT, object], object] +_CheckUpdateType = tuple[object, ConversationKey, BaseHandler[Update, CCT, object], object] _LOGGER = get_logger(__name__, class_name="ConversationHandler") @@ -192,16 +179,16 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): * :any:`Persistent Conversation Bot ` Args: - entry_points (List[:class:`telegram.ext.BaseHandler`]): A list of :obj:`BaseHandler` + entry_points (list[:class:`telegram.ext.BaseHandler`]): A list of :obj:`BaseHandler` objects that can trigger the start of the conversation. The first handler whose :meth:`check_update` method returns :obj:`True` will be used. If all return :obj:`False`, the update is not handled. - states (Dict[:obj:`object`, List[:class:`telegram.ext.BaseHandler`]]): A :obj:`dict` that + states (dict[:obj:`object`, list[:class:`telegram.ext.BaseHandler`]]): A :obj:`dict` that defines the different states of conversation a user can be in and one or more associated :obj:`BaseHandler` objects that should be used in that state. The first handler whose :meth:`check_update` method returns :obj:`True` will be used. - fallbacks (List[:class:`telegram.ext.BaseHandler`]): A list of handlers that might be used + fallbacks (list[:class:`telegram.ext.BaseHandler`]): A list of handlers that might be used if the user is in a conversation, but every handler for their current state returned :obj:`False` on :meth:`check_update`. The first handler which :meth:`check_update` method returns :obj:`True` will be used. If all return :obj:`False`, the update is not @@ -238,7 +225,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): .. versionchanged:: 20.0 Was previously named as ``persistence``. - map_to_parent (Dict[:obj:`object`, :obj:`object`], optional): A :obj:`dict` that can be + map_to_parent (dict[:obj:`object`, :obj:`object`], optional): A :obj:`dict` that can be used to instruct a child conversation handler to transition into a mapped state on its parent conversation handler in place of a specified nested state. block (:obj:`bool`, optional): Pass :obj:`False` or :obj:`True` to set a default value for @@ -297,9 +284,9 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): # pylint: disable=super-init-not-called def __init__( self: "ConversationHandler[CCT]", - entry_points: List[BaseHandler[Update, CCT, object]], - states: Dict[object, List[BaseHandler[Update, CCT, object]]], - fallbacks: List[BaseHandler[Update, CCT, object]], + entry_points: list[BaseHandler[Update, CCT, object]], + states: dict[object, list[BaseHandler[Update, CCT, object]]], + fallbacks: list[BaseHandler[Update, CCT, object]], allow_reentry: bool = False, per_chat: bool = True, per_user: bool = True, @@ -307,7 +294,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): conversation_timeout: Optional[Union[float, datetime.timedelta]] = None, name: Optional[str] = None, persistent: bool = False, - map_to_parent: Optional[Dict[object, object]] = None, + map_to_parent: Optional[dict[object, object]] = None, block: DVType[bool] = DEFAULT_TRUE, ): # these imports need to be here because of circular import error otherwise @@ -324,9 +311,9 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): # Store the actual setting in a protected variable instead self._block: DVType[bool] = block - self._entry_points: List[BaseHandler[Update, CCT, object]] = entry_points - self._states: Dict[object, List[BaseHandler[Update, CCT, object]]] = states - self._fallbacks: List[BaseHandler[Update, CCT, object]] = fallbacks + self._entry_points: list[BaseHandler[Update, CCT, object]] = entry_points + self._states: dict[object, list[BaseHandler[Update, CCT, object]]] = states + self._fallbacks: list[BaseHandler[Update, CCT, object]] = fallbacks self._allow_reentry: bool = allow_reentry self._per_user: bool = per_user @@ -336,14 +323,14 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): conversation_timeout ) self._name: Optional[str] = name - self._map_to_parent: Optional[Dict[object, object]] = map_to_parent + self._map_to_parent: Optional[dict[object, object]] = map_to_parent # if conversation_timeout is used, this dict is used to schedule a job which runs when the # conv has timed out. - self.timeout_jobs: Dict[ConversationKey, Job[Any]] = {} + self.timeout_jobs: dict[ConversationKey, Job[Any]] = {} self._timeout_jobs_lock = asyncio.Lock() self._conversations: ConversationDict = {} - self._child_conversations: Set[ConversationHandler] = set() + self._child_conversations: set[ConversationHandler] = set() if persistent and not self.name: raise ValueError("Conversations can't be persistent when handler is unnamed.") @@ -359,7 +346,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): stacklevel=2, ) - all_handlers: List[BaseHandler[Update, CCT, object]] = [] + all_handlers: list[BaseHandler[Update, CCT, object]] = [] all_handlers.extend(entry_points) all_handlers.extend(fallbacks) @@ -466,8 +453,8 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): ) @property - def entry_points(self) -> List[BaseHandler[Update, CCT, object]]: - """List[:class:`telegram.ext.BaseHandler`]: A list of :obj:`BaseHandler` objects that can + def entry_points(self) -> list[BaseHandler[Update, CCT, object]]: + """list[:class:`telegram.ext.BaseHandler`]: A list of :obj:`BaseHandler` objects that can trigger the start of the conversation. """ return self._entry_points @@ -479,8 +466,8 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): ) @property - def states(self) -> Dict[object, List[BaseHandler[Update, CCT, object]]]: - """Dict[:obj:`object`, List[:class:`telegram.ext.BaseHandler`]]: A :obj:`dict` that + def states(self) -> dict[object, list[BaseHandler[Update, CCT, object]]]: + """dict[:obj:`object`, list[:class:`telegram.ext.BaseHandler`]]: A :obj:`dict` that defines the different states of conversation a user can be in and one or more associated :obj:`BaseHandler` objects that should be used in that state. """ @@ -491,8 +478,8 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): raise AttributeError("You can not assign a new value to states after initialization.") @property - def fallbacks(self) -> List[BaseHandler[Update, CCT, object]]: - """List[:class:`telegram.ext.BaseHandler`]: A list of handlers that might be used if + def fallbacks(self) -> list[BaseHandler[Update, CCT, object]]: + """list[:class:`telegram.ext.BaseHandler`]: A list of handlers that might be used if the user is in a conversation, but every handler for their current state returned :obj:`False` on :meth:`check_update`. """ @@ -578,8 +565,8 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): raise AttributeError("You can not assign a new value to persistent after initialization.") @property - def map_to_parent(self) -> Optional[Dict[object, object]]: - """Dict[:obj:`object`, :obj:`object`]: Optional. A :obj:`dict` that can be + def map_to_parent(self) -> Optional[dict[object, object]]: + """dict[:obj:`object`, :obj:`object`]: Optional. A :obj:`dict` that can be used to instruct a nested :class:`ConversationHandler` to transition into a mapped state on its parent :class:`ConversationHandler` in place of a specified nested state. """ @@ -593,7 +580,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): async def _initialize_persistence( self, application: "Application" - ) -> Dict[str, TrackingDict[ConversationKey, object]]: + ) -> dict[str, TrackingDict[ConversationKey, object]]: """Initializes the persistence for this handler and its child conversations. While this method is marked as protected, we expect it to be called by the Application/parent conversations. It's just protected to hide it from users. @@ -648,7 +635,7 @@ class ConversationHandler(BaseHandler[Update, CCT, object]): chat = update.effective_chat user = update.effective_user - key: List[Union[int, str]] = [] + key: list[Union[int, str]] = [] if self.per_chat: if chat is None: diff --git a/telegram/ext/_handlers/inlinequeryhandler.py b/telegram/ext/_handlers/inlinequeryhandler.py index 9d2cbb710..4c52417f6 100644 --- a/telegram/ext/_handlers/inlinequeryhandler.py +++ b/telegram/ext/_handlers/inlinequeryhandler.py @@ -18,7 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the InlineQueryHandler class.""" import re -from typing import TYPE_CHECKING, Any, List, Match, Optional, Pattern, TypeVar, Union, cast +from re import Match, Pattern +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union, cast from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE @@ -67,7 +68,7 @@ class InlineQueryHandler(BaseHandler[Update, CCT, RT]): :meth:`telegram.ext.Application.process_update`. Defaults to :obj:`True`. .. seealso:: :wiki:`Concurrency` - chat_types (List[:obj:`str`], optional): List of allowed chat types. If passed, will only + chat_types (list[:obj:`str`], optional): List of allowed chat types. If passed, will only handle inline queries with the appropriate :attr:`telegram.InlineQuery.chat_type`. .. versionadded:: 13.5 @@ -75,7 +76,7 @@ class InlineQueryHandler(BaseHandler[Update, CCT, RT]): callback (:term:`coroutine function`): The callback function for this handler. pattern (:obj:`str` | :func:`re.Pattern `): Optional. Regex pattern to test :attr:`telegram.InlineQuery.query` against. - chat_types (List[:obj:`str`]): Optional. List of allowed chat types. + chat_types (list[:obj:`str`]): Optional. List of allowed chat types. .. versionadded:: 13.5 block (:obj:`bool`): Determines whether the return value of the callback should be @@ -91,7 +92,7 @@ class InlineQueryHandler(BaseHandler[Update, CCT, RT]): callback: HandlerCallback[Update, CCT, RT], pattern: Optional[Union[str, Pattern[str]]] = None, block: DVType[bool] = DEFAULT_TRUE, - chat_types: Optional[List[str]] = None, + chat_types: Optional[list[str]] = None, ): super().__init__(callback, block=block) @@ -99,7 +100,7 @@ class InlineQueryHandler(BaseHandler[Update, CCT, RT]): pattern = re.compile(pattern) self.pattern: Optional[Union[str, Pattern[str]]] = pattern - self.chat_types: Optional[List[str]] = chat_types + self.chat_types: Optional[list[str]] = chat_types def check_update(self, update: object) -> Optional[Union[bool, Match[str]]]: """ diff --git a/telegram/ext/_handlers/messagehandler.py b/telegram/ext/_handlers/messagehandler.py index 43d8c8d81..e613f4d36 100644 --- a/telegram/ext/_handlers/messagehandler.py +++ b/telegram/ext/_handlers/messagehandler.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 the MessageHandler class.""" -from typing import TYPE_CHECKING, Any, Dict, List, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE @@ -85,7 +85,7 @@ class MessageHandler(BaseHandler[Update, CCT, RT]): filters if filters is not None else filters_module.ALL ) - def check_update(self, update: object) -> Optional[Union[bool, Dict[str, List[Any]]]]: + def check_update(self, update: object) -> Optional[Union[bool, dict[str, list[Any]]]]: """Determines whether an update should be passed to this handler's :attr:`callback`. Args: @@ -104,7 +104,7 @@ class MessageHandler(BaseHandler[Update, CCT, RT]): context: CCT, update: Update, # noqa: ARG002 application: "Application[Any, CCT, Any, Any, Any, Any]", # noqa: ARG002 - check_result: Optional[Union[bool, Dict[str, object]]], + check_result: Optional[Union[bool, dict[str, object]]], ) -> None: """Adds possible output of data filters to the :class:`CallbackContext`.""" if isinstance(check_result, dict): diff --git a/telegram/ext/_handlers/precheckoutqueryhandler.py b/telegram/ext/_handlers/precheckoutqueryhandler.py index de035364e..265351ed3 100644 --- a/telegram/ext/_handlers/precheckoutqueryhandler.py +++ b/telegram/ext/_handlers/precheckoutqueryhandler.py @@ -20,7 +20,8 @@ import re -from typing import Optional, Pattern, TypeVar, Union +from re import Pattern +from typing import Optional, TypeVar, Union from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE diff --git a/telegram/ext/_handlers/prefixhandler.py b/telegram/ext/_handlers/prefixhandler.py index bda265e10..c68c6fd8c 100644 --- a/telegram/ext/_handlers/prefixhandler.py +++ b/telegram/ext/_handlers/prefixhandler.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the PrefixHandler class.""" import itertools -from typing import TYPE_CHECKING, Any, Dict, FrozenSet, List, Optional, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union from telegram import Update from telegram._utils.defaultvalue import DEFAULT_TRUE @@ -108,7 +108,7 @@ class PrefixHandler(BaseHandler[Update, CCT, RT]): .. seealso:: :wiki:`Concurrency` Attributes: - commands (FrozenSet[:obj:`str`]): The commands that this handler will listen for, i.e. the + commands (frozenset[:obj:`str`]): The commands that this handler will listen for, i.e. the combinations of :paramref:`prefix` and :paramref:`command`. callback (:term:`coroutine function`): The callback function for this handler. filters (:class:`telegram.ext.filters.BaseFilter`): Optional. Only allow updates with these @@ -136,7 +136,7 @@ class PrefixHandler(BaseHandler[Update, CCT, RT]): commands = {command.lower()} if isinstance(command, str) else {x.lower() for x in command} - self.commands: FrozenSet[str] = frozenset( + self.commands: frozenset[str] = frozenset( p + c for p, c in itertools.product(prefixes, commands) ) self.filters: filters_module.BaseFilter = ( @@ -145,7 +145,7 @@ class PrefixHandler(BaseHandler[Update, CCT, RT]): def check_update( self, update: object - ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict[Any, Any]]]]]]: + ) -> Optional[Union[bool, tuple[list[str], Optional[Union[bool, dict[Any, Any]]]]]]: """Determines whether an update should be passed to this handler's :attr:`callback`. Args: @@ -173,7 +173,7 @@ class PrefixHandler(BaseHandler[Update, CCT, RT]): context: CCT, update: Update, # noqa: ARG002 application: "Application[Any, CCT, Any, Any, Any, Any]", # noqa: ARG002 - check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]], + check_result: Optional[Union[bool, tuple[list[str], Optional[bool]]]], ) -> None: """Add text after the command to :attr:`CallbackContext.args` as list, split on single whitespaces and add output of data filters to :attr:`CallbackContext` as well. diff --git a/telegram/ext/_handlers/stringcommandhandler.py b/telegram/ext/_handlers/stringcommandhandler.py index ff655d9d6..22d34c555 100644 --- a/telegram/ext/_handlers/stringcommandhandler.py +++ b/telegram/ext/_handlers/stringcommandhandler.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the StringCommandHandler class.""" -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, Optional from telegram._utils.defaultvalue import DEFAULT_TRUE from telegram._utils.types import DVType @@ -79,14 +79,14 @@ class StringCommandHandler(BaseHandler[str, CCT, RT]): super().__init__(callback, block=block) self.command: str = command - def check_update(self, update: object) -> Optional[List[str]]: + def check_update(self, update: object) -> Optional[list[str]]: """Determines whether an update should be passed to this handler's :attr:`callback`. Args: update (:obj:`object`): The incoming update. Returns: - List[:obj:`str`]: List containing the text command split on whitespace. + list[:obj:`str`]: List containing the text command split on whitespace. """ if isinstance(update, str) and update.startswith("/"): @@ -100,7 +100,7 @@ class StringCommandHandler(BaseHandler[str, CCT, RT]): context: CCT, update: str, # noqa: ARG002 application: "Application[Any, CCT, Any, Any, Any, Any]", # noqa: ARG002 - check_result: Optional[List[str]], + check_result: Optional[list[str]], ) -> None: """Add text after the command to :attr:`CallbackContext.args` as list, split on single whitespaces. diff --git a/telegram/ext/_handlers/stringregexhandler.py b/telegram/ext/_handlers/stringregexhandler.py index bd9746949..f5c0c64a8 100644 --- a/telegram/ext/_handlers/stringregexhandler.py +++ b/telegram/ext/_handlers/stringregexhandler.py @@ -19,7 +19,8 @@ """This module contains the StringRegexHandler class.""" import re -from typing import TYPE_CHECKING, Any, Match, Optional, Pattern, TypeVar, Union +from re import Match, Pattern +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union from telegram._utils.defaultvalue import DEFAULT_TRUE from telegram._utils.types import DVType diff --git a/telegram/ext/_handlers/typehandler.py b/telegram/ext/_handlers/typehandler.py index 48a4530bc..f473f2a5f 100644 --- a/telegram/ext/_handlers/typehandler.py +++ b/telegram/ext/_handlers/typehandler.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the TypeHandler class.""" -from typing import Optional, Type, TypeVar +from typing import Optional, TypeVar from telegram._utils.defaultvalue import DEFAULT_TRUE from telegram._utils.types import DVType @@ -27,6 +27,9 @@ from telegram.ext._utils.types import CCT, HandlerCallback RT = TypeVar("RT") UT = TypeVar("UT") +# If this is written directly next to the type variable mypy gets confused with [valid-type]. This +# could be reported to them, but I doubt they would change this since we override a builtin type +GenericUT = type[UT] class TypeHandler(BaseHandler[UT, CCT, RT]): @@ -71,13 +74,13 @@ class TypeHandler(BaseHandler[UT, CCT, RT]): def __init__( self: "TypeHandler[UT, CCT, RT]", - type: Type[UT], # pylint: disable=redefined-builtin + type: GenericUT[UT], # pylint: disable=redefined-builtin callback: HandlerCallback[UT, CCT, RT], strict: bool = False, block: DVType[bool] = DEFAULT_TRUE, ): super().__init__(callback, block=block) - self.type: Type[UT] = type + self.type: GenericUT[UT] = type self.strict: Optional[bool] = strict def check_update(self, update: object) -> bool: diff --git a/telegram/ext/_jobqueue.py b/telegram/ext/_jobqueue.py index db84841d5..b73d0545e 100644 --- a/telegram/ext/_jobqueue.py +++ b/telegram/ext/_jobqueue.py @@ -20,7 +20,7 @@ import asyncio import datetime import weakref -from typing import TYPE_CHECKING, Any, Generic, Optional, Tuple, Union, cast, overload +from typing import TYPE_CHECKING, Any, Generic, Optional, Union, cast, overload try: import pytz @@ -152,7 +152,7 @@ class JobQueue(Generic[CCT]): .. versionadded:: 20.7 Returns: - Dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary. + dict[:obj:`str`, :obj:`object`]: The configuration values as dictionary. """ timezone: object = pytz.utc @@ -532,7 +532,7 @@ class JobQueue(Generic[CCT]): self, callback: JobCallback[CCT], time: datetime.time, - days: Tuple[int, ...] = _ALL_DAYS, + days: tuple[int, ...] = _ALL_DAYS, data: Optional[object] = None, name: Optional[str] = None, chat_id: Optional[int] = None, @@ -556,7 +556,7 @@ class JobQueue(Generic[CCT]): time (:obj:`datetime.time`): Time of day at which the job should run. If the timezone (:obj:`datetime.time.tzinfo`) is :obj:`None`, the default timezone of the bot will be used, which is UTC unless :attr:`telegram.ext.Defaults.tzinfo` is used. - days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should + days (tuple[:obj:`int`], optional): Defines on which days of the week the job should run (where ``0-6`` correspond to sunday - saturday). By default, the job will run every day. @@ -693,20 +693,20 @@ class JobQueue(Generic[CCT]): # so give it a tiny bit of time to actually shut down. await asyncio.sleep(0.01) - def jobs(self) -> Tuple["Job[CCT]", ...]: + def jobs(self) -> tuple["Job[CCT]", ...]: """Returns a tuple of all *scheduled* jobs that are currently in the :class:`JobQueue`. Returns: - Tuple[:class:`Job`]: Tuple of all *scheduled* jobs. + tuple[:class:`Job`]: Tuple of all *scheduled* jobs. """ return tuple(Job.from_aps_job(job) for job in self.scheduler.get_jobs()) - def get_jobs_by_name(self, name: str) -> Tuple["Job[CCT]", ...]: + def get_jobs_by_name(self, name: str) -> tuple["Job[CCT]", ...]: """Returns a tuple of all *pending/scheduled* jobs with the given name that are currently in the :class:`JobQueue`. Returns: - Tuple[:class:`Job`]: Tuple of all *pending* or *scheduled* jobs matching the name. + tuple[:class:`Job`]: Tuple of all *pending* or *scheduled* jobs matching the name. """ return tuple(job for job in self.jobs() if job.name == name) diff --git a/telegram/ext/_picklepersistence.py b/telegram/ext/_picklepersistence.py index adc8220af..a0d96ce84 100644 --- a/telegram/ext/_picklepersistence.py +++ b/telegram/ext/_picklepersistence.py @@ -20,7 +20,7 @@ import pickle from copy import deepcopy from pathlib import Path -from typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, Union, cast, overload +from typing import Any, Callable, Optional, TypeVar, Union, cast, overload from telegram import Bot, TelegramObject from telegram._utils.types import FilePathInput @@ -35,7 +35,7 @@ _REPLACED_UNKNOWN_BOT = "an unknown bot replaced by PTB's PicklePersistence" TelegramObj = TypeVar("TelegramObj", bound=TelegramObject) -def _all_subclasses(cls: Type[TelegramObj]) -> Set[Type[TelegramObj]]: +def _all_subclasses(cls: type[TelegramObj]) -> set[type[TelegramObj]]: """Gets all subclasses of the specified object, recursively. from https://stackoverflow.com/a/3862957/9706202 """ @@ -43,7 +43,7 @@ def _all_subclasses(cls: Type[TelegramObj]) -> Set[Type[TelegramObj]]: return set(subclasses).union([s for c in subclasses for s in _all_subclasses(c)]) -def _reconstruct_to(cls: Type[TelegramObj], kwargs: dict) -> TelegramObj: +def _reconstruct_to(cls: type[TelegramObj], kwargs: dict) -> TelegramObj: """ This method is used for unpickling. The data, which is in the form a dictionary, is converted back into a class. Works mostly the same as :meth:`TelegramObject.__setstate__`. @@ -55,7 +55,7 @@ def _reconstruct_to(cls: Type[TelegramObj], kwargs: dict) -> TelegramObj: return obj -def _custom_reduction(cls: TelegramObj) -> Tuple[Callable, Tuple[Type[TelegramObj], dict]]: +def _custom_reduction(cls: TelegramObj) -> tuple[Callable, tuple[type[TelegramObj], dict]]: """ This method is used for pickling. The bot attribute is preserved so _BotPickler().persistent_id works as intended. @@ -76,7 +76,7 @@ class _BotPickler(pickle.Pickler): def reducer_override( self, obj: TelegramObj - ) -> Tuple[Callable, Tuple[Type[TelegramObj], dict]]: + ) -> tuple[Callable, tuple[type[TelegramObj], dict]]: """ This method is used for pickling. The bot attribute is preserved so _BotPickler().persistent_id works as intended. @@ -199,7 +199,7 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): @overload def __init__( - self: "PicklePersistence[Dict[Any, Any], Dict[Any, Any], Dict[Any, Any]]", + self: "PicklePersistence[dict[Any, Any], dict[Any, Any], dict[Any, Any]]", filepath: FilePathInput, store_data: Optional[PersistenceInput] = None, single_file: bool = True, @@ -231,11 +231,11 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): self.filepath: Path = Path(filepath) self.single_file: Optional[bool] = single_file self.on_flush: Optional[bool] = on_flush - self.user_data: Optional[Dict[int, UD]] = None - self.chat_data: Optional[Dict[int, CD]] = None + self.user_data: Optional[dict[int, UD]] = None + self.chat_data: Optional[dict[int, CD]] = None self.bot_data: Optional[BD] = None self.callback_data: Optional[CDCData] = None - self.conversations: Optional[Dict[str, Dict[Tuple[Union[int, str], ...], object]]] = None + self.conversations: Optional[dict[str, dict[tuple[Union[int, str], ...], object]]] = None self.context_types: ContextTypes[Any, UD, CD, BD] = cast( ContextTypes[Any, UD, CD, BD], context_types or ContextTypes() ) @@ -290,11 +290,11 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): with filepath.open("wb") as file: _BotPickler(self.bot, file, protocol=pickle.HIGHEST_PROTOCOL).dump(data) - async def get_user_data(self) -> Dict[int, UD]: + async def get_user_data(self) -> dict[int, UD]: """Returns the user_data from the pickle file if it exists or an empty :obj:`dict`. Returns: - Dict[:obj:`int`, :obj:`dict`]: The restored user data. + dict[:obj:`int`, :obj:`dict`]: The restored user data. """ if self.user_data: pass @@ -307,11 +307,11 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): self._load_singlefile() return deepcopy(self.user_data) # type: ignore[arg-type] - async def get_chat_data(self) -> Dict[int, CD]: + async def get_chat_data(self) -> dict[int, CD]: """Returns the chat_data from the pickle file if it exists or an empty :obj:`dict`. Returns: - Dict[:obj:`int`, :obj:`dict`]: The restored chat data. + dict[:obj:`int`, :obj:`dict`]: The restored chat data. """ if self.chat_data: pass @@ -348,8 +348,8 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): .. versionadded:: 13.6 Returns: - Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], - Dict[:obj:`str`, :obj:`str`]] | :obj:`None`: The restored metadata or :obj:`None`, + tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], + dict[:obj:`str`, :obj:`str`]] | :obj:`None`: The restored metadata or :obj:`None`, if no data was stored. """ if self.callback_data: @@ -466,8 +466,8 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): .. versionadded:: 13.6 Args: - data (Tuple[List[Tuple[:obj:`str`, :obj:`float`, \ - Dict[:obj:`str`, :class:`object`]]], Dict[:obj:`str`, :obj:`str`]]): + data (tuple[list[tuple[:obj:`str`, :obj:`float`, \ + dict[:obj:`str`, :class:`object`]]], dict[:obj:`str`, :obj:`str`]]): The relevant data to restore :class:`telegram.ext.CallbackDataCache`. """ if self.callback_data == data: diff --git a/telegram/ext/_updater.py b/telegram/ext/_updater.py index 30635e40a..96bc6a3ed 100644 --- a/telegram/ext/_updater.py +++ b/telegram/ext/_updater.py @@ -20,20 +20,10 @@ import asyncio import contextlib import ssl +from collections.abc import Coroutine from pathlib import Path from types import TracebackType -from typing import ( - TYPE_CHECKING, - Any, - AsyncContextManager, - Callable, - Coroutine, - List, - Optional, - Type, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union from telegram._utils.defaultvalue import DEFAULT_80, DEFAULT_IP, DEFAULT_NONE, DefaultValue from telegram._utils.logging import get_logger @@ -58,7 +48,7 @@ _UpdaterType = TypeVar("_UpdaterType", bound="Updater") # pylint: disable=inval _LOGGER = get_logger(__name__) -class Updater(AsyncContextManager["Updater"]): +class Updater(contextlib.AbstractAsyncContextManager["Updater"]): """This class fetches updates for the bot either via long polling or by starting a webhook server. Received updates are enqueued into the :attr:`update_queue` and may be fetched from there to handle them appropriately. @@ -152,7 +142,7 @@ class Updater(AsyncContextManager["Updater"]): async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: @@ -220,7 +210,7 @@ class Updater(AsyncContextManager["Updater"]): write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - allowed_updates: Optional[List[str]] = None, + allowed_updates: Optional[list[str]] = None, drop_pending_updates: Optional[bool] = None, error_callback: Optional[Callable[[TelegramError], None]] = None, ) -> "asyncio.Queue[object]": @@ -275,7 +265,7 @@ class Updater(AsyncContextManager["Updater"]): Deprecated in favor of setting the timeout via :meth:`telegram.ext.ApplicationBuilder.get_updates_pool_timeout` or :paramref:`telegram.Bot.get_updates_request`. - allowed_updates (List[:obj:`str`], optional): Passed to + allowed_updates (list[:obj:`str`], optional): Passed to :meth:`telegram.Bot.get_updates`. drop_pending_updates (:obj:`bool`, optional): Whether to clean any pending updates on Telegram servers before actually starting to poll. Default is :obj:`False`. @@ -354,7 +344,7 @@ class Updater(AsyncContextManager["Updater"]): pool_timeout: ODVInput[float], bootstrap_retries: int, drop_pending_updates: Optional[bool], - allowed_updates: Optional[List[str]], + allowed_updates: Optional[list[str]], ready: asyncio.Event, error_callback: Optional[Callable[[TelegramError], None]], ) -> None: @@ -467,7 +457,7 @@ class Updater(AsyncContextManager["Updater"]): key: Optional[Union[str, Path]] = None, bootstrap_retries: int = 0, webhook_url: Optional[str] = None, - allowed_updates: Optional[List[str]] = None, + allowed_updates: Optional[list[str]] = None, drop_pending_updates: Optional[bool] = None, ip_address: Optional[str] = None, max_connections: int = 40, @@ -526,7 +516,7 @@ class Updater(AsyncContextManager["Updater"]): Defaults to :obj:`None`. .. versionadded :: 13.4 - allowed_updates (List[:obj:`str`], optional): Passed to + allowed_updates (list[:obj:`str`], optional): Passed to :meth:`telegram.Bot.set_webhook`. Defaults to :obj:`None`. max_connections (:obj:`int`, optional): Passed to :meth:`telegram.Bot.set_webhook`. Defaults to ``40``. @@ -634,7 +624,7 @@ class Updater(AsyncContextManager["Updater"]): port: int, url_path: str, bootstrap_retries: int, - allowed_updates: Optional[List[str]], + allowed_updates: Optional[list[str]], cert: Optional[Union[str, Path]] = None, key: Optional[Union[str, Path]] = None, drop_pending_updates: Optional[bool] = None, @@ -777,7 +767,7 @@ class Updater(AsyncContextManager["Updater"]): self, max_retries: int, webhook_url: Optional[str], - allowed_updates: Optional[List[str]], + allowed_updates: Optional[list[str]], drop_pending_updates: Optional[bool] = None, cert: Optional[bytes] = None, bootstrap_interval: float = 1, diff --git a/telegram/ext/_utils/_update_parsing.py b/telegram/ext/_utils/_update_parsing.py index f74c35e8c..7bc4c4989 100644 --- a/telegram/ext/_utils/_update_parsing.py +++ b/telegram/ext/_utils/_update_parsing.py @@ -25,12 +25,12 @@ Warning: user. Changes to this module are not considered breaking changes and may not be documented in the changelog. """ -from typing import FrozenSet, Optional +from typing import Optional from telegram._utils.types import SCT -def parse_chat_id(chat_id: Optional[SCT[int]]) -> FrozenSet[int]: +def parse_chat_id(chat_id: Optional[SCT[int]]) -> frozenset[int]: """Accepts a chat id or collection of chat ids and returns a frozenset of chat ids.""" if chat_id is None: return frozenset() @@ -39,12 +39,12 @@ def parse_chat_id(chat_id: Optional[SCT[int]]) -> FrozenSet[int]: return frozenset(chat_id) -def parse_username(username: Optional[SCT[str]]) -> FrozenSet[str]: +def parse_username(username: Optional[SCT[str]]) -> frozenset[str]: """Accepts a username or collection of usernames and returns a frozenset of usernames. Strips the leading ``@`` if present. """ if username is None: return frozenset() if isinstance(username, str): - return frozenset({username[1:] if username.startswith("@") else username}) - return frozenset({usr[1:] if usr.startswith("@") else usr for usr in username}) + return frozenset({username.removeprefix("@")}) + return frozenset(usr.removeprefix("@") for usr in username) diff --git a/telegram/ext/_utils/trackingdict.py b/telegram/ext/_utils/trackingdict.py index 682cd6486..05ca5122b 100644 --- a/telegram/ext/_utils/trackingdict.py +++ b/telegram/ext/_utils/trackingdict.py @@ -26,7 +26,8 @@ Warning: the changelog. """ from collections import UserDict -from typing import Final, Generic, List, Mapping, Optional, Set, Tuple, TypeVar, Union +from collections.abc import Mapping +from typing import Final, Generic, Optional, TypeVar, Union from telegram._utils.defaultvalue import DEFAULT_NONE, DefaultValue @@ -52,7 +53,7 @@ class TrackingDict(UserDict, Generic[_KT, _VT]): def __init__(self) -> None: super().__init__() - self._write_access_keys: Set[_KT] = set() + self._write_access_keys: set[_KT] = set() def __setitem__(self, key: _KT, value: _VT) -> None: self.__track_write(key) @@ -62,19 +63,19 @@ class TrackingDict(UserDict, Generic[_KT, _VT]): self.__track_write(key) super().__delitem__(key) - def __track_write(self, key: Union[_KT, Set[_KT]]) -> None: + def __track_write(self, key: Union[_KT, set[_KT]]) -> None: if isinstance(key, set): self._write_access_keys |= key else: self._write_access_keys.add(key) - def pop_accessed_keys(self) -> Set[_KT]: + def pop_accessed_keys(self) -> set[_KT]: """Returns all keys that were write-accessed since the last time this method was called.""" out = self._write_access_keys self._write_access_keys = set() return out - def pop_accessed_write_items(self) -> List[Tuple[_KT, _VT]]: + def pop_accessed_write_items(self) -> list[tuple[_KT, _VT]]: """ Returns all keys & corresponding values as set of tuples that were write-accessed since the last time this method was called. If a key was deleted, the value will be diff --git a/telegram/ext/_utils/types.py b/telegram/ext/_utils/types.py index 5f9fc0832..62393355f 100644 --- a/telegram/ext/_utils/types.py +++ b/telegram/ext/_utils/types.py @@ -25,18 +25,8 @@ Warning: user. Changes to this module are not considered breaking changes and may not be documented in the changelog. """ -from typing import ( - TYPE_CHECKING, - Any, - Callable, - Coroutine, - Dict, - List, - MutableMapping, - Tuple, - TypeVar, - Union, -) +from collections.abc import Coroutine, MutableMapping +from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union if TYPE_CHECKING: from typing import Optional @@ -63,17 +53,17 @@ JobCallback = Callable[[CCT], Coroutine[Any, Any, Any]] .. versionadded:: 20.0 """ -ConversationKey = Tuple[Union[int, str], ...] +ConversationKey = tuple[Union[int, str], ...] ConversationDict = MutableMapping[ConversationKey, object] -"""Dict[Tuple[:obj:`int` | :obj:`str`, ...], Optional[:obj:`object`]]: +"""dict[tuple[:obj:`int` | :obj:`str`, ...], Optional[:obj:`object`]]: Dicts as maintained by the :class:`telegram.ext.ConversationHandler`. .. versionadded:: 13.6 """ -CDCData = Tuple[List[Tuple[str, float, Dict[str, Any]]], Dict[str, str]] -"""Tuple[List[Tuple[:obj:`str`, :obj:`float`, Dict[:obj:`str`, :class:`object`]]], \ - Dict[:obj:`str`, :obj:`str`]]: Data returned by +CDCData = tuple[list[tuple[str, float, dict[str, Any]]], dict[str, str]] +"""tuple[list[tuple[:obj:`str`, :obj:`float`, dict[:obj:`str`, :class:`object`]]], \ + dict[:obj:`str`, :obj:`str`]]: Data returned by :attr:`telegram.ext.CallbackDataCache.persistence_data`. .. versionadded:: 13.6 @@ -113,4 +103,4 @@ RLARGS = TypeVar("RLARGS") """Type of the rate limiter arguments. .. versionadded:: 20.0""" -FilterDataDict = Dict[str, List[Any]] +FilterDataDict = dict[str, list[Any]] diff --git a/telegram/ext/_utils/webhookhandler.py b/telegram/ext/_utils/webhookhandler.py index a174fbaa4..d707f9f45 100644 --- a/telegram/ext/_utils/webhookhandler.py +++ b/telegram/ext/_utils/webhookhandler.py @@ -24,7 +24,7 @@ from pathlib import Path from socket import socket from ssl import SSLContext from types import TracebackType -from typing import TYPE_CHECKING, Optional, Type, Union +from typing import TYPE_CHECKING, Optional, Union # Instead of checking for ImportError here, we do that in `updater.py`, where we import from # this module. Doing it here would be tricky, as the classes below subclass tornado classes @@ -210,7 +210,7 @@ class TelegramHandler(tornado.web.RequestHandler): def log_exception( self, - typ: Optional[Type[BaseException]], + typ: Optional[type[BaseException]], value: Optional[BaseException], tb: Optional[TracebackType], ) -> None: diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index fe5b8a79d..7b1f5a45b 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -104,22 +104,9 @@ __all__ = ( import mimetypes import re from abc import ABC, abstractmethod -from typing import ( - Collection, - Dict, - FrozenSet, - Iterable, - List, - Match, - NoReturn, - Optional, - Pattern, - Sequence, - Set, - Tuple, - Union, - cast, -) +from collections.abc import Collection, Iterable, Sequence +from re import Match, Pattern +from typing import NoReturn, Optional, Union, cast from telegram import Chat as TGChat from telegram import ( @@ -320,7 +307,7 @@ class MessageFilter(BaseFilter): update (:class:`telegram.Update`): The update to check. Returns: - :obj:`bool` | Dict[:obj:`str`, :obj:`list`] | :obj:`None`: If the update should be + :obj:`bool` | dict[:obj:`str`, :obj:`list`] | :obj:`None`: If the update should be handled by this filter, returns :obj:`True` or a dict with lists, in case the filter is a data filter. If the update should not be handled by this filter, :obj:`False` or :obj:`None`. @@ -361,7 +348,7 @@ class UpdateFilter(BaseFilter): update (:class:`telegram.Update`): The update to check. Returns: - :obj:`bool` | Dict[:obj:`str`, :obj:`list`] | :obj:`None`: If the update should be + :obj:`bool` | dict[:obj:`str`, :obj:`list`] | :obj:`None`: If the update should be handled by this filter, returns :obj:`True` or a dict with lists, in case the filter is a data filter. If the update should not be handled by this filter, :obj:`False` or :obj:`None`. @@ -441,7 +428,7 @@ class _MergedFilter(UpdateFilter): self.data_filter = True @staticmethod - def _merge(base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> FilterDataDict: + def _merge(base_output: Union[bool, dict], comp_output: Union[bool, dict]) -> FilterDataDict: base = base_output if isinstance(base_output, dict) else {} comp = comp_output if isinstance(comp_output, dict) else {} for k in comp: @@ -585,13 +572,13 @@ class Caption(MessageFilter): :attr:`telegram.ext.filters.CAPTION` Args: - strings (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which captions to allow. Only + strings (list[:obj:`str`] | tuple[:obj:`str`], optional): Which captions to allow. Only exact matches are allowed. If not specified, will allow any message with a caption. """ __slots__ = ("strings",) - def __init__(self, strings: Optional[Union[List[str], Tuple[str, ...]]] = None): + def __init__(self, strings: Optional[Union[list[str], tuple[str, ...]]] = None): self.strings: Optional[Sequence[str]] = strings super().__init__(name=f"filters.Caption({strings})" if strings else "filters.CAPTION") @@ -660,7 +647,7 @@ class CaptionRegex(MessageFilter): self.pattern: Pattern[str] = pattern super().__init__(name=f"filters.CaptionRegex({self.pattern})", data_filter=True) - def filter(self, message: Message) -> Optional[Dict[str, List[Match[str]]]]: + def filter(self, message: Message) -> Optional[dict[str, list[Match[str]]]]: if message.caption and (match := self.pattern.search(message.caption)): return {"matches": [match]} return {} @@ -686,8 +673,8 @@ class _ChatUserBaseFilter(MessageFilter, ABC): self._username_name: str = "username" self.allow_empty: bool = allow_empty - self._chat_ids: Set[int] = set() - self._usernames: Set[str] = set() + self._chat_ids: set[int] = set() + self._usernames: set[str] = set() self._set_chat_ids(chat_id) self._set_usernames(username) @@ -712,7 +699,7 @@ class _ChatUserBaseFilter(MessageFilter, ABC): self._usernames = set(parse_username(username)) @property - def chat_ids(self) -> FrozenSet[int]: + def chat_ids(self) -> frozenset[int]: return frozenset(self._chat_ids) @chat_ids.setter @@ -720,7 +707,7 @@ class _ChatUserBaseFilter(MessageFilter, ABC): self._set_chat_ids(chat_id) @property - def usernames(self) -> FrozenSet[str]: + def usernames(self) -> frozenset[str]: """Which username(s) to allow through. Warning: @@ -1617,7 +1604,7 @@ class Language(MessageFilter): lang = cast(str, lang) self.lang: Sequence[str] = [lang] else: - lang = cast(List[str], lang) + lang = cast(list[str], lang) self.lang = lang super().__init__(name=f"filters.Language({self.lang})") @@ -1795,7 +1782,7 @@ class Regex(MessageFilter): self.pattern: Pattern[str] = pattern super().__init__(name=f"filters.Regex({self.pattern})", data_filter=True) - def filter(self, message: Message) -> Optional[Dict[str, List[Match[str]]]]: + def filter(self, message: Message) -> Optional[dict[str, list[Match[str]]]]: if message.text and (match := self.pattern.search(message.text)): return {"matches": [match]} return {} @@ -2440,7 +2427,7 @@ class SuccessfulPayment(MessageFilter): :attr:`telegram.ext.filters.SUCCESSFUL_PAYMENT` Args: - invoice_payloads (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which + invoice_payloads (list[:obj:`str`] | tuple[:obj:`str`], optional): Which invoice payloads to allow. Only exact matches are allowed. If not specified, will allow any invoice payload. @@ -2449,7 +2436,7 @@ class SuccessfulPayment(MessageFilter): __slots__ = ("invoice_payloads",) - def __init__(self, invoice_payloads: Optional[Union[List[str], Tuple[str, ...]]] = None): + def __init__(self, invoice_payloads: Optional[Union[list[str], tuple[str, ...]]] = None): self.invoice_payloads: Optional[Sequence[str]] = invoice_payloads super().__init__( name=( @@ -2498,13 +2485,13 @@ class Text(MessageFilter): commands. Args: - strings (List[:obj:`str`] | Tuple[:obj:`str`], optional): Which messages to allow. Only + strings (list[:obj:`str`] | tuple[:obj:`str`], optional): Which messages to allow. Only exact matches are allowed. If not specified, will allow any text message. """ __slots__ = ("strings",) - def __init__(self, strings: Optional[Union[List[str], Tuple[str, ...]]] = None): + def __init__(self, strings: Optional[Union[list[str], tuple[str, ...]]] = None): self.strings: Optional[Sequence[str]] = strings super().__init__(name=f"filters.Text({strings})" if strings else "filters.TEXT") @@ -2694,7 +2681,7 @@ class User(_ChatUserBaseFilter): return message.from_user @property - def user_ids(self) -> FrozenSet[int]: + def user_ids(self) -> frozenset[int]: """ Which user ID(s) to allow through. @@ -2832,7 +2819,7 @@ class ViaBot(_ChatUserBaseFilter): return message.via_bot @property - def bot_ids(self) -> FrozenSet[int]: + def bot_ids(self) -> frozenset[int]: """ Which bot ID(s) to allow through. diff --git a/telegram/request/_baserequest.py b/telegram/request/_baserequest.py index aa0fe232c..446315a5b 100644 --- a/telegram/request/_baserequest.py +++ b/telegram/request/_baserequest.py @@ -19,9 +19,10 @@ """This module contains an abstract class to make POST and GET requests.""" import abc import json +from contextlib import AbstractAsyncContextManager from http import HTTPStatus from types import TracebackType -from typing import AsyncContextManager, Final, List, Optional, Tuple, Type, TypeVar, Union, final +from typing import Final, Optional, TypeVar, Union, final from telegram._utils.defaultvalue import DEFAULT_NONE as _DEFAULT_NONE from telegram._utils.defaultvalue import DefaultValue @@ -49,7 +50,7 @@ _LOGGER = get_logger(__name__, class_name="BaseRequest") class BaseRequest( - AsyncContextManager["BaseRequest"], + AbstractAsyncContextManager["BaseRequest"], abc.ABC, ): """Abstract interface class that allows python-telegram-bot to make requests to the Bot API. @@ -122,7 +123,7 @@ class BaseRequest( async def __aexit__( self, - exc_type: Optional[Type[BaseException]], + exc_type: Optional[type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: @@ -166,7 +167,7 @@ class BaseRequest( write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Union[JSONDict, List[JSONDict], bool]: + ) -> Union[JSONDict, list[JSONDict], bool]: """Makes a request to the Bot API handles the return code and parses the answer. Warning: @@ -421,7 +422,7 @@ class BaseRequest( write_timeout: ODVInput[float] = DEFAULT_NONE, connect_timeout: ODVInput[float] = DEFAULT_NONE, pool_timeout: ODVInput[float] = DEFAULT_NONE, - ) -> Tuple[int, bytes]: + ) -> tuple[int, bytes]: """Makes a request to the Bot API. Must be implemented by a subclass. Warning: @@ -451,6 +452,6 @@ class BaseRequest( :attr:`DEFAULT_NONE`. Returns: - Tuple[:obj:`int`, :obj:`bytes`]: The HTTP return code & the payload part of the server + tuple[:obj:`int`, :obj:`bytes`]: The HTTP return code & the payload part of the server response. """ diff --git a/telegram/request/_httpxrequest.py b/telegram/request/_httpxrequest.py index a2e13582d..08c2bfcc5 100644 --- a/telegram/request/_httpxrequest.py +++ b/telegram/request/_httpxrequest.py @@ -17,7 +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 methods to make POST and GET requests using the httpx library.""" -from typing import Any, Collection, Dict, Optional, Tuple, Union +from collections.abc import Collection +from typing import Any, Optional, Union import httpx @@ -122,7 +123,7 @@ class HTTPXRequest(BaseRequest): :meth:`do_request`. Defaults to ``20`` seconds. .. versionadded:: 21.0 - httpx_kwargs (Dict[:obj:`str`, Any], optional): Additional keyword arguments to be passed + httpx_kwargs (dict[:obj:`str`, Any], optional): Additional keyword arguments to be passed to the `httpx.AsyncClient `_ constructor. @@ -153,7 +154,7 @@ class HTTPXRequest(BaseRequest): socket_options: Optional[Collection[SocketOpt]] = None, proxy: Optional[Union[str, httpx.Proxy, httpx.URL]] = None, media_write_timeout: Optional[float] = 20.0, - httpx_kwargs: Optional[Dict[str, Any]] = None, + httpx_kwargs: Optional[dict[str, Any]] = None, ): if proxy_url is not None and proxy is not None: raise ValueError("The parameters `proxy_url` and `proxy` are mutually exclusive.") @@ -261,7 +262,7 @@ class HTTPXRequest(BaseRequest): write_timeout: ODVInput[float] = BaseRequest.DEFAULT_NONE, connect_timeout: ODVInput[float] = BaseRequest.DEFAULT_NONE, pool_timeout: ODVInput[float] = BaseRequest.DEFAULT_NONE, - ) -> Tuple[int, bytes]: + ) -> tuple[int, bytes]: """See :meth:`BaseRequest.do_request`.""" if self._client.is_closed: raise RuntimeError("This HTTPXRequest is not initialized!") diff --git a/telegram/request/_requestdata.py b/telegram/request/_requestdata.py index 71b2654e5..9e89f0090 100644 --- a/telegram/request/_requestdata.py +++ b/telegram/request/_requestdata.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains a class that holds the parameters of a request to the Bot API.""" import json -from typing import Any, Dict, List, Optional, Union, final +from typing import Any, Optional, Union, final from urllib.parse import urlencode from telegram._utils.strings import TextEncoding @@ -45,19 +45,19 @@ class RequestData: __slots__ = ("_parameters", "contains_files") - def __init__(self, parameters: Optional[List[RequestParameter]] = None): - self._parameters: List[RequestParameter] = parameters or [] + def __init__(self, parameters: Optional[list[RequestParameter]] = None): + self._parameters: list[RequestParameter] = parameters or [] self.contains_files: bool = any(param.input_files for param in self._parameters) @property - def parameters(self) -> Dict[str, Union[str, int, List[Any], Dict[Any, Any]]]: + def parameters(self) -> dict[str, Union[str, int, list[Any], dict[Any, Any]]]: """Gives the parameters as mapping of parameter name to the parameter value, which can be a single object of type :obj:`int`, :obj:`float`, :obj:`str` or :obj:`bool` or any (possibly nested) composition of lists, tuples and dictionaries, where each entry, key and value is of one of the mentioned types. Returns: - Dict[:obj:`str`, Union[:obj:`str`, :obj:`int`, List[any], Dict[any, any]]] + dict[:obj:`str`, Union[:obj:`str`, :obj:`int`, list[any], dict[any, any]]] """ return { param.name: param.value # type: ignore[misc] @@ -66,7 +66,7 @@ class RequestData: } @property - def json_parameters(self) -> Dict[str, str]: + def json_parameters(self) -> dict[str, str]: """Gives the parameters as mapping of parameter name to the respective JSON encoded value. @@ -76,7 +76,7 @@ class RequestData: :attr:`parameters` - note that string valued keys should not be JSON encoded. Returns: - Dict[:obj:`str`, :obj:`str`] + dict[:obj:`str`, :obj:`str`] """ return { param.name: param.json_value @@ -84,11 +84,11 @@ class RequestData: if param.json_value is not None } - def url_encoded_parameters(self, encode_kwargs: Optional[Dict[str, Any]] = None) -> str: + def url_encoded_parameters(self, encode_kwargs: Optional[dict[str, Any]] = None) -> str: """Encodes the parameters with :func:`urllib.parse.urlencode`. Args: - encode_kwargs (Dict[:obj:`str`, any], optional): Additional keyword arguments to pass + encode_kwargs (dict[:obj:`str`, any], optional): Additional keyword arguments to pass along to :func:`urllib.parse.urlencode`. Returns: @@ -98,13 +98,13 @@ class RequestData: return urlencode(self.json_parameters, **encode_kwargs) return urlencode(self.json_parameters) - def parametrized_url(self, url: str, encode_kwargs: Optional[Dict[str, Any]] = None) -> str: + def parametrized_url(self, url: str, encode_kwargs: Optional[dict[str, Any]] = None) -> str: """Shortcut for attaching the return value of :meth:`url_encoded_parameters` to the :paramref:`url`. Args: url (:obj:`str`): The URL the parameters will be attached to. - encode_kwargs (Dict[:obj:`str`, any], optional): Additional keyword arguments to pass + encode_kwargs (dict[:obj:`str`, any], optional): Additional keyword arguments to pass along to :func:`urllib.parse.urlencode`. Returns: diff --git a/telegram/request/_requestparameter.py b/telegram/request/_requestparameter.py index 88ed231c0..311b37ff3 100644 --- a/telegram/request/_requestparameter.py +++ b/telegram/request/_requestparameter.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains a class that describes a single parameter of a request to the Bot API.""" import json +from collections.abc import Sequence from dataclasses import dataclass from datetime import datetime -from typing import List, Optional, Sequence, Tuple, final +from typing import Optional, final from telegram._files.inputfile import InputFile from telegram._files.inputmedia import InputMedia, InputPaidMedia @@ -47,13 +48,13 @@ class RequestParameter: Args: name (:obj:`str`): The name of the parameter. value (:obj:`object` | :obj:`None`): The value of the parameter. Must be JSON-dumpable. - input_files (List[:class:`telegram.InputFile`], optional): A list of files that should be + input_files (list[:class:`telegram.InputFile`], optional): A list of files that should be uploaded along with this parameter. Attributes: name (:obj:`str`): The name of the parameter. value (:obj:`object` | :obj:`None`): The value of the parameter. - input_files (List[:class:`telegram.InputFile` | :obj:`None`): A list of files that should + input_files (list[:class:`telegram.InputFile` | :obj:`None`): A list of files that should be uploaded along with this parameter. """ @@ -61,7 +62,7 @@ class RequestParameter: name: str value: object - input_files: Optional[List[InputFile]] + input_files: Optional[list[InputFile]] @property def json_value(self) -> Optional[str]: @@ -92,7 +93,7 @@ class RequestParameter: @staticmethod def _value_and_input_files_from_input( # pylint: disable=too-many-return-statements value: object, - ) -> Tuple[object, List[InputFile]]: + ) -> tuple[object, list[InputFile]]: """Converts `value` into something that we can json-dump. Returns two values: 1. the JSON-dumpable value. May be `None` in case the value is an InputFile which must not be uploaded via an attach:// URI diff --git a/tests/auxil/bot_method_checks.py b/tests/auxil/bot_method_checks.py index 7b69863b1..a498693ce 100644 --- a/tests/auxil/bot_method_checks.py +++ b/tests/auxil/bot_method_checks.py @@ -21,7 +21,8 @@ import datetime import functools import inspect import re -from typing import Any, Callable, Collection, Dict, Iterable, List, Optional, Tuple +from collections.abc import Collection, Iterable +from typing import Any, Callable, Optional import pytest @@ -57,9 +58,9 @@ Class name (in a named group) is surrounded by parentheses and single quotes. def check_shortcut_signature( shortcut: Callable, bot_method: Callable, - shortcut_kwargs: List[str], - additional_kwargs: List[str], - annotation_overrides: Optional[Dict[str, Tuple[Any, Any]]] = None, + shortcut_kwargs: list[str], + additional_kwargs: list[str], + annotation_overrides: Optional[dict[str, tuple[Any, Any]]] = None, ) -> bool: """ Checks that the signature of a shortcut matches the signature of the underlying bot method. @@ -389,7 +390,7 @@ async def make_assertion( url, request_data: RequestData, method_name: str, - kwargs_need_default: List[str], + kwargs_need_default: list[str], return_value, manually_passed_value: Any = DEFAULT_NONE, expected_defaults_value: Any = DEFAULT_NONE, @@ -451,7 +452,7 @@ async def make_assertion( ) # Check InputMedia (parse_mode can have a default) - def check_input_media(m: Dict): + def check_input_media(m: dict): parse_mode = m.get("parse_mode") if no_value_expected and parse_mode is not None: pytest.fail("InputMedia has non-None parse_mode, expected it to be absent") diff --git a/tests/auxil/networking.py b/tests/auxil/networking.py index a695eb232..d103154f9 100644 --- a/tests/auxil/networking.py +++ b/tests/auxil/networking.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/]. from pathlib import Path -from typing import Optional, Tuple +from typing import Optional import pytest from httpx import AsyncClient, AsyncHTTPTransport, Response @@ -83,7 +83,7 @@ class OfflineRequest(BaseRequest): write_timeout: ODVInput[float] = BaseRequest.DEFAULT_NONE, connect_timeout: ODVInput[float] = BaseRequest.DEFAULT_NONE, pool_timeout: ODVInput[float] = BaseRequest.DEFAULT_NONE, - ) -> Tuple[int, bytes]: + ) -> tuple[int, bytes]: pytest.fail("OfflineRequest: Network access disallowed in this test") diff --git a/tests/conftest.py b/tests/conftest.py index 69c8ce960..70a190096 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -21,7 +21,6 @@ import datetime import logging import sys from pathlib import Path -from typing import Dict, List from uuid import uuid4 import pytest @@ -76,7 +75,7 @@ def no_rerun_after_xfail_or_flood(error, name, test: pytest.Function, plugin): return not (xfail_present or did_we_flood) -def pytest_collection_modifyitems(items: List[pytest.Item]): +def pytest_collection_modifyitems(items: list[pytest.Item]): """Here we add a flaky marker to all request making tests and a (no_)req marker to the rest.""" for item in items: # items are the test methods parent = item.parent # Get the parent of the item (class, or module if defined outside) @@ -145,7 +144,7 @@ def event_loop(request): @pytest.fixture(scope="session") -def bot_info() -> Dict[str, str]: +def bot_info() -> dict[str, str]: return BOT_INFO_PROVIDER.get_info() diff --git a/tests/docs/admonition_inserter.py b/tests/docs/admonition_inserter.py index fa19d9a0f..bc03c7a10 100644 --- a/tests/docs/admonition_inserter.py +++ b/tests/docs/admonition_inserter.py @@ -96,7 +96,7 @@ class TestAdmonitionInserter: ( "available_in", telegram.Sticker, - ":attr:`telegram.StickerSet.stickers`", # Tuple[telegram.Sticker] + ":attr:`telegram.StickerSet.stickers`", # tuple[telegram.Sticker] ), ( "available_in", diff --git a/tests/ext/test_application.py b/tests/ext/test_application.py index 2826f4cad..ec6da310c 100644 --- a/tests/ext/test_application.py +++ b/tests/ext/test_application.py @@ -302,15 +302,19 @@ class TestApplication: ) if updater: - async with ApplicationBuilder().bot(one_time_bot).concurrent_updates( - update_processor - ).build(): + async with ( + ApplicationBuilder().bot(one_time_bot).concurrent_updates(update_processor).build() + ): pass assert self.test_flag == {"bot", "update_processor", "updater"} else: - async with ApplicationBuilder().bot(one_time_bot).updater(None).concurrent_updates( - update_processor - ).build(): + async with ( + ApplicationBuilder() + .bot(one_time_bot) + .updater(None) + .concurrent_updates(update_processor) + .build() + ): pass assert self.test_flag == {"bot", "update_processor"} diff --git a/tests/ext/test_filters.py b/tests/ext/test_filters.py index 76b8dec91..ce728bb7d 100644 --- a/tests/ext/test_filters.py +++ b/tests/ext/test_filters.py @@ -103,7 +103,7 @@ class TestFilters: # The total no. of filters is about 72 as of 31/10/21. # Gather all the filters to test using DFS- visited = [] - classes = inspect.getmembers(filters, predicate=filter_class) # List[Tuple[str, type]] + classes = inspect.getmembers(filters, predicate=filter_class) # list[tuple[str, type]] stack = classes.copy() while stack: cls = stack[-1][-1] # get last element and its class diff --git a/tests/ext/test_picklepersistence.py b/tests/ext/test_picklepersistence.py index ef23715b5..d30b24814 100644 --- a/tests/ext/test_picklepersistence.py +++ b/tests/ext/test_picklepersistence.py @@ -872,9 +872,12 @@ class TestPicklePersistence: "A load persistent id instruction was encountered,\nbut no persistent_load " "function was specified." ) - with Path("pickletest_chat_data").open("rb") as f, pytest.raises( - pickle.UnpicklingError, - match=err_msg if sys.version_info < (3, 12) else err_msg.replace("\n", " "), + with ( + Path("pickletest_chat_data").open("rb") as f, + pytest.raises( + pickle.UnpicklingError, + match=err_msg if sys.version_info < (3, 12) else err_msg.replace("\n", " "), + ), ): pickle.load(f) diff --git a/tests/request/test_request.py b/tests/request/test_request.py index bd186c2ef..cbb513440 100644 --- a/tests/request/test_request.py +++ b/tests/request/test_request.py @@ -22,9 +22,10 @@ import asyncio import json import logging from collections import defaultdict +from collections.abc import Coroutine from dataclasses import dataclass from http import HTTPStatus -from typing import Any, Callable, Coroutine, Tuple +from typing import Any, Callable import httpx import pytest @@ -67,7 +68,7 @@ from .test_requestdata import ( # noqa: F401 def mocker_factory( response: bytes, return_code: int = HTTPStatus.OK -) -> Callable[[Tuple[Any]], Coroutine[Any, Any, Tuple[int, bytes]]]: +) -> Callable[[tuple[Any]], Coroutine[Any, Any, tuple[int, bytes]]]: async def make_assertion(*args, **kwargs): return return_code, response @@ -219,8 +220,9 @@ class TestRequestWithoutRequest: monkeypatch.setattr(httpx_request, "do_request", mocker_factory(response=server_response)) - with pytest.raises(TelegramError, match="Invalid server response"), caplog.at_level( - logging.ERROR + with ( + pytest.raises(TelegramError, match="Invalid server response"), + caplog.at_level(logging.ERROR), ): await httpx_request.post(None, None, None) @@ -413,7 +415,7 @@ class TestRequestWithoutRequest: async def shutdown(self_) -> None: pass - async def do_request(self_, *args, **kwargs) -> Tuple[int, bytes]: + async def do_request(self_, *args, **kwargs) -> tuple[int, bytes]: self.test_flag = ( kwargs.get("read_timeout"), kwargs.get("connect_timeout"), diff --git a/tests/request/test_requestdata.py b/tests/request/test_requestdata.py index 3dc8ca1af..9fa3480c1 100644 --- a/tests/request/test_requestdata.py +++ b/tests/request/test_requestdata.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/]. import json -from typing import Any, Dict +from typing import Any from urllib.parse import quote import pytest @@ -30,7 +30,7 @@ from tests.auxil.slots import mro_slots @pytest.fixture(scope="module") -def inputfiles() -> Dict[bool, InputFile]: +def inputfiles() -> dict[bool, InputFile]: return {True: InputFile(obj="data", attach=True), False: InputFile(obj="data", attach=False)} @@ -52,7 +52,7 @@ def input_media_photo() -> InputMediaPhoto: @pytest.fixture(scope="module") -def simple_params() -> Dict[str, Any]: +def simple_params() -> dict[str, Any]: return { "string": "string", "integer": 1, @@ -62,7 +62,7 @@ def simple_params() -> Dict[str, Any]: @pytest.fixture(scope="module") -def simple_jsons() -> Dict[str, Any]: +def simple_jsons() -> dict[str, Any]: return { "string": "string", "integer": json.dumps(1), @@ -79,7 +79,7 @@ def simple_rqs(simple_params) -> RequestData: @pytest.fixture(scope="module") -def file_params(inputfiles, input_media_video, input_media_photo) -> Dict[str, Any]: +def file_params(inputfiles, input_media_video, input_media_photo) -> dict[str, Any]: return { "inputfile_attach": inputfiles[True], "inputfile_no_attach": inputfiles[False], @@ -89,7 +89,7 @@ def file_params(inputfiles, input_media_video, input_media_photo) -> Dict[str, A @pytest.fixture(scope="module") -def file_jsons(inputfiles, input_media_video, input_media_photo) -> Dict[str, Any]: +def file_jsons(inputfiles, input_media_video, input_media_photo) -> dict[str, Any]: input_media_video_dict = input_media_video.to_dict() input_media_video_dict["media"] = input_media_video.media.attach_uri input_media_video_dict["thumbnail"] = input_media_video.thumbnail.attach_uri @@ -110,14 +110,14 @@ def file_rqs(file_params) -> RequestData: @pytest.fixture(scope="module") -def mixed_params(file_params, simple_params) -> Dict[str, Any]: +def mixed_params(file_params, simple_params) -> dict[str, Any]: both = file_params.copy() both.update(simple_params) return both @pytest.fixture(scope="module") -def mixed_jsons(file_jsons, simple_jsons) -> Dict[str, Any]: +def mixed_jsons(file_jsons, simple_jsons) -> dict[str, Any]: both = file_jsons.copy() both.update(simple_jsons) return both diff --git a/tests/request/test_requestparameter.py b/tests/request/test_requestparameter.py index d7ad2088a..4106a69a5 100644 --- a/tests/request/test_requestparameter.py +++ b/tests/request/test_requestparameter.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/]. import datetime -from typing import Sequence +from collections.abc import Sequence import pytest diff --git a/tests/test_bot.py b/tests/test_bot.py index 1c8671c97..7f060fb09 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -27,7 +27,6 @@ import time from collections import defaultdict from http import HTTPStatus from io import BytesIO -from typing import Tuple import httpx import pytest @@ -334,9 +333,12 @@ class TestBotWithoutRequest: assert self.test_flag == "stop" async def test_equality(self): - async with make_bot(token=FALLBACKS[0]["token"]) as a, make_bot( - token=FALLBACKS[0]["token"] - ) as b, Bot(token=FALLBACKS[0]["token"]) as c, make_bot(token=FALLBACKS[1]["token"]) as d: + async with ( + make_bot(token=FALLBACKS[0]["token"]) as a, + make_bot(token=FALLBACKS[0]["token"]) as b, + Bot(token=FALLBACKS[0]["token"]) as c, + make_bot(token=FALLBACKS[1]["token"]) as d, + ): e = Update(123456789) f = Bot(token=FALLBACKS[0]["token"]) @@ -2172,7 +2174,7 @@ class TestBotWithoutRequest: async def shutdown(self_) -> None: pass - async def do_request(self_, *args, **kwargs) -> Tuple[int, bytes]: + async def do_request(self_, *args, **kwargs) -> tuple[int, bytes]: nonlocal test_flag test_flag = ( kwargs.get("read_timeout"), diff --git a/tests/test_messageentity.py b/tests/test_messageentity.py index 4c981f453..b65ccf418 100644 --- a/tests/test_messageentity.py +++ b/tests/test_messageentity.py @@ -17,7 +17,6 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. import random -from typing import List, Tuple import pytest @@ -86,7 +85,7 @@ class TestMessageEntityWithoutRequest(MessageEntityTestBase): def test_fix_utf16(self): text = "𠌕 bold 𝄢 italic underlined: 𝛙𝌢𑁍" - inputs_outputs: List[Tuple[Tuple[int, int, str], Tuple[int, int]]] = [ + inputs_outputs: list[tuple[tuple[int, int, str], tuple[int, int]]] = [ ((2, 4, MessageEntity.BOLD), (3, 4)), ((9, 6, MessageEntity.ITALIC), (11, 6)), ((28, 3, MessageEntity.UNDERLINE), (30, 6)), diff --git a/tests/test_official/arg_type_checker.py b/tests/test_official/arg_type_checker.py index 24ef867ba..c6d5bae53 100644 --- a/tests/test_official/arg_type_checker.py +++ b/tests/test_official/arg_type_checker.py @@ -23,9 +23,10 @@ are required or not.""" import inspect import logging import re +from collections.abc import Sequence from datetime import datetime from types import FunctionType -from typing import Any, Sequence +from typing import Any from telegram._utils.defaultvalue import DefaultValue from telegram._utils.types import FileInput, ODVInput diff --git a/tests/test_official/exceptions.py b/tests/test_official/exceptions.py index c6122f312..50551559b 100644 --- a/tests/test_official/exceptions.py +++ b/tests/test_official/exceptions.py @@ -57,7 +57,7 @@ class ParamTypeCheckingExceptions: ("reaction", False): "ReactionType", # + str ("options", False): "InputPollOption", # + str # TODO: Deprecated and will be corrected (and removed) in next major PTB version: - ("file_hashes", True): "List[str]", + ("file_hashes", True): "list[str]", } # Special cases for other parameters that accept more types than the official API, and are diff --git a/tests/test_official/helpers.py b/tests/test_official/helpers.py index 6851bf85f..68ffffa09 100644 --- a/tests/test_official/helpers.py +++ b/tests/test_official/helpers.py @@ -20,7 +20,8 @@ import functools import re -from typing import TYPE_CHECKING, Any, Sequence, _eval_type, get_type_hints +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, _eval_type, get_type_hints from bs4 import PageElement, Tag