mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
parent
be441d56f9
commit
b4ea5557ac
222 changed files with 277 additions and 931 deletions
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
fail-fast: False
|
||||
steps:
|
||||
|
|
|
@ -56,4 +56,4 @@ repos:
|
|||
- id: pyupgrade
|
||||
files: ^(telegram|examples|tests)/.*\.py$
|
||||
args:
|
||||
- --py36-plus
|
||||
- --py37-plus
|
||||
|
|
|
@ -93,7 +93,7 @@ Introduction
|
|||
|
||||
This library provides a pure Python interface for the
|
||||
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
|
||||
It's compatible with Python versions 3.6.8+. PTB might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
It's compatible with Python versions **3.7+**. PTB might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
|
||||
In addition to the pure API implementation, this library features a number of high-level classes to
|
||||
make the development of bots easy and straightforward. These classes are contained in the
|
||||
|
|
|
@ -91,7 +91,7 @@ Introduction
|
|||
|
||||
This library provides a pure Python, lightweight interface for the
|
||||
`Telegram Bot API <https://core.telegram.org/bots/api>`_.
|
||||
It's compatible with Python versions 3.6.8+. PTB-Raw might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
It's compatible with Python versions **3.7+**. PTB-Raw might also work on `PyPy <http://pypy.org/>`_, though there have been a lot of issues before. Hence, PyPy is not officially supported.
|
||||
|
||||
``python-telegram-bot-raw`` is part of the `python-telegram-bot <https://python-telegram-bot.org>`_ ecosystem and provides the pure API functionality extracted from PTB. It therefore does *not* have independent release schedules, changelogs or documentation. Please consult the PTB resources.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.black]
|
||||
line-length = 99
|
||||
target-version = ['py36']
|
||||
target-version = ['py37']
|
||||
skip-string-normalization = true
|
||||
|
||||
# We need to force-exclude the negated include pattern
|
||||
|
|
3
setup.py
3
setup.py
|
@ -98,12 +98,11 @@ def get_setup_kwargs(raw=False):
|
|||
'Topic :: Internet',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
],
|
||||
python_requires='>=3.6'
|
||||
python_requires='>=3.7'
|
||||
)
|
||||
|
||||
return kwargs
|
||||
|
|
|
@ -23,10 +23,9 @@ except ImportError:
|
|||
import json # type: ignore[no-redef]
|
||||
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, List, Optional, Tuple, Type, TypeVar
|
||||
from typing import TYPE_CHECKING, List, Optional, Type, TypeVar, Tuple
|
||||
|
||||
from telegram.utils.types import JSONDict
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
@ -37,12 +36,21 @@ TO = TypeVar('TO', bound='TelegramObject', covariant=True)
|
|||
class TelegramObject:
|
||||
"""Base class for most Telegram objects."""
|
||||
|
||||
_id_attrs: Tuple[object, ...] = ()
|
||||
|
||||
# type hints in __new__ are not read by mypy (https://github.com/python/mypy/issues/1021). As a
|
||||
# workaround we can type hint instance variables in __new__ using a syntax defined in PEP 526 -
|
||||
# https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations
|
||||
if TYPE_CHECKING:
|
||||
_id_attrs: Tuple[object, ...]
|
||||
# Adding slots reduces memory usage & allows for faster attribute access.
|
||||
# Only instance variables should be added to __slots__.
|
||||
# We add __dict__ here for backward compatibility & also to avoid repetition for subclasses.
|
||||
__slots__ = ('__dict__',)
|
||||
__slots__ = ('_id_attrs',)
|
||||
|
||||
def __new__(cls, *args: object, **kwargs: object) -> 'TelegramObject': # pylint: disable=W0613
|
||||
# We add _id_attrs in __new__ instead of __init__ since we want to add this to the slots
|
||||
# w/o calling __init__ in all of the subclasses. This is what we also do in BaseFilter.
|
||||
instance = super().__new__(cls)
|
||||
instance._id_attrs = ()
|
||||
return instance
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.to_dict())
|
||||
|
@ -50,9 +58,6 @@ class TelegramObject:
|
|||
def __getitem__(self, item: str) -> object:
|
||||
return getattr(self, item, None)
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@staticmethod
|
||||
def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
return None if data is None else data.copy()
|
||||
|
@ -76,7 +81,7 @@ class TelegramObject:
|
|||
|
||||
if cls == TelegramObject:
|
||||
return cls()
|
||||
return cls(bot=bot, **data) # type: ignore[call-arg]
|
||||
return cls(bot=bot, **data)
|
||||
|
||||
@classmethod
|
||||
def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]:
|
||||
|
@ -132,6 +137,7 @@ class TelegramObject:
|
|||
return data
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
# pylint: disable=no-member
|
||||
if isinstance(other, self.__class__):
|
||||
if self._id_attrs == ():
|
||||
warnings.warn(
|
||||
|
@ -144,9 +150,10 @@ class TelegramObject:
|
|||
" for equivalence."
|
||||
)
|
||||
return self._id_attrs == other._id_attrs
|
||||
return super().__eq__(other) # pylint: disable=no-member
|
||||
return super().__eq__(other)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
# pylint: disable=no-member
|
||||
if self._id_attrs:
|
||||
return hash((self.__class__, self._id_attrs)) # pylint: disable=no-member
|
||||
return hash((self.__class__, self._id_attrs))
|
||||
return super().__hash__()
|
||||
|
|
|
@ -224,14 +224,6 @@ class Bot(TelegramObject):
|
|||
private_key, password=private_key_password, backend=default_backend()
|
||||
)
|
||||
|
||||
# The ext_bot argument is a little hack to get warnings handled correctly.
|
||||
# It's not very clean, but the warnings will be dropped at some point anyway.
|
||||
def __setattr__(self, key: str, value: object, ext_bot: bool = False) -> None:
|
||||
if issubclass(self.__class__, Bot) and self.__class__ is not Bot and not ext_bot:
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
super().__setattr__(key, value)
|
||||
|
||||
def _insert_defaults(
|
||||
self, data: Dict[str, object], timeout: ODVInput[float]
|
||||
) -> Optional[float]:
|
||||
|
|
|
@ -41,7 +41,7 @@ class BotCommand(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('description', '_id_attrs', 'command')
|
||||
__slots__ = ('description', 'command')
|
||||
|
||||
def __init__(self, command: str, description: str, **_kwargs: Any):
|
||||
self.command = command
|
||||
|
|
|
@ -57,7 +57,7 @@ class BotCommandScope(TelegramObject):
|
|||
type (:obj:`str`): Scope type.
|
||||
"""
|
||||
|
||||
__slots__ = ('type', '_id_attrs')
|
||||
__slots__ = ('type',)
|
||||
|
||||
DEFAULT = constants.BOT_COMMAND_SCOPE_DEFAULT
|
||||
""":const:`telegram.constants.BOT_COMMAND_SCOPE_DEFAULT`"""
|
||||
|
|
|
@ -101,7 +101,6 @@ class CallbackQuery(TelegramObject):
|
|||
'from_user',
|
||||
'inline_message_id',
|
||||
'data',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -186,7 +186,6 @@ class Chat(TelegramObject):
|
|||
'message_auto_delete_time',
|
||||
'has_protected_content',
|
||||
'has_private_forwards',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
SENDER: ClassVar[str] = constants.CHAT_SENDER
|
||||
|
|
|
@ -20,13 +20,12 @@
|
|||
"""This module contains an object that represents a Telegram ChatAction."""
|
||||
from typing import ClassVar
|
||||
from telegram import constants
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
|
||||
class ChatAction:
|
||||
"""Helper class to provide constants for different chat actions."""
|
||||
|
||||
__slots__ = ('__dict__',) # Adding __dict__ here since it doesn't subclass TGObject
|
||||
__slots__ = ()
|
||||
FIND_LOCATION: ClassVar[str] = constants.CHATACTION_FIND_LOCATION
|
||||
""":const:`telegram.constants.CHATACTION_FIND_LOCATION`"""
|
||||
RECORD_AUDIO: ClassVar[str] = constants.CHATACTION_RECORD_AUDIO
|
||||
|
@ -69,6 +68,3 @@ class ChatAction:
|
|||
""":const:`telegram.constants.CHATACTION_UPLOAD_VIDEO`"""
|
||||
UPLOAD_VIDEO_NOTE: ClassVar[str] = constants.CHATACTION_UPLOAD_VIDEO_NOTE
|
||||
""":const:`telegram.constants.CHATACTION_UPLOAD_VIDEO_NOTE`"""
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
|
|
@ -92,7 +92,6 @@ class ChatInviteLink(TelegramObject):
|
|||
'name',
|
||||
'creates_join_request',
|
||||
'pending_join_request_count',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -68,7 +68,6 @@ class ChatJoinRequest(TelegramObject):
|
|||
'bio',
|
||||
'invite_link',
|
||||
'bot',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -47,7 +47,7 @@ class ChatLocation(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('location', '_id_attrs', 'address')
|
||||
__slots__ = ('location', 'address')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -287,7 +287,6 @@ class ChatMember(TelegramObject):
|
|||
'can_manage_chat',
|
||||
'can_manage_voice_chats',
|
||||
'until_date',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
ADMINISTRATOR: ClassVar[str] = constants.CHATMEMBER_ADMINISTRATOR
|
||||
|
|
|
@ -69,7 +69,6 @@ class ChatMemberUpdated(TelegramObject):
|
|||
'old_chat_member',
|
||||
'new_chat_member',
|
||||
'invite_link',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -82,7 +82,6 @@ class ChatPermissions(TelegramObject):
|
|||
'can_send_other_messages',
|
||||
'can_invite_users',
|
||||
'can_send_polls',
|
||||
'_id_attrs',
|
||||
'can_send_messages',
|
||||
'can_send_media_messages',
|
||||
'can_change_info',
|
||||
|
|
|
@ -61,7 +61,7 @@ class ChosenInlineResult(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('location', 'result_id', 'from_user', 'inline_message_id', '_id_attrs', 'query')
|
||||
__slots__ = ('location', 'result_id', 'from_user', 'inline_message_id', 'query')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -64,7 +64,7 @@ class Dice(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('emoji', 'value', '_id_attrs')
|
||||
__slots__ = ('emoji', 'value')
|
||||
|
||||
def __init__(self, value: int, emoji: str, **_kwargs: Any):
|
||||
self.value = value
|
||||
|
|
|
@ -41,7 +41,6 @@ def _lstrip_str(in_s: str, lstr: str) -> str:
|
|||
class TelegramError(Exception):
|
||||
"""Base class for Telegram errors."""
|
||||
|
||||
# Apparently the base class Exception already has __dict__ in it, so its not included here
|
||||
__slots__ = ('message',)
|
||||
|
||||
def __init__(self, message: str):
|
||||
|
|
|
@ -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/].
|
||||
# pylint: disable=C0413
|
||||
"""Extensions over the Telegram Bot API to facilitate bot making"""
|
||||
|
||||
from .extbot import ExtBot
|
||||
|
@ -28,17 +27,6 @@ from .callbackcontext import CallbackContext
|
|||
from .contexttypes import ContextTypes
|
||||
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
|
||||
|
||||
# https://bugs.python.org/issue41451, fixed on 3.7+, doesn't actually remove slots
|
||||
# try-except is just here in case the __init__ is called twice (like in the tests)
|
||||
# this block is also the reason for the pylint-ignore at the top of the file
|
||||
try:
|
||||
del Dispatcher.__slots__
|
||||
except AttributeError as exc:
|
||||
if str(exc) == '__slots__':
|
||||
pass
|
||||
else:
|
||||
raise exc
|
||||
|
||||
from .jobqueue import JobQueue, Job
|
||||
from .updater import Updater
|
||||
from .callbackqueryhandler import CallbackQueryHandler
|
||||
|
|
|
@ -18,13 +18,10 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the BasePersistence class."""
|
||||
import warnings
|
||||
from sys import version_info as py_ver
|
||||
from abc import ABC, abstractmethod
|
||||
from copy import copy
|
||||
from typing import Dict, Optional, Tuple, cast, ClassVar, Generic, DefaultDict, NamedTuple
|
||||
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
from telegram import Bot
|
||||
import telegram.ext.extbot
|
||||
|
||||
|
@ -108,18 +105,11 @@ class BasePersistence(Generic[UD, CD, BD], ABC):
|
|||
persistence instance.
|
||||
"""
|
||||
|
||||
# Apparently Py 3.7 and below have '__dict__' in ABC
|
||||
if py_ver < (3, 7):
|
||||
__slots__ = (
|
||||
'store_data',
|
||||
'bot',
|
||||
)
|
||||
else:
|
||||
__slots__ = (
|
||||
'store_data', # type: ignore[assignment]
|
||||
'bot',
|
||||
'__dict__',
|
||||
)
|
||||
__slots__ = (
|
||||
'bot',
|
||||
'store_data',
|
||||
'__dict__', # __dict__ is included because we replace methods in the __new__
|
||||
)
|
||||
|
||||
def __new__(
|
||||
cls, *args: object, **kwargs: object # pylint: disable=W0613
|
||||
|
@ -169,15 +159,15 @@ class BasePersistence(Generic[UD, CD, BD], ABC):
|
|||
obj_data, queue = data
|
||||
return update_callback_data((instance.replace_bot(obj_data), queue))
|
||||
|
||||
# We want to ignore TGDeprecation warnings so we use obj.__setattr__. Adds to __dict__
|
||||
object.__setattr__(instance, 'get_user_data', get_user_data_insert_bot)
|
||||
object.__setattr__(instance, 'get_chat_data', get_chat_data_insert_bot)
|
||||
object.__setattr__(instance, 'get_bot_data', get_bot_data_insert_bot)
|
||||
object.__setattr__(instance, 'get_callback_data', get_callback_data_insert_bot)
|
||||
object.__setattr__(instance, 'update_user_data', update_user_data_replace_bot)
|
||||
object.__setattr__(instance, 'update_chat_data', update_chat_data_replace_bot)
|
||||
object.__setattr__(instance, 'update_bot_data', update_bot_data_replace_bot)
|
||||
object.__setattr__(instance, 'update_callback_data', update_callback_data_replace_bot)
|
||||
# Adds to __dict__
|
||||
setattr(instance, 'get_user_data', get_user_data_insert_bot)
|
||||
setattr(instance, 'get_chat_data', get_chat_data_insert_bot)
|
||||
setattr(instance, 'get_bot_data', get_bot_data_insert_bot)
|
||||
setattr(instance, 'get_callback_data', get_callback_data_insert_bot)
|
||||
setattr(instance, 'update_user_data', update_user_data_replace_bot)
|
||||
setattr(instance, 'update_chat_data', update_chat_data_replace_bot)
|
||||
setattr(instance, 'update_bot_data', update_bot_data_replace_bot)
|
||||
setattr(instance, 'update_callback_data', update_callback_data_replace_bot)
|
||||
return instance
|
||||
|
||||
def __init__(
|
||||
|
@ -188,16 +178,6 @@ class BasePersistence(Generic[UD, CD, BD], ABC):
|
|||
|
||||
self.bot: Bot = None # type: ignore[assignment]
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
# Allow user defined subclasses to have custom attributes.
|
||||
if issubclass(self.__class__, BasePersistence) and self.__class__.__name__ not in {
|
||||
'DictPersistence',
|
||||
'PicklePersistence',
|
||||
}:
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
def set_bot(self, bot: Bot) -> None:
|
||||
"""Set the Bot to be used by this persistence instance.
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ CheckUpdateType = Optional[Tuple[Tuple[int, ...], Handler, object]]
|
|||
|
||||
|
||||
class _ConversationTimeoutContext:
|
||||
# '__dict__' is not included since this a private class
|
||||
__slots__ = ('conversation_key', 'update', 'dispatcher', 'callback_context')
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -22,7 +22,6 @@ from typing import NoReturn, Optional, Dict, Any
|
|||
|
||||
import pytz
|
||||
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
from telegram.utils.helpers import DEFAULT_NONE
|
||||
from telegram.utils.types import ODVInput
|
||||
|
||||
|
@ -67,7 +66,6 @@ class Defaults:
|
|||
'_allow_sending_without_reply',
|
||||
'_parse_mode',
|
||||
'_api_defaults',
|
||||
'__dict__',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -108,9 +106,6 @@ class Defaults:
|
|||
if self._timeout != DEFAULT_NONE:
|
||||
self._api_defaults['timeout'] = self._timeout
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@property
|
||||
def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003
|
||||
return self._api_defaults
|
||||
|
|
|
@ -48,7 +48,7 @@ from telegram.ext.callbackcontext import CallbackContext
|
|||
from telegram.ext.handler import Handler
|
||||
import telegram.ext.extbot
|
||||
from telegram.ext.callbackdatacache import CallbackDataCache
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.ext.utils.promise import Promise
|
||||
from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE
|
||||
from telegram.ext.utils.types import CCT, UD, CD, BD
|
||||
|
@ -312,17 +312,6 @@ class Dispatcher(Generic[CCT, UD, CD, BD]):
|
|||
else:
|
||||
self._set_singleton(None)
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
# Mangled names don't automatically apply in __setattr__ (see
|
||||
# https://docs.python.org/3/tutorial/classes.html#private-variables), so we have to make
|
||||
# it mangled so they don't raise TelegramDeprecationWarning unnecessarily
|
||||
if key.startswith('__'):
|
||||
key = f"_{self.__class__.__name__}{key}"
|
||||
if issubclass(self.__class__, Dispatcher) and self.__class__ is not Dispatcher:
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@property
|
||||
def exception_event(self) -> Event: # skipcq: PY-D0003
|
||||
return self.__exception_event
|
||||
|
|
|
@ -75,14 +75,6 @@ class ExtBot(telegram.bot.Bot):
|
|||
|
||||
__slots__ = ('arbitrary_callback_data', 'callback_data_cache')
|
||||
|
||||
# The ext_bot argument is a little hack to get warnings handled correctly.
|
||||
# It's not very clean, but the warnings will be dropped at some point anyway.
|
||||
def __setattr__(self, key: str, value: object, ext_bot: bool = True) -> None:
|
||||
if issubclass(self.__class__, ExtBot) and self.__class__ is not ExtBot:
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
super().__setattr__(key, value, ext_bot=ext_bot) # type: ignore[call-arg]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
token: str,
|
||||
|
@ -265,7 +257,7 @@ class ExtBot(telegram.bot.Bot):
|
|||
# different places
|
||||
new_result = copy(result)
|
||||
markup = self._replace_keyboard(result.reply_markup) # type: ignore[attr-defined]
|
||||
new_result.reply_markup = markup
|
||||
new_result.reply_markup = markup # type: ignore[attr-defined]
|
||||
results.append(new_result)
|
||||
|
||||
return results, next_offset
|
||||
|
|
|
@ -23,7 +23,6 @@ import re
|
|||
import warnings
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from sys import version_info as py_ver
|
||||
from threading import Lock
|
||||
from typing import (
|
||||
Dict,
|
||||
|
@ -51,7 +50,7 @@ __all__ = [
|
|||
'XORFilter',
|
||||
]
|
||||
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.utils.types import SLT
|
||||
|
||||
DataDict = Dict[str, list]
|
||||
|
@ -113,12 +112,10 @@ class BaseFilter(ABC):
|
|||
(depends on the handler).
|
||||
"""
|
||||
|
||||
if py_ver < (3, 7):
|
||||
__slots__ = ('_name', '_data_filter')
|
||||
else:
|
||||
__slots__ = ('_name', '_data_filter', '__dict__') # type: ignore[assignment]
|
||||
__slots__ = ('_name', '_data_filter')
|
||||
|
||||
def __new__(cls, *args: object, **kwargs: object) -> 'BaseFilter': # pylint: disable=W0613
|
||||
# We do this here instead of in a __init__ so filter don't have to call __init__ or super()
|
||||
instance = super().__new__(cls)
|
||||
instance._name = None
|
||||
instance._data_filter = False
|
||||
|
@ -141,18 +138,6 @@ class BaseFilter(ABC):
|
|||
def __invert__(self) -> 'BaseFilter':
|
||||
return InvertedFilter(self)
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
# Allow setting custom attributes w/o warning for user defined custom filters.
|
||||
# To differentiate between a custom and a PTB filter, we use this hacky but
|
||||
# simple way of checking the module name where the class is defined from.
|
||||
if (
|
||||
issubclass(self.__class__, (UpdateFilter, MessageFilter))
|
||||
and self.__class__.__module__ != __name__
|
||||
): # __name__ is telegram.ext.filters
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@property
|
||||
def data_filter(self) -> bool:
|
||||
return self._data_filter
|
||||
|
@ -437,10 +422,7 @@ class Filters:
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('__dict__',)
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
__slots__ = ()
|
||||
|
||||
class _All(MessageFilter):
|
||||
__slots__ = ()
|
||||
|
|
|
@ -19,9 +19,6 @@
|
|||
"""This module contains the base class for handlers as used by the Dispatcher."""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, Union, Generic
|
||||
from sys import version_info as py_ver
|
||||
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
from telegram import Update
|
||||
from telegram.ext.utils.promise import Promise
|
||||
|
@ -93,26 +90,14 @@ class Handler(Generic[UT, CCT], ABC):
|
|||
|
||||
"""
|
||||
|
||||
# Apparently Py 3.7 and below have '__dict__' in ABC
|
||||
if py_ver < (3, 7):
|
||||
__slots__ = (
|
||||
'callback',
|
||||
'pass_update_queue',
|
||||
'pass_job_queue',
|
||||
'pass_user_data',
|
||||
'pass_chat_data',
|
||||
'run_async',
|
||||
)
|
||||
else:
|
||||
__slots__ = (
|
||||
'callback', # type: ignore[assignment]
|
||||
'pass_update_queue',
|
||||
'pass_job_queue',
|
||||
'pass_user_data',
|
||||
'pass_chat_data',
|
||||
'run_async',
|
||||
'__dict__',
|
||||
)
|
||||
__slots__ = (
|
||||
'callback',
|
||||
'pass_update_queue',
|
||||
'pass_job_queue',
|
||||
'pass_user_data',
|
||||
'pass_chat_data',
|
||||
'run_async',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -130,17 +115,6 @@ class Handler(Generic[UT, CCT], ABC):
|
|||
self.pass_chat_data = pass_chat_data
|
||||
self.run_async = run_async
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
# See comment on BaseFilter to know why this was done.
|
||||
if key.startswith('__'):
|
||||
key = f"_{self.__class__.__name__}{key}"
|
||||
if issubclass(self.__class__, Handler) and not self.__class__.__module__.startswith(
|
||||
'telegram.ext.'
|
||||
):
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@abstractmethod
|
||||
def check_update(self, update: object) -> Optional[Union[bool, object]]:
|
||||
"""
|
||||
|
|
|
@ -31,7 +31,6 @@ from apscheduler.job import Job as APSJob
|
|||
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
from telegram.utils.types import JSONDict
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
@ -50,7 +49,7 @@ class JobQueue:
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('_dispatcher', 'logger', 'scheduler', '__dict__')
|
||||
__slots__ = ('_dispatcher', 'logger', 'scheduler')
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._dispatcher: 'Dispatcher' = None # type: ignore[assignment]
|
||||
|
@ -67,9 +66,6 @@ class JobQueue:
|
|||
logging.getLogger('apscheduler.executors.default').addFilter(aps_log_filter)
|
||||
self.scheduler.add_listener(self._dispatch_error, EVENT_JOB_ERROR)
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
def _build_args(self, job: 'Job') -> List[Union[CallbackContext, 'Bot', 'Job']]:
|
||||
if self._dispatcher.use_context:
|
||||
return [self._dispatcher.context_types.context.from_job(job, self._dispatcher)]
|
||||
|
@ -560,7 +556,6 @@ class Job:
|
|||
'_removed',
|
||||
'_enabled',
|
||||
'job',
|
||||
'__dict__',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -582,9 +577,6 @@ class Job:
|
|||
|
||||
self.job = cast(APSJob, job) # skipcq: PTC-W0052
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
def run(self, dispatcher: 'Dispatcher') -> None:
|
||||
"""Executes the callback function independently of the jobs schedule."""
|
||||
try:
|
||||
|
|
|
@ -42,7 +42,7 @@ from typing import (
|
|||
from telegram import Bot, TelegramError
|
||||
from telegram.error import InvalidToken, RetryAfter, TimedOut, Unauthorized
|
||||
from telegram.ext import Dispatcher, JobQueue, ContextTypes, ExtBot
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.utils.helpers import get_signal_name, DEFAULT_FALSE, DefaultValue
|
||||
from telegram.utils.request import Request
|
||||
from telegram.ext.utils.types import CCT, UD, CD, BD
|
||||
|
@ -149,7 +149,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
'httpd',
|
||||
'__lock',
|
||||
'__threads',
|
||||
'__dict__',
|
||||
)
|
||||
|
||||
@overload
|
||||
|
@ -328,14 +327,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
self.__lock = Lock()
|
||||
self.__threads: List[Thread] = []
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
if key.startswith('__'):
|
||||
key = f"_{self.__class__.__name__}{key}"
|
||||
if issubclass(self.__class__, Updater) and self.__class__ is not Updater:
|
||||
object.__setattr__(self, key, value)
|
||||
return
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
def _init_thread(self, target: Callable, name: str, *args: object, **kwargs: object) -> None:
|
||||
thr = Thread(
|
||||
target=self._thread_wrapper,
|
||||
|
|
|
@ -22,7 +22,6 @@ import logging
|
|||
from threading import Event
|
||||
from typing import Callable, List, Optional, Tuple, TypeVar, Union
|
||||
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
RT = TypeVar('RT')
|
||||
|
@ -65,7 +64,6 @@ class Promise:
|
|||
'_done_callback',
|
||||
'_result',
|
||||
'_exception',
|
||||
'__dict__',
|
||||
)
|
||||
|
||||
# TODO: Remove error_handling parameter once we drop the @run_async decorator
|
||||
|
@ -87,9 +85,6 @@ class Promise:
|
|||
self._result: Optional[RT] = None
|
||||
self._exception: Optional[Exception] = None
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
def run(self) -> None:
|
||||
"""Calls the :attr:`pooled_function` callable."""
|
||||
try:
|
||||
|
|
|
@ -31,7 +31,6 @@ from tornado.ioloop import IOLoop
|
|||
|
||||
from telegram import Update
|
||||
from telegram.ext import ExtBot
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
from telegram.utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -53,7 +52,6 @@ class WebhookServer:
|
|||
'is_running',
|
||||
'server_lock',
|
||||
'shutdown_lock',
|
||||
'__dict__',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -68,9 +66,6 @@ class WebhookServer:
|
|||
self.server_lock = Lock()
|
||||
self.shutdown_lock = Lock()
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
def serve_forever(self, ready: Event = None) -> None:
|
||||
with self.server_lock:
|
||||
IOLoop().make_current()
|
||||
|
|
|
@ -76,7 +76,6 @@ class Animation(TelegramObject):
|
|||
'mime_type',
|
||||
'height',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -80,7 +80,6 @@ class Audio(TelegramObject):
|
|||
'performer',
|
||||
'mime_type',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -71,7 +71,6 @@ class ChatPhoto(TelegramObject):
|
|||
'small_file_id',
|
||||
'small_file_unique_id',
|
||||
'big_file_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -46,7 +46,7 @@ class Contact(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('vcard', 'user_id', 'first_name', 'last_name', 'phone_number', '_id_attrs')
|
||||
__slots__ = ('vcard', 'user_id', 'first_name', 'last_name', 'phone_number')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -68,11 +68,8 @@ class Document(TelegramObject):
|
|||
'thumb',
|
||||
'mime_type',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
_id_keys = ('file_id',)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file_id: str,
|
||||
|
|
|
@ -74,7 +74,6 @@ class File(TelegramObject):
|
|||
'file_unique_id',
|
||||
'file_path',
|
||||
'_credentials',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -26,8 +26,6 @@ import os
|
|||
from typing import IO, Optional, Tuple, Union
|
||||
from uuid import uuid4
|
||||
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
DEFAULT_MIME_TYPE = 'application/octet-stream'
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -52,7 +50,7 @@ class InputFile:
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('filename', 'attach', 'input_file_content', 'mimetype', '__dict__')
|
||||
__slots__ = ('filename', 'attach', 'input_file_content', 'mimetype')
|
||||
|
||||
def __init__(self, obj: Union[IO, bytes], filename: str = None, attach: bool = None):
|
||||
self.filename = None
|
||||
|
@ -78,9 +76,6 @@ class InputFile:
|
|||
if not self.filename:
|
||||
self.filename = self.mimetype.replace('/', '.')
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@property
|
||||
def field_tuple(self) -> Tuple[str, bytes, str]: # skipcq: PY-D0003
|
||||
return self.filename, self.input_file_content, self.mimetype
|
||||
|
|
|
@ -63,7 +63,6 @@ class Location(TelegramObject):
|
|||
'live_period',
|
||||
'latitude',
|
||||
'heading',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -58,7 +58,7 @@ class PhotoSize(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('bot', 'width', 'file_id', 'file_size', 'height', 'file_unique_id', '_id_attrs')
|
||||
__slots__ = ('bot', 'width', 'file_id', 'file_size', 'height', 'file_unique_id')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -97,7 +97,6 @@ class Sticker(TelegramObject):
|
|||
'height',
|
||||
'file_unique_id',
|
||||
'emoji',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -208,7 +207,6 @@ class StickerSet(TelegramObject):
|
|||
'title',
|
||||
'stickers',
|
||||
'name',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
@ -286,7 +284,7 @@ class MaskPosition(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('point', 'scale', 'x_shift', 'y_shift', '_id_attrs')
|
||||
__slots__ = ('point', 'scale', 'x_shift', 'y_shift')
|
||||
|
||||
FOREHEAD: ClassVar[str] = constants.STICKER_FOREHEAD
|
||||
""":const:`telegram.constants.STICKER_FOREHEAD`"""
|
||||
|
|
|
@ -68,7 +68,6 @@ class Venue(TelegramObject):
|
|||
'foursquare_type',
|
||||
'foursquare_id',
|
||||
'google_place_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -77,7 +77,6 @@ class Video(TelegramObject):
|
|||
'mime_type',
|
||||
'height',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -69,7 +69,6 @@ class VideoNote(TelegramObject):
|
|||
'thumb',
|
||||
'duration',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -65,7 +65,6 @@ class Voice(TelegramObject):
|
|||
'duration',
|
||||
'mime_type',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -60,7 +60,7 @@ class ForceReply(ReplyMarkup):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('selective', 'force_reply', 'input_field_placeholder', '_id_attrs')
|
||||
__slots__ = ('selective', 'force_reply', 'input_field_placeholder')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -74,7 +74,6 @@ class Game(TelegramObject):
|
|||
'text_entities',
|
||||
'text',
|
||||
'animation',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -45,7 +45,7 @@ class GameHighScore(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('position', 'user', 'score', '_id_attrs')
|
||||
__slots__ = ('position', 'user', 'score')
|
||||
|
||||
def __init__(self, position: int, user: User, score: int):
|
||||
self.position = position
|
||||
|
|
|
@ -121,7 +121,6 @@ class InlineKeyboardButton(TelegramObject):
|
|||
'pay',
|
||||
'switch_inline_query',
|
||||
'text',
|
||||
'_id_attrs',
|
||||
'login_url',
|
||||
)
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class InlineKeyboardMarkup(ReplyMarkup):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('inline_keyboard', '_id_attrs')
|
||||
__slots__ = ('inline_keyboard',)
|
||||
|
||||
def __init__(self, inline_keyboard: List[List[InlineKeyboardButton]], **_kwargs: Any):
|
||||
# Required
|
||||
|
|
|
@ -71,7 +71,7 @@ class InlineQuery(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('bot', 'location', 'chat_type', 'id', 'offset', 'from_user', 'query', '_id_attrs')
|
||||
__slots__ = ('bot', 'location', 'chat_type', 'id', 'offset', 'from_user', 'query')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -46,7 +46,7 @@ class InlineQueryResult(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('type', 'id', '_id_attrs')
|
||||
__slots__ = ('type', 'id')
|
||||
|
||||
def __init__(self, type: str, id: str, **_kwargs: Any):
|
||||
# Required
|
||||
|
|
|
@ -46,7 +46,7 @@ class InputContactMessageContent(InputMessageContent):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('vcard', 'first_name', 'last_name', 'phone_number', '_id_attrs')
|
||||
__slots__ = ('vcard', 'first_name', 'last_name', 'phone_number')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -144,7 +144,6 @@ class InputInvoiceMessageContent(InputMessageContent):
|
|||
'send_phone_number_to_provider',
|
||||
'send_email_to_provider',
|
||||
'is_flexible',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -60,7 +60,7 @@ class InputLocationMessageContent(InputMessageContent):
|
|||
"""
|
||||
|
||||
__slots__ = ('longitude', 'horizontal_accuracy', 'proximity_alert_radius', 'live_period',
|
||||
'latitude', 'heading', '_id_attrs')
|
||||
'latitude', 'heading')
|
||||
# fmt: on
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -59,7 +59,7 @@ class InputTextMessageContent(InputMessageContent):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('disable_web_page_preview', 'parse_mode', 'entities', 'message_text', '_id_attrs')
|
||||
__slots__ = ('disable_web_page_preview', 'parse_mode', 'entities', 'message_text')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -69,7 +69,6 @@ class InputVenueMessageContent(InputMessageContent):
|
|||
'foursquare_type',
|
||||
'google_place_id',
|
||||
'latitude',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -58,7 +58,7 @@ class KeyboardButton(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('request_location', 'request_contact', 'request_poll', 'text', '_id_attrs')
|
||||
__slots__ = ('request_location', 'request_contact', 'request_poll', 'text')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -37,7 +37,7 @@ class KeyboardButtonPollType(TelegramObject):
|
|||
create a poll of any type.
|
||||
"""
|
||||
|
||||
__slots__ = ('type', '_id_attrs')
|
||||
__slots__ = ('type',)
|
||||
|
||||
def __init__(self, type: str = None, **_kwargs: Any): # pylint: disable=W0622
|
||||
self.type = type
|
||||
|
|
|
@ -69,7 +69,7 @@ class LoginUrl(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('bot_username', 'request_write_access', 'url', 'forward_text', '_id_attrs')
|
||||
__slots__ = ('bot_username', 'request_write_access', 'url', 'forward_text')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -412,7 +412,6 @@ class Message(TelegramObject):
|
|||
'voice_chat_scheduled',
|
||||
'is_automatic_forward',
|
||||
'has_protected_content',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
ATTACHMENT_TYPES: ClassVar[List[str]] = [
|
||||
|
|
|
@ -44,7 +44,7 @@ class MessageAutoDeleteTimerChanged(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('message_auto_delete_time', '_id_attrs')
|
||||
__slots__ = ('message_auto_delete_time',)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -60,7 +60,7 @@ class MessageEntity(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('length', 'url', 'user', 'type', 'language', 'offset', '_id_attrs')
|
||||
__slots__ = ('length', 'url', 'user', 'type', 'language', 'offset')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -32,7 +32,7 @@ class MessageId(TelegramObject):
|
|||
message_id (:obj:`int`): Unique message identifier
|
||||
"""
|
||||
|
||||
__slots__ = ('message_id', '_id_attrs')
|
||||
__slots__ = ('message_id',)
|
||||
|
||||
def __init__(self, message_id: int, **_kwargs: Any):
|
||||
self.message_id = int(message_id)
|
||||
|
|
|
@ -21,13 +21,12 @@
|
|||
from typing import ClassVar
|
||||
|
||||
from telegram import constants
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
|
||||
class ParseMode:
|
||||
"""This object represents a Telegram Message Parse Modes."""
|
||||
|
||||
__slots__ = ('__dict__',)
|
||||
__slots__ = ()
|
||||
|
||||
MARKDOWN: ClassVar[str] = constants.PARSEMODE_MARKDOWN
|
||||
""":const:`telegram.constants.PARSEMODE_MARKDOWN`\n
|
||||
|
@ -40,6 +39,3 @@ class ParseMode:
|
|||
""":const:`telegram.constants.PARSEMODE_MARKDOWN_V2`"""
|
||||
HTML: ClassVar[str] = constants.PARSEMODE_HTML
|
||||
""":const:`telegram.constants.PARSEMODE_HTML`"""
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
|
|
@ -137,7 +137,6 @@ class EncryptedCredentials(TelegramObject):
|
|||
'secret',
|
||||
'bot',
|
||||
'data',
|
||||
'_id_attrs',
|
||||
'_decrypted_secret',
|
||||
'_decrypted_data',
|
||||
)
|
||||
|
|
|
@ -130,7 +130,6 @@ class EncryptedPassportElement(TelegramObject):
|
|||
'reverse_side',
|
||||
'front_side',
|
||||
'data',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -51,7 +51,7 @@ class PassportData(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('bot', 'credentials', 'data', '_decrypted_data', '_id_attrs')
|
||||
__slots__ = ('bot', 'credentials', 'data', '_decrypted_data')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -46,7 +46,7 @@ class PassportElementError(TelegramObject):
|
|||
"""
|
||||
|
||||
# All subclasses of this class won't have _id_attrs in slots since it's added here.
|
||||
__slots__ = ('message', 'source', 'type', '_id_attrs')
|
||||
__slots__ = ('message', 'source', 'type')
|
||||
|
||||
def __init__(self, source: str, type: str, message: str, **_kwargs: Any):
|
||||
# Required
|
||||
|
|
|
@ -65,7 +65,6 @@ class PassportFile(TelegramObject):
|
|||
'file_size',
|
||||
'_credentials',
|
||||
'file_unique_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -59,7 +59,6 @@ class Invoice(TelegramObject):
|
|||
'title',
|
||||
'description',
|
||||
'total_amount',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -45,7 +45,7 @@ class LabeledPrice(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('label', '_id_attrs', 'amount')
|
||||
__slots__ = ('label', 'amount')
|
||||
|
||||
def __init__(self, label: str, amount: int, **_kwargs: Any):
|
||||
self.label = label
|
||||
|
|
|
@ -49,7 +49,7 @@ class OrderInfo(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('email', 'shipping_address', 'phone_number', 'name', '_id_attrs')
|
||||
__slots__ = ('email', 'shipping_address', 'phone_number', 'name')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -76,7 +76,6 @@ class PreCheckoutQuery(TelegramObject):
|
|||
'total_amount',
|
||||
'id',
|
||||
'from_user',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -52,7 +52,6 @@ class ShippingAddress(TelegramObject):
|
|||
__slots__ = (
|
||||
'post_code',
|
||||
'city',
|
||||
'_id_attrs',
|
||||
'country_code',
|
||||
'street_line2',
|
||||
'street_line1',
|
||||
|
|
|
@ -46,7 +46,7 @@ class ShippingOption(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('prices', 'title', 'id', '_id_attrs')
|
||||
__slots__ = ('prices', 'title', 'id')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -54,7 +54,7 @@ class ShippingQuery(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('bot', 'invoice_payload', 'shipping_address', 'id', 'from_user', '_id_attrs')
|
||||
__slots__ = ('bot', 'invoice_payload', 'shipping_address', 'id', 'from_user')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -70,7 +70,6 @@ class SuccessfulPayment(TelegramObject):
|
|||
'telegram_payment_charge_id',
|
||||
'provider_payment_charge_id',
|
||||
'total_amount',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -48,7 +48,7 @@ class PollOption(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('voter_count', 'text', '_id_attrs')
|
||||
__slots__ = ('voter_count', 'text')
|
||||
|
||||
def __init__(self, text: str, voter_count: int, **_kwargs: Any):
|
||||
self.text = text
|
||||
|
@ -80,7 +80,7 @@ class PollAnswer(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('option_ids', 'user', 'poll_id', '_id_attrs')
|
||||
__slots__ = ('option_ids', 'user', 'poll_id')
|
||||
|
||||
def __init__(self, poll_id: str, user: User, option_ids: List[int], **_kwargs: Any):
|
||||
self.poll_id = poll_id
|
||||
|
@ -164,7 +164,6 @@ class Poll(TelegramObject):
|
|||
'explanation',
|
||||
'question',
|
||||
'correct_option_id',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -46,7 +46,7 @@ class ProximityAlertTriggered(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('traveler', 'distance', 'watcher', '_id_attrs')
|
||||
__slots__ = ('traveler', 'distance', 'watcher')
|
||||
|
||||
def __init__(self, traveler: User, watcher: User, distance: int, **_kwargs: Any):
|
||||
self.traveler = traveler
|
||||
|
|
|
@ -81,7 +81,6 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
|||
'resize_keyboard',
|
||||
'one_time_keyboard',
|
||||
'input_field_placeholder',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -156,7 +156,6 @@ class Update(TelegramObject):
|
|||
'my_chat_member',
|
||||
'chat_member',
|
||||
'chat_join_request',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
MESSAGE = constants.UPDATE_MESSAGE
|
||||
|
|
|
@ -108,7 +108,6 @@ class User(TelegramObject):
|
|||
'id',
|
||||
'bot',
|
||||
'language_code',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -44,7 +44,7 @@ class UserProfilePhotos(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('photos', 'total_count', '_id_attrs')
|
||||
__slots__ = ('photos', 'total_count')
|
||||
|
||||
def __init__(self, total_count: int, photos: List[List[PhotoSize]], **_kwargs: Any):
|
||||
# Required
|
||||
|
|
|
@ -16,9 +16,7 @@
|
|||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module facilitates the deprecation of functions."""
|
||||
|
||||
import warnings
|
||||
"""This module contains a class which is used for deprecation warnings."""
|
||||
|
||||
|
||||
# We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it
|
||||
|
@ -28,20 +26,3 @@ class TelegramDeprecationWarning(Warning):
|
|||
"""Custom warning class for deprecations in this library."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
# Function to warn users that setting custom attributes is deprecated (Use only in __setattr__!)
|
||||
# Checks if a custom attribute is added by checking length of dictionary before & after
|
||||
# assigning attribute. This is the fastest way to do it (I hope!).
|
||||
def set_new_attribute_deprecated(self: object, key: str, value: object) -> None:
|
||||
"""Warns the user if they set custom attributes on PTB objects."""
|
||||
org = len(self.__dict__)
|
||||
object.__setattr__(self, key, value)
|
||||
new = len(self.__dict__)
|
||||
if new > org:
|
||||
warnings.warn(
|
||||
f"Setting custom attributes such as {key!r} on objects such as "
|
||||
f"{self.__class__.__name__!r} of the PTB library is deprecated.",
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
|
|
@ -544,7 +544,7 @@ class DefaultValue(Generic[DVType]):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('value', '__dict__')
|
||||
__slots__ = ('value',)
|
||||
|
||||
def __init__(self, value: DVType = None):
|
||||
self.value = value
|
||||
|
|
|
@ -70,7 +70,6 @@ from telegram.error import (
|
|||
Unauthorized,
|
||||
)
|
||||
from telegram.utils.types import JSONDict
|
||||
from telegram.utils.deprecate import set_new_attribute_deprecated
|
||||
|
||||
|
||||
def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: disable=W0613
|
||||
|
@ -112,7 +111,7 @@ class Request:
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('_connect_timeout', '_con_pool_size', '_con_pool', '__dict__')
|
||||
__slots__ = ('_connect_timeout', '_con_pool_size', '_con_pool')
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
@ -192,9 +191,6 @@ class Request:
|
|||
|
||||
self._con_pool = mgr
|
||||
|
||||
def __setattr__(self, key: str, value: object) -> None:
|
||||
set_new_attribute_deprecated(self, key, value)
|
||||
|
||||
@property
|
||||
def con_pool_size(self) -> int:
|
||||
"""The size of the connection pool used."""
|
||||
|
|
|
@ -64,7 +64,7 @@ class VoiceChatEnded(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('duration', '_id_attrs')
|
||||
__slots__ = ('duration',)
|
||||
|
||||
def __init__(self, duration: int, **_kwargs: Any) -> None:
|
||||
self.duration = int(duration) if duration is not None else None
|
||||
|
@ -93,7 +93,7 @@ class VoiceChatParticipantsInvited(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('users', '_id_attrs')
|
||||
__slots__ = ('users',)
|
||||
|
||||
def __init__(self, users: List[User], **_kwargs: Any) -> None:
|
||||
self.users = users
|
||||
|
@ -140,7 +140,7 @@ class VoiceChatScheduled(TelegramObject):
|
|||
|
||||
"""
|
||||
|
||||
__slots__ = ('start_date', '_id_attrs')
|
||||
__slots__ = ('start_date',)
|
||||
|
||||
def __init__(self, start_date: dtm.datetime, **_kwargs: Any) -> None:
|
||||
self.start_date = start_date
|
||||
|
|
|
@ -71,7 +71,6 @@ class WebhookInfo(TelegramObject):
|
|||
'last_error_message',
|
||||
'pending_update_count',
|
||||
'has_custom_certificate',
|
||||
'_id_attrs',
|
||||
)
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -44,6 +44,7 @@ from telegram import (
|
|||
ChosenInlineResult,
|
||||
File,
|
||||
ChatPermissions,
|
||||
Bot,
|
||||
)
|
||||
from telegram.ext import (
|
||||
Dispatcher,
|
||||
|
@ -56,6 +57,7 @@ from telegram.ext import (
|
|||
)
|
||||
from telegram.error import BadRequest
|
||||
from telegram.utils.helpers import DefaultValue, DEFAULT_NONE
|
||||
from telegram.utils.request import Request
|
||||
from tests.bots import get_bot
|
||||
|
||||
|
||||
|
@ -89,14 +91,22 @@ def bot_info():
|
|||
return get_bot()
|
||||
|
||||
|
||||
# Below Dict* classes are used to monkeypatch attributes since parent classes don't have __dict__
|
||||
class DictRequest(Request):
|
||||
pass
|
||||
|
||||
|
||||
class DictExtBot(ExtBot):
|
||||
pass
|
||||
|
||||
|
||||
class DictBot(Bot):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def bot(bot_info):
|
||||
class DictExtBot(
|
||||
ExtBot
|
||||
): # Subclass Bot to allow monkey patching of attributes and functions, would
|
||||
pass # come into effect when we __dict__ is dropped from slots
|
||||
|
||||
return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY)
|
||||
return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY, request=DictRequest())
|
||||
|
||||
|
||||
DEFAULT_BOTS = {}
|
||||
|
@ -230,7 +240,7 @@ def make_bot(bot_info, **kwargs):
|
|||
"""
|
||||
Tests are executed on tg.ext.ExtBot, as that class only extends the functionality of tg.bot
|
||||
"""
|
||||
return ExtBot(bot_info['token'], private_key=PRIVATE_KEY, **kwargs)
|
||||
return ExtBot(bot_info['token'], private_key=PRIVATE_KEY, request=DictRequest(), **kwargs)
|
||||
|
||||
|
||||
CMD_PATTERN = re.compile(r'/[\da-z_]{1,32}(?:@\w{1,32})?')
|
||||
|
@ -361,9 +371,9 @@ def mro_slots():
|
|||
return [
|
||||
attr
|
||||
for cls in _class.__class__.__mro__[:-1]
|
||||
if hasattr(cls, '__slots__') # ABC doesn't have slots in py 3.7 and below
|
||||
if hasattr(cls, '__slots__') # The Exception class doesn't have slots
|
||||
for attr in cls.__slots__
|
||||
if attr != '__dict__'
|
||||
if attr != '__dict__' # left here for classes which still has __dict__
|
||||
]
|
||||
|
||||
return _mro_slots
|
||||
|
|
|
@ -57,13 +57,10 @@ class TestAnimation:
|
|||
file_size = 4127
|
||||
caption = "Test *animation*"
|
||||
|
||||
def test_slot_behaviour(self, animation, recwarn, mro_slots):
|
||||
def test_slot_behaviour(self, animation, mro_slots):
|
||||
for attr in animation.__slots__:
|
||||
assert getattr(animation, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not animation.__dict__, f"got missing slot(s): {animation.__dict__}"
|
||||
assert len(mro_slots(animation)) == len(set(mro_slots(animation))), "duplicate slot"
|
||||
animation.custom, animation.file_name = 'should give warning', self.file_name
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_creation(self, animation):
|
||||
assert isinstance(animation, Animation)
|
||||
|
|
|
@ -59,13 +59,10 @@ class TestAudio:
|
|||
audio_file_id = '5a3128a4d2a04750b5b58397f3b5e812'
|
||||
audio_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e'
|
||||
|
||||
def test_slot_behaviour(self, audio, recwarn, mro_slots):
|
||||
def test_slot_behaviour(self, audio, mro_slots):
|
||||
for attr in audio.__slots__:
|
||||
assert getattr(audio, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not audio.__dict__, f"got missing slot(s): {audio.__dict__}"
|
||||
assert len(mro_slots(audio)) == len(set(mro_slots(audio))), "duplicate slot"
|
||||
audio.custom, audio.file_name = 'should give warning', self.file_name
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_creation(self, audio):
|
||||
# Make sure file has been uploaded.
|
||||
|
|
|
@ -145,20 +145,10 @@ class TestBot:
|
|||
"""
|
||||
|
||||
@pytest.mark.parametrize('inst', ['bot', "default_bot"], indirect=True)
|
||||
def test_slot_behaviour(self, inst, recwarn, mro_slots):
|
||||
def test_slot_behaviour(self, inst, mro_slots):
|
||||
for attr in inst.__slots__:
|
||||
assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not inst.__dict__, f"got missing slots: {inst.__dict__}"
|
||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||
inst.custom, inst.base_url = 'should give warning', inst.base_url
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
class CustomBot(Bot):
|
||||
pass # Tests that setting custom attributes of Bot subclass doesn't raise warning
|
||||
|
||||
a = CustomBot(inst.token)
|
||||
a.my_custom = 'no error!'
|
||||
assert len(recwarn) == 1
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'token',
|
||||
|
|
|
@ -31,13 +31,10 @@ class TestBotCommand:
|
|||
command = 'start'
|
||||
description = 'A command'
|
||||
|
||||
def test_slot_behaviour(self, bot_command, recwarn, mro_slots):
|
||||
def test_slot_behaviour(self, bot_command, mro_slots):
|
||||
for attr in bot_command.__slots__:
|
||||
assert getattr(bot_command, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not bot_command.__dict__, f"got missing slot(s): {bot_command.__dict__}"
|
||||
assert len(mro_slots(bot_command)) == len(set(mro_slots(bot_command))), "duplicate slot"
|
||||
bot_command.custom, bot_command.command = 'should give warning', self.command
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_de_json(self, bot):
|
||||
json_dict = {'command': self.command, 'description': self.description}
|
||||
|
|
|
@ -113,15 +113,12 @@ def bot_command_scope(scope_class_and_type, chat_id):
|
|||
|
||||
# All the scope types are very similar, so we test everything via parametrization
|
||||
class TestBotCommandScope:
|
||||
def test_slot_behaviour(self, bot_command_scope, mro_slots, recwarn):
|
||||
def test_slot_behaviour(self, bot_command_scope, mro_slots):
|
||||
for attr in bot_command_scope.__slots__:
|
||||
assert getattr(bot_command_scope, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert not bot_command_scope.__dict__, f"got missing slot(s): {bot_command_scope.__dict__}"
|
||||
assert len(mro_slots(bot_command_scope)) == len(
|
||||
set(mro_slots(bot_command_scope))
|
||||
), "duplicate slot"
|
||||
bot_command_scope.custom, bot_command_scope.type = 'warning!', bot_command_scope.type
|
||||
assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list
|
||||
|
||||
def test_de_json(self, bot, scope_class_and_type, chat_id):
|
||||
cls = scope_class_and_type[0]
|
||||
|
|
|
@ -38,7 +38,7 @@ CallbackContext.refresh_data is tested in TestBasePersistence
|
|||
|
||||
|
||||
class TestCallbackContext:
|
||||
def test_slot_behaviour(self, cdp, recwarn, mro_slots):
|
||||
def test_slot_behaviour(self, cdp, mro_slots, recwarn):
|
||||
c = CallbackContext(cdp)
|
||||
for attr in c.__slots__:
|
||||
assert getattr(c, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
|
|
|
@ -38,15 +38,13 @@ def callback_data_cache(bot):
|
|||
|
||||
|
||||
class TestInvalidCallbackData:
|
||||
def test_slot_behaviour(self, mro_slots, recwarn):
|
||||
def test_slot_behaviour(self, mro_slots):
|
||||
invalid_callback_data = InvalidCallbackData()
|
||||
for attr in invalid_callback_data.__slots__:
|
||||
assert getattr(invalid_callback_data, attr, 'err') != 'err', f"got extra slot '{attr}'"
|
||||
assert len(mro_slots(invalid_callback_data)) == len(
|
||||
set(mro_slots(invalid_callback_data))
|
||||
), "duplicate slot"
|
||||
with pytest.raises(AttributeError):
|
||||
invalid_callback_data.custom
|
||||
|
||||
|
||||
class TestKeyboardData:
|
||||
|
@ -57,8 +55,6 @@ class TestKeyboardData:
|
|||
assert len(mro_slots(keyboard_data)) == len(
|
||||
set(mro_slots(keyboard_data))
|
||||
), "duplicate slot"
|
||||
with pytest.raises(AttributeError):
|
||||
keyboard_data.custom = 42
|
||||
|
||||
|
||||
class TestCallbackDataCache:
|
||||
|
@ -73,8 +69,6 @@ class TestCallbackDataCache:
|
|||
assert len(mro_slots(callback_data_cache)) == len(
|
||||
set(mro_slots(callback_data_cache))
|
||||
), "duplicate slot"
|
||||
with pytest.raises(AttributeError):
|
||||
callback_data_cache.custom = 42
|
||||
|
||||
@pytest.mark.parametrize('maxsize', [1, 5, 2048])
|
||||
def test_init_maxsize(self, maxsize, bot):
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue