diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 000000000..a525644a9 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,20 @@ +version = 1 + +test_patterns = ["tests/**"] + +exclude_patterns = [ + "tests/**", + "docs/**", + "telegram/vendor/**", + "setup.py", + "setup-raw.py" +] + +[[analyzers]] +name = "python" +enabled = true + + [analyzers.meta] + runtime_version = "3.x.x" + max_line_length = 99 + skip_doc_coverage = ["module", "magic", "init", "nonpublic"] diff --git a/README.rst b/README.rst index ebe794e34..f5cda8de7 100644 --- a/README.rst +++ b/README.rst @@ -50,10 +50,14 @@ We have a vibrant community of developers helping each other in our `Telegram gr .. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968 :target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&utm_medium=referral&utm_content=python-telegram-bot/python-telegram-bot&utm_campaign=Badge_Grade - :alt: Code quality + :alt: Code quality: Codacy + +.. image:: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues + :target: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge + :alt: Code quality: DeepSource .. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black + :target: https://github.com/psf/black .. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram :target: https://telegram.me/pythontelegrambotgroup diff --git a/README_RAW.rst b/README_RAW.rst index a15f566ae..aa523981f 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -50,10 +50,14 @@ We have a vibrant community of developers helping each other in our `Telegram gr .. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968 :target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&utm_medium=referral&utm_content=python-telegram-bot/python-telegram-bot&utm_campaign=Badge_Grade - :alt: Code quality + :alt: Code quality: Codacy + +.. image:: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues + :target: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge + :alt: Code quality: DeepSource .. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black + :target: https://github.com/psf/black .. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram :target: https://telegram.me/pythontelegrambotgroup diff --git a/examples/passportbot.py b/examples/passportbot.py index 4c57170a3..7bf2a50b6 100644 --- a/examples/passportbot.py +++ b/examples/passportbot.py @@ -24,6 +24,7 @@ logger = logging.getLogger(__name__) def msg(update: Update, _: CallbackContext) -> None: + """Downloads and prints the received passport data.""" # Retrieve passport data passport_data = update.message.passport_data # If our nonce doesn't match what we think, this Update did not originate from us @@ -61,21 +62,24 @@ def msg(update: Update, _: CallbackContext) -> None: actual_file = file.get_file() print(actual_file) actual_file.download() - if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'): - if data.front_side: - front_file = data.front_side.get_file() - print(data.type, front_file) - front_file.download() - if data.type in ('driver_license' and 'identity_card'): - if data.reverse_side: - reverse_file = data.reverse_side.get_file() - print(data.type, reverse_file) - reverse_file.download() - if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'): - if data.selfie: - selfie_file = data.selfie.get_file() - print(data.type, selfie_file) - selfie_file.download() + if ( + data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport') + and data.front_side + ): + front_file = data.front_side.get_file() + print(data.type, front_file) + front_file.download() + if data.type in ('driver_license' and 'identity_card') and data.reverse_side: + reverse_file = data.reverse_side.get_file() + print(data.type, reverse_file) + reverse_file.download() + if ( + data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport') + and data.selfie + ): + selfie_file = data.selfie.get_file() + print(data.type, selfie_file) + selfie_file.download() if data.type in ( 'passport', 'driver_license', @@ -97,7 +101,8 @@ def msg(update: Update, _: CallbackContext) -> None: def main() -> None: """Start the bot.""" # Create the Updater and pass it your token and private key - updater = Updater("TOKEN", private_key=open('private.key', 'rb').read()) + with open('private.key', 'rb') as private_key: + updater = Updater("TOKEN", private_key=private_key.read()) # Get the dispatcher to register handlers dispatcher = updater.dispatcher diff --git a/telegram/__main__.py b/telegram/__main__.py index 9532748e2..0e8db8276 100644 --- a/telegram/__main__.py +++ b/telegram/__main__.py @@ -29,7 +29,7 @@ from .constants import BOT_API_VERSION def _git_revision() -> Optional[str]: try: - output = subprocess.check_output( + output = subprocess.check_output( # skipcq: BAN-B607 ["git", "describe", "--long", "--tags"], stderr=subprocess.STDOUT ) except (subprocess.SubprocessError, OSError): @@ -37,7 +37,7 @@ def _git_revision() -> Optional[str]: return output.decode().strip() -def print_ver_info() -> None: +def print_ver_info() -> None: # skipcq: PY-D0003 git_revision = _git_revision() print(f'python-telegram-bot {telegram_ver}' + (f' ({git_revision})' if git_revision else '')) print(f'Bot API {BOT_API_VERSION}') @@ -46,7 +46,7 @@ def print_ver_info() -> None: print(f'Python {sys_version}') -def main() -> None: +def main() -> None: # skipcq: PY-D0003 print_ver_info() diff --git a/telegram/base.py b/telegram/base.py index 4009b49ca..f33491384 100644 --- a/telegram/base.py +++ b/telegram/base.py @@ -34,7 +34,7 @@ TO = TypeVar('TO', bound='TelegramObject', covariant=True) class TelegramObject: - """Base class for most telegram objects.""" + """Base class for most Telegram objects.""" _id_attrs: Tuple[object, ...] = () @@ -45,12 +45,22 @@ class TelegramObject: return self.__dict__[item] @staticmethod - def parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]: + def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]: return None if data is None else data.copy() @classmethod def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO]: - data = cls.parse_data(data) + """Converts JSON data to a Telegram object. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`): The bot associated with this object. + + Returns: + The Telegram object. + + """ + data = cls._parse_data(data) if data is None: return None @@ -61,21 +71,35 @@ class TelegramObject: @classmethod def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]: + """Converts JSON data to a list of Telegram objects. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`): The bot associated with these objects. + + Returns: + A list of Telegram objects. + + """ if not data: return [] return [cls.de_json(d, bot) for d in data] def to_json(self) -> str: - """ + """Gives a JSON representation of object. + Returns: :obj:`str` - """ - return json.dumps(self.to_dict()) def to_dict(self) -> JSONDict: + """Gives representation of object as :obj:`dict`. + + Returns: + :obj:`dict` + """ data = {} for key in iter(self.__dict__): diff --git a/telegram/bot.py b/telegram/bot.py index 6051f8fd8..79fcefa2a 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -116,7 +116,7 @@ if TYPE_CHECKING: RT = TypeVar('RT') -def log( +def log( # skipcq: PY-D0003 func: Callable[..., RT], *args: object, **kwargs: object # pylint: disable=W0613 ) -> Callable[..., RT]: logger = logging.getLogger(func.__module__) @@ -301,7 +301,7 @@ class Bot(TelegramObject): return Message.de_json(result, self) # type: ignore[return-value, arg-type] @property - def request(self) -> Request: + def request(self) -> Request: # skip-cq: PY-D0003 return self._request @staticmethod @@ -319,7 +319,6 @@ class Bot(TelegramObject): @property def bot(self) -> User: """:class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`.""" - if self._bot is None: self._bot = self.get_me() return self._bot @@ -327,55 +326,46 @@ class Bot(TelegramObject): @property def id(self) -> int: # pylint: disable=C0103 """:obj:`int`: Unique identifier for this bot.""" - return self.bot.id @property def first_name(self) -> str: """:obj:`str`: Bot's first name.""" - return self.bot.first_name @property def last_name(self) -> str: """:obj:`str`: Optional. Bot's last name.""" - return self.bot.last_name # type: ignore @property def username(self) -> str: """:obj:`str`: Bot's username.""" - return self.bot.username # type: ignore @property def link(self) -> str: """:obj:`str`: Convenience property. Returns the t.me link of the bot.""" - return f"https://t.me/{self.username}" @property def can_join_groups(self) -> bool: """:obj:`bool`: Bot's :attr:`telegram.User.can_join_groups` attribute.""" - return self.bot.can_join_groups # type: ignore @property def can_read_all_group_messages(self) -> bool: """:obj:`bool`: Bot's :attr:`telegram.User.can_read_all_group_messages` attribute.""" - return self.bot.can_read_all_group_messages # type: ignore @property def supports_inline_queries(self) -> bool: """:obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute.""" - return self.bot.supports_inline_queries # type: ignore @property def commands(self) -> List[BotCommand]: """List[:class:`BotCommand`]: Bot's commands.""" - if self._commands is None: self._commands = self.get_my_commands() return self._commands @@ -383,7 +373,6 @@ class Bot(TelegramObject): @property def name(self) -> str: """:obj:`str`: Bot's @username.""" - return f'@{self.username}' @log @@ -2606,7 +2595,6 @@ class Bot(TelegramObject): Raises: :class:`telegram.error.TelegramError` """ - if inline_message_id is None and (chat_id is None or message_id is None): raise ValueError( 'edit_message_media: Both chat_id and message_id are required when ' @@ -4257,7 +4245,6 @@ class Bot(TelegramObject): :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} return self._post( # type: ignore[return-value] @@ -5089,6 +5076,7 @@ class Bot(TelegramObject): return MessageId.de_json(result, self) # type: ignore[return-value, arg-type] def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name} if self.last_name: diff --git a/telegram/callbackquery.py b/telegram/callbackquery.py index 95d0de9d9..370398935 100644 --- a/telegram/callbackquery.py +++ b/telegram/callbackquery.py @@ -113,7 +113,8 @@ class CallbackQuery(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['CallbackQuery']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/chat.py b/telegram/chat.py index 5337a1a22..6a8b1e0f5 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -229,14 +229,16 @@ class Chat(TelegramObject): @property def link(self) -> Optional[str]: """:obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me - link of the chat.""" + link of the chat. + """ if self.username: return f"https://t.me/{self.username}" return None @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Chat']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/chatinvitelink.py b/telegram/chatinvitelink.py index a06782229..ecffe50cc 100644 --- a/telegram/chatinvitelink.py +++ b/telegram/chatinvitelink.py @@ -84,7 +84,8 @@ class ChatInviteLink(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatInviteLink']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -95,6 +96,7 @@ class ChatInviteLink(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['expire_date'] = to_timestamp(self.expire_date) diff --git a/telegram/chatlocation.py b/telegram/chatlocation.py index bb42015ab..3d74fa5f9 100644 --- a/telegram/chatlocation.py +++ b/telegram/chatlocation.py @@ -60,7 +60,8 @@ class ChatLocation(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatLocation']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/chatmember.py b/telegram/chatmember.py index 68141cd3d..722281f39 100644 --- a/telegram/chatmember.py +++ b/telegram/chatmember.py @@ -207,7 +207,8 @@ class ChatMember(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMember']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -218,6 +219,7 @@ class ChatMember(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['until_date'] = to_timestamp(self.until_date) diff --git a/telegram/chatmemberupdated.py b/telegram/chatmemberupdated.py index 5348d0f4a..4304beaf9 100644 --- a/telegram/chatmemberupdated.py +++ b/telegram/chatmemberupdated.py @@ -92,7 +92,8 @@ class ChatMemberUpdated(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMemberUpdated']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -107,6 +108,7 @@ class ChatMemberUpdated(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() # Required diff --git a/telegram/choseninlineresult.py b/telegram/choseninlineresult.py index 24416728e..a7f5cfd01 100644 --- a/telegram/choseninlineresult.py +++ b/telegram/choseninlineresult.py @@ -82,7 +82,8 @@ class ChosenInlineResult(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChosenInlineResult']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/dice.py b/telegram/dice.py index 3c7f8296d..f3715f007 100644 --- a/telegram/dice.py +++ b/telegram/dice.py @@ -70,7 +70,7 @@ class Dice(TelegramObject): self._id_attrs = (self.value, self.emoji) - DICE: ClassVar[str] = constants.DICE_DICE + DICE: ClassVar[str] = constants.DICE_DICE # skipcq: PTC-W0052 """:const:`telegram.constants.DICE_DICE`""" DARTS: ClassVar[str] = constants.DICE_DARTS """:const:`telegram.constants.DICE_DARTS`""" diff --git a/telegram/error.py b/telegram/error.py index 462353774..6a546c449 100644 --- a/telegram/error.py +++ b/telegram/error.py @@ -39,6 +39,8 @@ def _lstrip_str(in_s: str, lstr: str) -> str: class TelegramError(Exception): + """Base class for Telegram errors.""" + def __init__(self, message: str): super().__init__() @@ -58,10 +60,12 @@ class TelegramError(Exception): class Unauthorized(TelegramError): - pass + """Raised when the bot has not enough rights to perform the requested action.""" class InvalidToken(TelegramError): + """Raised when the token is invalid.""" + def __init__(self) -> None: super().__init__('Invalid token') @@ -70,14 +74,16 @@ class InvalidToken(TelegramError): class NetworkError(TelegramError): - pass + """Base class for exceptions due to networking errors.""" class BadRequest(NetworkError): - pass + """Raised when Telegram could not process the request correctly.""" class TimedOut(NetworkError): + """Raised when a request took too long to finish.""" + def __init__(self) -> None: super().__init__('Timed out') @@ -87,6 +93,8 @@ class TimedOut(NetworkError): class ChatMigrated(TelegramError): """ + Raised when the requested group chat migrated to supergroup and has a new chat id. + Args: new_chat_id (:obj:`int`): The new chat id of the group. @@ -102,6 +110,8 @@ class ChatMigrated(TelegramError): class RetryAfter(TelegramError): """ + Raised when flood limits where exceeded. + Args: retry_after (:obj:`int`): Time in seconds, after which the bot can retry the request. @@ -116,13 +126,7 @@ class RetryAfter(TelegramError): class Conflict(TelegramError): - """ - Raised when a long poll or webhook conflicts with another one. - - Args: - msg (:obj:`str`): The message from telegrams server. - - """ + """Raised when a long poll or webhook conflicts with another one.""" def __reduce__(self) -> Tuple[type, Tuple[str]]: return self.__class__, (self.message,) diff --git a/telegram/ext/callbackcontext.py b/telegram/ext/callbackcontext.py index 388ff68a2..7f84016a6 100644 --- a/telegram/ext/callbackcontext.py +++ b/telegram/ext/callbackcontext.py @@ -51,19 +51,6 @@ class CallbackContext: that you think you added will not be present. Attributes: - bot_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each - update it will be the same ``dict``. - chat_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each - update from the same chat id it will be the same ``dict``. - - Warning: - When a group chat migrates to a supergroup, its chat id will change and the - ``chat_data`` needs to be transferred. For details see our `wiki page - `_. - - user_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each - update from the same user it will be the same ``dict``. matches (List[:obj:`re match object`]): Optional. If the associated update originated from a regex-supported handler or had a :class:`Filters.regex`, this will contain a list of match objects for every pattern where ``re.search(pattern, string)`` returned a match. @@ -113,6 +100,9 @@ class CallbackContext: @property def bot_data(self) -> Dict: + """:obj:`dict`: Optional. A dict that can be used to keep any data in. For each + update it will be the same ``dict``. + """ return self._bot_data @bot_data.setter @@ -123,6 +113,15 @@ class CallbackContext: @property def chat_data(self) -> Optional[Dict]: + """:obj:`dict`: Optional. A dict that can be used to keep any data in. For each + update from the same chat id it will be the same ``dict``. + + Warning: + When a group chat migrates to a supergroup, its chat id will change and the + ``chat_data`` needs to be transferred. For details see our `wiki page + `_. + """ return self._chat_data @chat_data.setter @@ -133,6 +132,9 @@ class CallbackContext: @property def user_data(self) -> Optional[Dict]: + """:obj:`dict`: Optional. A dict that can be used to keep any data in. For each + update from the same user it will be the same ``dict``. + """ return self._user_data @user_data.setter @@ -150,6 +152,28 @@ class CallbackContext: async_args: Union[List, Tuple] = None, async_kwargs: Dict[str, object] = None, ) -> 'CallbackContext': + """ + Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the error + handlers. + + .. seealso:: :meth:`telegram.ext.Dispatcher.add_error_handler` + + Args: + update (:obj:`object` | :class:`telegram.Update`): The update associated with the + error. May be :obj:`None`, e.g. for errors in job callbacks. + error (:obj:`Exception`): The error. + dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this + context. + async_args (List[:obj:`object`]): Optional. Positional arguments of the function that + raised the error. Pass only when the raising function was run asynchronously using + :meth:`telegram.ext.Dispatcher.run_async`. + async_kwargs (Dict[:obj:`str`, :obj:`object`]): Optional. Keyword arguments of the + function that raised the error. Pass only when the raising function was run + asynchronously using :meth:`telegram.ext.Dispatcher.run_async`. + + Returns: + :class:`telegram.ext.CallbackContext` + """ self = cls.from_update(update, dispatcher) self.error = error self.async_args = async_args @@ -158,6 +182,20 @@ class CallbackContext: @classmethod def from_update(cls, update: object, dispatcher: 'Dispatcher') -> 'CallbackContext': + """ + Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the + handlers. + + .. seealso:: :meth:`telegram.ext.Dispatcher.add_handler` + + Args: + update (:obj:`object` | :class:`telegram.Update`): The update. + dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this + context. + + Returns: + :class:`telegram.ext.CallbackContext` + """ self = cls(dispatcher) if update is not None and isinstance(update, Update): @@ -172,11 +210,30 @@ class CallbackContext: @classmethod def from_job(cls, job: 'Job', dispatcher: 'Dispatcher') -> 'CallbackContext': + """ + Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to a + job callback. + + .. seealso:: :meth:`telegram.ext.JobQueue` + + Args: + job (:class:`telegram.ext.Job`): The job. + dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this + context. + + Returns: + :class:`telegram.ext.CallbackContext` + """ self = cls(dispatcher) self.job = job return self def update(self, data: Dict[str, object]) -> None: + """Updates ``self.__dict__`` with the passed data. + + Args: + data (Dict[:obj:`str`, :obj:`object`]): The data. + """ self.__dict__.update(data) @property diff --git a/telegram/ext/callbackqueryhandler.py b/telegram/ext/callbackqueryhandler.py index 5992f9130..df0205359 100644 --- a/telegram/ext/callbackqueryhandler.py +++ b/telegram/ext/callbackqueryhandler.py @@ -174,6 +174,10 @@ class CallbackQueryHandler(Handler[Update]): update: Update = None, check_result: Union[bool, Match] = None, ) -> Dict[str, object]: + """Pass the results of ``re.match(pattern, data).{groups(), groupdict()}`` to the + callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if + needed. + """ optional_args = super().collect_optional_args(dispatcher, update, check_result) if self.pattern: check_result = cast(Match, check_result) @@ -190,6 +194,9 @@ class CallbackQueryHandler(Handler[Update]): dispatcher: 'Dispatcher', check_result: Union[bool, Match], ) -> None: + """Add the result of ``re.match(pattern, update.callback_query.data)`` to + :attr:`CallbackContext.matches` as list with one element. + """ if self.pattern: check_result = cast(Match, check_result) context.matches = [check_result] diff --git a/telegram/ext/choseninlineresulthandler.py b/telegram/ext/choseninlineresulthandler.py index bcdcea6bd..6e2b59b1f 100644 --- a/telegram/ext/choseninlineresulthandler.py +++ b/telegram/ext/choseninlineresulthandler.py @@ -150,7 +150,8 @@ class ChosenInlineResultHandler(Handler[Update]): check_result: Union[bool, Match], ) -> None: """This function adds the matched regex pattern result to - :attr:`telegram.ext.CallbackContext.matches`.""" + :attr:`telegram.ext.CallbackContext.matches`. + """ if self.pattern: check_result = cast(Match, check_result) context.matches = [check_result] diff --git a/telegram/ext/commandhandler.py b/telegram/ext/commandhandler.py index 0d25b7f61..f5ab7c4d9 100644 --- a/telegram/ext/commandhandler.py +++ b/telegram/ext/commandhandler.py @@ -219,6 +219,9 @@ class CommandHandler(Handler[Update]): update: Update = None, check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]] = None, ) -> Dict[str, object]: + """Provide text after the command to the callback the ``args`` argument as list, split on + single whitespaces. + """ optional_args = super().collect_optional_args(dispatcher, update) if self.pass_args and isinstance(check_result, tuple): optional_args['args'] = check_result[0] @@ -231,6 +234,9 @@ class CommandHandler(Handler[Update]): dispatcher: 'Dispatcher', 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. + """ if isinstance(check_result, tuple): context.args = check_result[0] if isinstance(check_result[1], dict): @@ -238,7 +244,7 @@ class CommandHandler(Handler[Update]): class PrefixHandler(CommandHandler): - """Handler class to handle custom prefix commands + """Handler class to handle custom prefix commands. This is a intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`. It supports configurable commands with the same options as CommandHandler. It will respond to @@ -265,7 +271,7 @@ class PrefixHandler(CommandHandler): .. code:: python PrefixHandler(['!', '#'], ['test', 'help'], callback) # will respond to '!test', \ -'#test', '!help' and '#help'. + '#test', '!help' and '#help'. By default the handler listens to messages as well as edited messages. To change this behavior @@ -442,15 +448,3 @@ class PrefixHandler(CommandHandler): return text_list[1:], filter_result return False return None - - def collect_additional_context( - self, - context: 'CallbackContext', - update: Update, - dispatcher: 'Dispatcher', - check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]], - ) -> None: - if isinstance(check_result, tuple): - context.args = check_result[0] - if isinstance(check_result[1], dict): - context.update(check_result[1]) diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index f1135c9a8..d4739315a 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -173,33 +173,8 @@ class ConversationHandler(Handler[Update]): ValueError Attributes: - entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can - trigger the start of the conversation. - states (Dict[:obj:`object`, List[:class:`telegram.ext.Handler`]]): A :obj:`dict` that - defines the different states of conversation a user can be in and one or more - associated ``Handler`` objects that should be used in that state. - fallbacks (List[:class:`telegram.ext.Handler`]): 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 :attr:`check_update`. - allow_reentry (:obj:`bool`): Determines if a user can restart a conversation with - an entry point. - per_chat (:obj:`bool`): If the conversationkey should contain the Chat's ID. - per_user (:obj:`bool`): If the conversationkey should contain the User's ID. - per_message (:obj:`bool`): If the conversationkey should contain the Message's - ID. - conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`): Optional. When this - handler is inactive more than this timeout (in seconds), it will be automatically - ended. If this value is 0 (default), there will be no timeout. When it's triggered, the - last received update and the corresponding ``context`` will be handled by ALL the - handler's who's :attr:`check_update` method returns :obj:`True` that are in the state - :attr:`ConversationHandler.TIMEOUT`. - name (:obj:`str`): Optional. The name for this conversationhandler. Required for - persistence persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be saved. Name is required and persistence has to be set in :class:`telegram.ext.Updater` - map_to_parent (Dict[:obj:`object`, :obj:`object`]): Optional. A :obj:`dict` that can be - used to instruct a nested conversationhandler to transition into a mapped state on - its parent conversationhandler in place of a specified nested state. run_async (:obj:`bool`): If :obj:`True`, will override the :attr:`Handler.run_async` setting of all internal handlers on initialization. @@ -316,6 +291,9 @@ class ConversationHandler(Handler[Update]): @property def entry_points(self) -> List[Handler]: + """List[:class:`telegram.ext.Handler`]: A list of ``Handler`` objects that can trigger the + start of the conversation. + """ return self._entry_points @entry_points.setter @@ -324,6 +302,10 @@ class ConversationHandler(Handler[Update]): @property def states(self) -> Dict[object, List[Handler]]: + """Dict[:obj:`object`, List[:class:`telegram.ext.Handler`]]: A :obj:`dict` that + defines the different states of conversation a user can be in and one or more + associated ``Handler`` objects that should be used in that state. + """ return self._states @states.setter @@ -332,6 +314,10 @@ class ConversationHandler(Handler[Update]): @property def fallbacks(self) -> List[Handler]: + """List[:class:`telegram.ext.Handler`]: 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 :attr:`check_update`. + """ return self._fallbacks @fallbacks.setter @@ -344,10 +330,12 @@ class ConversationHandler(Handler[Update]): @allow_reentry.setter def allow_reentry(self, value: object) -> NoReturn: + """:obj:`bool`: Determines if a user can restart a conversation with an entry point.""" raise ValueError('You can not assign a new value to allow_reentry after initialization.') @property def per_user(self) -> bool: + """:obj:`bool`: If the conversation key should contain the User's ID.""" return self._per_user @per_user.setter @@ -356,6 +344,7 @@ class ConversationHandler(Handler[Update]): @property def per_chat(self) -> bool: + """:obj:`bool`: If the conversation key should contain the Chat's ID.""" return self._per_chat @per_chat.setter @@ -364,6 +353,7 @@ class ConversationHandler(Handler[Update]): @property def per_message(self) -> bool: + """:obj:`bool`: If the conversation key should contain the message's ID.""" return self._per_message @per_message.setter @@ -374,16 +364,21 @@ class ConversationHandler(Handler[Update]): def conversation_timeout( self, ) -> Optional[Union[float, datetime.timedelta]]: + """:obj:`float` | :obj:`datetime.timedelta`: Optional. When this + handler is inactive more than this timeout (in seconds), it will be automatically + ended. + """ return self._conversation_timeout @conversation_timeout.setter def conversation_timeout(self, value: object) -> NoReturn: raise ValueError( - 'You can not assign a new value to conversation_timeout after ' 'initialization.' + 'You can not assign a new value to conversation_timeout after initialization.' ) @property def name(self) -> Optional[str]: + """:obj:`str`: Optional. The name for this :class:`ConversationHandler`.""" return self._name @name.setter @@ -392,6 +387,10 @@ class ConversationHandler(Handler[Update]): @property 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. + """ return self._map_to_parent @map_to_parent.setter @@ -400,6 +399,7 @@ class ConversationHandler(Handler[Update]): @property def persistence(self) -> Optional[BasePersistence]: + """The persistence class as provided by the :class:`Dispatcher`.""" return self._persistence @persistence.setter @@ -412,7 +412,7 @@ class ConversationHandler(Handler[Update]): handler.persistence = self.persistence @property - def conversations(self) -> ConversationDict: + def conversations(self) -> ConversationDict: # skipcq: PY-D0003 return self._conversations @conversations.setter @@ -518,7 +518,7 @@ class ConversationHandler(Handler[Update]): # check if promise is finished or not if state[1].done.wait(0): res = self._resolve_promise(state) - self.update_state(res, key) + self._update_state(res, key) with self._conversations_lock: state = self.conversations.get(key) @@ -627,19 +627,19 @@ class ConversationHandler(Handler[Update]): ) if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent: - self.update_state(self.END, conversation_key) + self._update_state(self.END, conversation_key) if raise_dp_handler_stop: raise DispatcherHandlerStop(self.map_to_parent.get(new_state)) return self.map_to_parent.get(new_state) - self.update_state(new_state, conversation_key) + self._update_state(new_state, conversation_key) if raise_dp_handler_stop: # Don't pass the new state here. If we're in a nested conversation, the parent is # expecting None as return value. raise DispatcherHandlerStop() return None - def update_state(self, new_state: object, key: Tuple[int, ...]) -> None: + def _update_state(self, new_state: object, key: Tuple[int, ...]) -> None: if new_state == self.END: with self._conversations_lock: if key in self.conversations: @@ -698,4 +698,4 @@ class ConversationHandler(Handler[Update]): 'ConversationHandler has no effect. Ignoring.' ) - self.update_state(self.END, ctxt.conversation_key) + self._update_state(self.END, ctxt.conversation_key) diff --git a/telegram/ext/defaults.py b/telegram/ext/defaults.py index 7e8e9fcdd..c9d66a3a8 100644 --- a/telegram/ext/defaults.py +++ b/telegram/ext/defaults.py @@ -54,29 +54,6 @@ class Defaults: run_async (:obj:`bool`, optional): Default setting for the ``run_async`` parameter of handlers and error handlers registered through :meth:`Dispatcher.add_handler` and :meth:`Dispatcher.add_error_handler`. Defaults to :obj:`False`. - - Attributes: - parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show - bold, italic, fixed-width text or URLs in your bot's message. - explanation_parse_mode (:obj:`str`): Optional. Alias for :attr:`parse_mode`, used for - the corresponding parameter of :meth:`telegram.Bot.send_poll`. - disable_notification (:obj:`bool`): Optional. Sends the message silently. Users will - receive a notification with no sound. - disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in this - message. - allow_sending_without_reply (:obj:`bool`): Optional. Pass :obj:`True`, if the message - should be sent even if the specified replied-to message is not found. - timeout (:obj:`int` | :obj:`float`): Optional. If this value is specified, use it as the - read timeout from the server (instead of the one specified during creation of the - connection pool). - quote (:obj:`bool`): Optional. If set to :obj:`True`, the reply is sent as an actual reply - to the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will - be ignored. Default: :obj:`True` in group chats and :obj:`False` in private chats. - tzinfo (:obj:`tzinfo`): A timezone to be used for all date(time) objects appearing - throughout PTB. - run_async (:obj:`bool`): Optional. Default setting for the ``run_async`` parameter of - handlers and error handlers registered through :meth:`Dispatcher.add_handler` and - :meth:`Dispatcher.add_error_handler`. """ def __init__( @@ -118,11 +95,14 @@ class Defaults: self._api_defaults['timeout'] = self.timeout @property - def api_defaults(self) -> Dict[str, Any]: + def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003 return self._api_defaults @property def parse_mode(self) -> Optional[str]: + """:obj:`str`: Optional. Send Markdown or HTML, if you want Telegram apps to show + bold, italic, fixed-width text or URLs in your bot's message. + """ return self._parse_mode @parse_mode.setter @@ -134,6 +114,9 @@ class Defaults: @property def explanation_parse_mode(self) -> Optional[str]: + """:obj:`str`: Optional. Alias for :attr:`parse_mode`, used for + the corresponding parameter of :meth:`telegram.Bot.send_poll`. + """ return self._parse_mode @explanation_parse_mode.setter @@ -145,6 +128,9 @@ class Defaults: @property def disable_notification(self) -> Optional[bool]: + """:obj:`bool`: Optional. Sends the message silently. Users will + receive a notification with no sound. + """ return self._disable_notification @disable_notification.setter @@ -156,6 +142,9 @@ class Defaults: @property def disable_web_page_preview(self) -> Optional[bool]: + """:obj:`bool`: Optional. Disables link previews for links in this + message. + """ return self._disable_web_page_preview @disable_web_page_preview.setter @@ -167,6 +156,9 @@ class Defaults: @property def allow_sending_without_reply(self) -> Optional[bool]: + """:obj:`bool`: Optional. Pass :obj:`True`, if the message + should be sent even if the specified replied-to message is not found. + """ return self._allow_sending_without_reply @allow_sending_without_reply.setter @@ -178,6 +170,10 @@ class Defaults: @property def timeout(self) -> ODVInput[float]: + """:obj:`int` | :obj:`float`: Optional. If this value is specified, use it as the + read timeout from the server (instead of the one specified during creation of the + connection pool). + """ return self._timeout @timeout.setter @@ -189,6 +185,10 @@ class Defaults: @property def quote(self) -> Optional[bool]: + """:obj:`bool`: Optional. If set to :obj:`True`, the reply is sent as an actual reply + to the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will + be ignored. Default: :obj:`True` in group chats and :obj:`False` in private chats. + """ return self._quote @quote.setter @@ -200,6 +200,9 @@ class Defaults: @property def tzinfo(self) -> pytz.BaseTzInfo: + """:obj:`tzinfo`: A timezone to be used for all date(time) objects appearing + throughout PTB. + """ return self._tzinfo @tzinfo.setter @@ -211,6 +214,10 @@ class Defaults: @property def run_async(self) -> bool: + """:obj:`bool`: Optional. Default setting for the ``run_async`` parameter of + handlers and error handlers registered through :meth:`Dispatcher.add_handler` and + :meth:`Dispatcher.add_error_handler`. + """ return self._run_async @run_async.setter diff --git a/telegram/ext/dispatcher.py b/telegram/ext/dispatcher.py index 13496bff4..9ca87046b 100644 --- a/telegram/ext/dispatcher.py +++ b/telegram/ext/dispatcher.py @@ -216,7 +216,7 @@ class Dispatcher: self._set_singleton(None) @property - def exception_event(self) -> Event: + def exception_event(self) -> Event: # skipcq: PY-D0003 return self.__exception_event def _init_async_threads(self, base_name: str, workers: int) -> None: @@ -404,7 +404,7 @@ class Dispatcher: self.logger.debug('async thread %s/%s has ended', i + 1, total) @property - def has_running_threads(self) -> bool: + def has_running_threads(self) -> bool: # skipcq: PY-D0003 return self.running or bool(self.__async_threads) def process_update(self, update: object) -> None: @@ -422,7 +422,6 @@ class Dispatcher: The update to process. """ - # An error happened while polling if isinstance(update, TelegramError): try: @@ -637,9 +636,8 @@ class Dispatcher: self.logger.debug('The callback is already registered as an error handler. Ignoring.') return - if run_async is DEFAULT_FALSE and self.bot.defaults: - if self.bot.defaults.run_async: - run_async = True + if run_async is DEFAULT_FALSE and self.bot.defaults and self.bot.defaults.run_async: + run_async = True self.error_handlers[callback] = run_async diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 41ccb7f67..27e788da4 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -117,7 +117,7 @@ class BaseFilter(ABC): @abstractmethod def __call__(self, update: Update) -> Optional[Union[bool, DataDict]]: - pass + ... def __and__(self, other: 'BaseFilter') -> 'BaseFilter': return MergedFilter(self, and_filter=other) @@ -652,17 +652,13 @@ class Filters: """ def __init__(self, category: Optional[str]): - """Initialize the category you want to filter - - Args: - category (str, optional): category of the media you want to filter""" - self.category = category - self.name = f"Filters.document.category('{self.category}')" + self._category = category + self.name = f"Filters.document.category('{self._category}')" def filter(self, message: Message) -> bool: """""" # remove method from docs if message.document: - return message.document.mime_type.startswith(self.category) + return message.document.mime_type.startswith(self._category) return False application = category('application/') @@ -685,10 +681,6 @@ class Filters: """ def __init__(self, mimetype: Optional[str]): - """Initialize the category you want to filter - - Args: - mimetype (str, optional): mime_type of the media you want to filter""" self.mimetype = mimetype self.name = f"Filters.document.mime_type('{self.mimetype}')" @@ -752,29 +744,29 @@ class Filters: """ self.is_case_sensitive = case_sensitive if file_extension is None: - self.file_extension = None + self._file_extension = None self.name = "Filters.document.file_extension(None)" - elif case_sensitive: - self.file_extension = f".{file_extension}" + elif self.is_case_sensitive: + self._file_extension = f".{file_extension}" self.name = ( f"Filters.document.file_extension({file_extension!r}," " case_sensitive=True)" ) else: - self.file_extension = f".{file_extension}".lower() + self._file_extension = f".{file_extension}".lower() self.name = f"Filters.document.file_extension({file_extension.lower()!r})" def filter(self, message: Message) -> bool: """""" # remove method from docs if message.document is None: return False - if self.file_extension is None: + if self._file_extension is None: return "." not in message.document.file_name if self.is_case_sensitive: filename = message.document.file_name else: filename = message.document.file_name.lower() - return filename.endswith(self.file_extension) + return filename.endswith(self._file_extension) def filter(self, message: Message) -> bool: return bool(message.document) @@ -1328,7 +1320,7 @@ officedocument.wordprocessingml.document")``. private: Updates sent in private chat """ - class _ChatUserBaseFilter(MessageFilter): + class _ChatUserBaseFilter(MessageFilter, ABC): def __init__( self, chat_id: SLT[int] = None, @@ -1348,7 +1340,7 @@ officedocument.wordprocessingml.document")``. @abstractmethod def get_chat_or_user(self, message: Message) -> Union[Chat, User, None]: - pass + ... @staticmethod def _parse_chat_id(chat_id: SLT[int]) -> Set[int]: diff --git a/telegram/ext/handler.py b/telegram/ext/handler.py index d4bc46f64..c2fbce33b 100644 --- a/telegram/ext/handler.py +++ b/telegram/ext/handler.py @@ -149,9 +149,12 @@ class Handler(Generic[UT], ABC): """ run_async = self.run_async - if self.run_async is DEFAULT_FALSE and dispatcher.bot.defaults: - if dispatcher.bot.defaults.run_async: - run_async = True + if ( + self.run_async is DEFAULT_FALSE + and dispatcher.bot.defaults + and dispatcher.bot.defaults.run_async + ): + run_async = True if context: self.collect_additional_context(context, update, dispatcher, check_result) diff --git a/telegram/ext/inlinequeryhandler.py b/telegram/ext/inlinequeryhandler.py index 24d259338..9e3f6f279 100644 --- a/telegram/ext/inlinequeryhandler.py +++ b/telegram/ext/inlinequeryhandler.py @@ -16,7 +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 contains the InlineQueryHandler class """ +"""This module contains the InlineQueryHandler class.""" import re from typing import ( TYPE_CHECKING, @@ -170,7 +170,6 @@ class InlineQueryHandler(Handler[Update]): :obj:`bool` """ - if isinstance(update, Update) and update.inline_query: if (self.chat_types is not None) and ( update.inline_query.chat_type not in self.chat_types @@ -191,6 +190,10 @@ class InlineQueryHandler(Handler[Update]): update: Update = None, check_result: Optional[Union[bool, Match]] = None, ) -> Dict[str, object]: + """Pass the results of ``re.match(pattern, query).{groups(), groupdict()}`` to the + callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if + needed. + """ optional_args = super().collect_optional_args(dispatcher, update, check_result) if self.pattern: check_result = cast(Match, check_result) @@ -207,6 +210,9 @@ class InlineQueryHandler(Handler[Update]): dispatcher: 'Dispatcher', check_result: Optional[Union[bool, Match]], ) -> None: + """Add the result of ``re.match(pattern, update.inline_query.query)`` to + :attr:`CallbackContext.matches` as list with one element. + """ if self.pattern: check_result = cast(Match, check_result) context.matches = [check_result] diff --git a/telegram/ext/jobqueue.py b/telegram/ext/jobqueue.py index 240c45d4f..9ea4722d2 100644 --- a/telegram/ext/jobqueue.py +++ b/telegram/ext/jobqueue.py @@ -38,11 +38,6 @@ if TYPE_CHECKING: import apscheduler.job # noqa: F401 -class Days: - MON, TUE, WED, THU, FRI, SAT, SUN = range(7) - EVERY_DAY = tuple(range(7)) - - class JobQueue: """This class allows you to periodically perform tasks with the bot. It is a convenience wrapper for the APScheduler library. @@ -136,8 +131,7 @@ class JobQueue: """ self._dispatcher = dispatcher if dispatcher.bot.defaults: - if dispatcher.bot.defaults: - self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc) + self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc) def run_once( self, @@ -392,7 +386,7 @@ class JobQueue: self, callback: Callable[['CallbackContext'], None], time: datetime.time, - days: Tuple[int, ...] = Days.EVERY_DAY, + days: Tuple[int, ...] = tuple(range(7)), context: object = None, name: str = None, job_kwargs: JSONDict = None, @@ -499,14 +493,16 @@ class JobQueue: self.scheduler.shutdown() def jobs(self) -> Tuple['Job', ...]: - """ - Returns a tuple of all *pending/scheduled* jobs that are currently in the ``JobQueue``. - """ - return tuple(Job.from_aps_job(job, self) for job in self.scheduler.get_jobs()) + """Returns a tuple of all *scheduled* jobs that are currently in the ``JobQueue``.""" + return tuple( + Job._from_aps_job(job, self) # pylint: disable=W0212 + for job in self.scheduler.get_jobs() + ) def get_jobs_by_name(self, name: str) -> Tuple['Job', ...]: """Returns a tuple of all *pending/scheduled* jobs with the given name that are currently - in the ``JobQueue``""" + in the ``JobQueue``. + """ return tuple(job for job in self.jobs() if job.name == name) @@ -563,7 +559,7 @@ class Job: self._removed = False self._enabled = False - self.job = cast(APSJob, job) + self.job = cast(APSJob, job) # skipcq: PTC-W0052 def run(self, dispatcher: 'Dispatcher') -> None: """Executes the callback function independently of the jobs schedule.""" @@ -619,7 +615,7 @@ class Job: return self.job.next_run_time @classmethod - def from_aps_job(cls, job: APSJob, job_queue: JobQueue) -> 'Job': + def _from_aps_job(cls, job: APSJob, job_queue: JobQueue) -> 'Job': # context based callbacks if len(job.args) == 1: context = job.args[0].job.context diff --git a/telegram/ext/messagehandler.py b/telegram/ext/messagehandler.py index 4e03ecef2..270c40481 100644 --- a/telegram/ext/messagehandler.py +++ b/telegram/ext/messagehandler.py @@ -200,5 +200,6 @@ class MessageHandler(Handler[Update]): dispatcher: 'Dispatcher', check_result: Optional[Union[bool, Dict[str, object]]], ) -> None: + """Adds possible output of data filters to the :class:`CallbackContext`.""" if isinstance(check_result, dict): context.update(check_result) diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py index 9a7691837..449396b71 100644 --- a/telegram/ext/messagequeue.py +++ b/telegram/ext/messagequeue.py @@ -113,7 +113,6 @@ class DelayQueue(threading.Thread): automatically called by autostart argument. """ - times: List[float] = [] # used to store each callable processing time while True: item = self._queue.get() @@ -150,7 +149,6 @@ class DelayQueue(threading.Thread): Defaults to :obj:`None`. """ - self.__exit_req = True # gently request self._queue.put(None) # put something to unfreeze if frozen super().join(timeout=timeout) @@ -162,7 +160,6 @@ class DelayQueue(threading.Thread): by subclasses. """ - raise exc def __call__(self, func: Callable, *args: object, **kwargs: object) -> None: @@ -175,7 +172,6 @@ class DelayQueue(threading.Thread): **kwargs (:obj:`dict`): Arbitrary keyword-arguments to `func`. """ - if not self.is_alive() or self.__exit_req: raise DelayQueueError('Could not process callback in stopped thread') self._queue.put((func, args, kwargs)) @@ -252,6 +248,7 @@ class MessageQueue: self._group_delayq.start() def stop(self, timeout: float = None) -> None: + """Stops the ``MessageQueue``.""" self._group_delayq.stop(timeout=timeout) self._all_delayq.stop(timeout=timeout) @@ -281,7 +278,6 @@ class MessageQueue: :obj:`callable`: Used as ``promise`` argument. """ - if not is_group_msg: # ignore middle group delay self._all_delayq(promise) else: # use middle group delay diff --git a/telegram/ext/picklepersistence.py b/telegram/ext/picklepersistence.py index 72a40a759..94fcb256a 100644 --- a/telegram/ext/picklepersistence.py +++ b/telegram/ext/picklepersistence.py @@ -95,7 +95,7 @@ class PicklePersistence(BasePersistence): self.bot_data: Optional[Dict] = None self.conversations: Optional[Dict[str, Dict[Tuple, object]]] = None - def load_singlefile(self) -> None: + def _load_singlefile(self) -> None: try: filename = self.filename with open(self.filename, "rb") as file: @@ -116,7 +116,7 @@ class PicklePersistence(BasePersistence): raise TypeError(f"Something went wrong unpickling {filename}") from exc @staticmethod - def load_file(filename: str) -> Any: + def _load_file(filename: str) -> Any: try: with open(filename, "rb") as file: return pickle.load(file) @@ -127,7 +127,7 @@ class PicklePersistence(BasePersistence): except Exception as exc: raise TypeError(f"Something went wrong unpickling {filename}") from exc - def dump_singlefile(self) -> None: + def _dump_singlefile(self) -> None: with open(self.filename, "wb") as file: data = { 'conversations': self.conversations, @@ -138,7 +138,7 @@ class PicklePersistence(BasePersistence): pickle.dump(data, file) @staticmethod - def dump_file(filename: str, data: object) -> None: + def _dump_file(filename: str, data: object) -> None: with open(filename, "wb") as file: pickle.dump(data, file) @@ -152,14 +152,14 @@ class PicklePersistence(BasePersistence): pass elif not self.single_file: filename = f"{self.filename}_user_data" - data = self.load_file(filename) + data = self._load_file(filename) if not data: data = defaultdict(dict) else: data = defaultdict(dict, data) self.user_data = data else: - self.load_singlefile() + self._load_singlefile() return deepcopy(self.user_data) # type: ignore[arg-type] def get_chat_data(self) -> DefaultDict[int, Dict[object, object]]: @@ -172,14 +172,14 @@ class PicklePersistence(BasePersistence): pass elif not self.single_file: filename = f"{self.filename}_chat_data" - data = self.load_file(filename) + data = self._load_file(filename) if not data: data = defaultdict(dict) else: data = defaultdict(dict, data) self.chat_data = data else: - self.load_singlefile() + self._load_singlefile() return deepcopy(self.chat_data) # type: ignore[arg-type] def get_bot_data(self) -> Dict[object, object]: @@ -192,12 +192,12 @@ class PicklePersistence(BasePersistence): pass elif not self.single_file: filename = f"{self.filename}_bot_data" - data = self.load_file(filename) + data = self._load_file(filename) if not data: data = {} self.bot_data = data else: - self.load_singlefile() + self._load_singlefile() return deepcopy(self.bot_data) # type: ignore[arg-type] def get_conversations(self, name: str) -> ConversationDict: @@ -213,12 +213,12 @@ class PicklePersistence(BasePersistence): pass elif not self.single_file: filename = f"{self.filename}_conversations" - data = self.load_file(filename) + data = self._load_file(filename) if not data: data = {name: {}} self.conversations = data else: - self.load_singlefile() + self._load_singlefile() return self.conversations.get(name, {}).copy() # type: ignore[union-attr] def update_conversation( @@ -240,9 +240,9 @@ class PicklePersistence(BasePersistence): if not self.on_flush: if not self.single_file: filename = f"{self.filename}_conversations" - self.dump_file(filename, self.conversations) + self._dump_file(filename, self.conversations) else: - self.dump_singlefile() + self._dump_singlefile() def update_user_data(self, user_id: int, data: Dict) -> None: """Will update the user_data and depending on :attr:`on_flush` save the pickle file. @@ -259,9 +259,9 @@ class PicklePersistence(BasePersistence): if not self.on_flush: if not self.single_file: filename = f"{self.filename}_user_data" - self.dump_file(filename, self.user_data) + self._dump_file(filename, self.user_data) else: - self.dump_singlefile() + self._dump_singlefile() def update_chat_data(self, chat_id: int, data: Dict) -> None: """Will update the chat_data and depending on :attr:`on_flush` save the pickle file. @@ -278,9 +278,9 @@ class PicklePersistence(BasePersistence): if not self.on_flush: if not self.single_file: filename = f"{self.filename}_chat_data" - self.dump_file(filename, self.chat_data) + self._dump_file(filename, self.chat_data) else: - self.dump_singlefile() + self._dump_singlefile() def update_bot_data(self, data: Dict) -> None: """Will update the bot_data and depending on :attr:`on_flush` save the pickle file. @@ -294,21 +294,21 @@ class PicklePersistence(BasePersistence): if not self.on_flush: if not self.single_file: filename = f"{self.filename}_bot_data" - self.dump_file(filename, self.bot_data) + self._dump_file(filename, self.bot_data) else: - self.dump_singlefile() + self._dump_singlefile() def flush(self) -> None: """Will save all data in memory to pickle file(s).""" if self.single_file: if self.user_data or self.chat_data or self.bot_data or self.conversations: - self.dump_singlefile() + self._dump_singlefile() else: if self.user_data: - self.dump_file(f"{self.filename}_user_data", self.user_data) + self._dump_file(f"{self.filename}_user_data", self.user_data) if self.chat_data: - self.dump_file(f"{self.filename}_chat_data", self.chat_data) + self._dump_file(f"{self.filename}_chat_data", self.chat_data) if self.bot_data: - self.dump_file(f"{self.filename}_bot_data", self.bot_data) + self._dump_file(f"{self.filename}_bot_data", self.bot_data) if self.conversations: - self.dump_file(f"{self.filename}_conversations", self.conversations) + self._dump_file(f"{self.filename}_conversations", self.conversations) diff --git a/telegram/ext/regexhandler.py b/telegram/ext/regexhandler.py index 176620d2c..4392c6e59 100644 --- a/telegram/ext/regexhandler.py +++ b/telegram/ext/regexhandler.py @@ -150,6 +150,10 @@ class RegexHandler(MessageHandler): update: Update = None, check_result: Optional[Union[bool, Dict[str, Any]]] = None, ) -> Dict[str, object]: + """Pass the results of ``re.match(pattern, text).{groups(), groupdict()}`` to the + callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if + needed. + """ optional_args = super().collect_optional_args(dispatcher, update, check_result) if isinstance(check_result, dict): if self.pass_groups: diff --git a/telegram/ext/stringcommandhandler.py b/telegram/ext/stringcommandhandler.py index cee6e821e..0da3574fe 100644 --- a/telegram/ext/stringcommandhandler.py +++ b/telegram/ext/stringcommandhandler.py @@ -32,6 +32,9 @@ RT = TypeVar('RT') class StringCommandHandler(Handler[str]): """Handler class to handle string commands. Commands are string updates that start with ``/``. + The handler will add a ``list`` to the + :class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings, + which is the text following the command split on single whitespace characters. Note: This handler is not used to handle Telegram :attr:`telegram.Update`, but strings manually @@ -122,6 +125,9 @@ class StringCommandHandler(Handler[str]): update: str = None, check_result: Optional[List[str]] = None, ) -> Dict[str, object]: + """Provide text after the command to the callback the ``args`` argument as list, split on + single whitespaces. + """ optional_args = super().collect_optional_args(dispatcher, update, check_result) if self.pass_args: optional_args['args'] = check_result @@ -134,4 +140,7 @@ class StringCommandHandler(Handler[str]): dispatcher: 'Dispatcher', check_result: Optional[List[str]], ) -> None: + """Add text after the command to :attr:`CallbackContext.args` as list, split on single + whitespaces. + """ context.args = check_result diff --git a/telegram/ext/stringregexhandler.py b/telegram/ext/stringregexhandler.py index 1236656a7..9af0910af 100644 --- a/telegram/ext/stringregexhandler.py +++ b/telegram/ext/stringregexhandler.py @@ -137,6 +137,10 @@ class StringRegexHandler(Handler[str]): update: str = None, check_result: Optional[Match] = None, ) -> Dict[str, object]: + """Pass the results of ``re.match(pattern, update).{groups(), groupdict()}`` to the + callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if + needed. + """ optional_args = super().collect_optional_args(dispatcher, update, check_result) if self.pattern: if self.pass_groups and check_result: @@ -152,5 +156,8 @@ class StringRegexHandler(Handler[str]): dispatcher: 'Dispatcher', check_result: Optional[Match], ) -> None: + """Add the result of ``re.match(pattern, update)`` to :attr:`CallbackContext.matches` as + list with one element. + """ if self.pattern and check_result: context.matches = [check_result] diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index 6ac01eeed..aceb7da59 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -547,9 +547,9 @@ class Updater: if current_interval == 0: current_interval = 1 elif current_interval < 30: - current_interval += current_interval / 2 - elif current_interval > 30: - current_interval = 30 + current_interval *= 1.5 + else: + current_interval = min(30.0, current_interval) return current_interval @no_type_check @@ -598,14 +598,17 @@ class Updater: webhook_url = self._gen_webhook_url(listen, port, url_path) # We pass along the cert to the webhook if present. + cert_file = open(cert, 'rb') if cert is not None else None self._bootstrap( max_retries=bootstrap_retries, drop_pending_updates=drop_pending_updates, webhook_url=webhook_url, allowed_updates=allowed_updates, - cert=open(cert, 'rb') if cert is not None else None, + cert=cert_file, ip_address=ip_address, ) + if cert_file is not None: + cert_file.close() self.httpd.serve_forever(ready=ready) @@ -681,7 +684,6 @@ class Updater: def stop(self) -> None: """Stops the polling/webhook thread, the dispatcher and the job queue.""" - self.job_queue.stop() with self.__lock: if self.running or self.dispatcher.has_running_threads: @@ -722,7 +724,7 @@ class Updater: self.__threads = [] @no_type_check - def signal_handler(self, signum, frame) -> None: + def _signal_handler(self, signum, frame) -> None: self.is_idle = False if self.running: self.logger.info( @@ -752,7 +754,7 @@ class Updater: """ for sig in stop_signals: - signal(sig, self.signal_handler) + signal(sig, self._signal_handler) self.is_idle = True diff --git a/telegram/ext/utils/promise.py b/telegram/ext/utils/promise.py index 48508e074..026d5f5ae 100644 --- a/telegram/ext/utils/promise.py +++ b/telegram/ext/utils/promise.py @@ -75,7 +75,6 @@ class Promise: def run(self) -> None: """Calls the :attr:`pooled_function` callable.""" - try: self._result = self.pooled_function(*self.args, **self.kwargs) @@ -133,5 +132,6 @@ class Promise: @property def exception(self) -> Optional[Exception]: """The exception raised by :attr:`pooled_function` or ``None`` if no exception has been - raised (yet).""" + raised (yet). + """ return self._exception diff --git a/telegram/ext/utils/webhookhandler.py b/telegram/ext/utils/webhookhandler.py index e87c76abd..e96234d1a 100644 --- a/telegram/ext/utils/webhookhandler.py +++ b/telegram/ext/utils/webhookhandler.py @@ -91,7 +91,7 @@ class WebhookAppClass(tornado.web.Application): handlers = [(rf"{webhook_path}/?", WebhookHandler, self.shared_objects)] # noqa tornado.web.Application.__init__(self, handlers) # type: ignore - def log_request(self, handler: tornado.web.RequestHandler) -> None: + def log_request(self, handler: tornado.web.RequestHandler) -> None: # skipcq: PTC-W0049 pass diff --git a/telegram/files/animation.py b/telegram/files/animation.py index 3f90dcc29..5e3c9e651 100644 --- a/telegram/files/animation.py +++ b/telegram/files/animation.py @@ -96,7 +96,8 @@ class Animation(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Animation']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/files/audio.py b/telegram/files/audio.py index bffeacec9..032b2a92d 100644 --- a/telegram/files/audio.py +++ b/telegram/files/audio.py @@ -100,7 +100,8 @@ class Audio(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Audio']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/files/document.py b/telegram/files/document.py index e70dd2323..aca4047eb 100644 --- a/telegram/files/document.py +++ b/telegram/files/document.py @@ -87,7 +87,8 @@ class Document(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Document']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/files/file.py b/telegram/files/file.py index c8b0c40c7..e8278c58f 100644 --- a/telegram/files/file.py +++ b/telegram/files/file.py @@ -195,4 +195,9 @@ class File(TelegramObject): return buf def set_credentials(self, credentials: 'FileCredentials') -> None: + """Sets the passport credentials for the file. + + Args: + credentials (:class:`telegram.FileCredentials`): The credentials. + """ self._credentials = credentials diff --git a/telegram/files/inputfile.py b/telegram/files/inputfile.py index a5c7b2402..43a8148ed 100644 --- a/telegram/files/inputfile.py +++ b/telegram/files/inputfile.py @@ -75,7 +75,7 @@ class InputFile: self.filename = self.mimetype.replace('/', '.') @property - def field_tuple(self) -> Tuple[str, bytes, str]: + def field_tuple(self) -> Tuple[str, bytes, str]: # skipcq: PY-D0003 return self.filename, self.input_file_content, self.mimetype @staticmethod @@ -102,10 +102,11 @@ class InputFile: return None @staticmethod - def is_file(obj: object) -> bool: + def is_file(obj: object) -> bool: # skipcq: PY-D0003 return hasattr(obj, 'read') def to_dict(self) -> Optional[str]: + """See :meth:`telegram.TelegramObject.to_dict`.""" if self.attach: return 'attach://' + self.attach return None diff --git a/telegram/files/inputmedia.py b/telegram/files/inputmedia.py index 501e26a28..a9dec806a 100644 --- a/telegram/files/inputmedia.py +++ b/telegram/files/inputmedia.py @@ -46,6 +46,7 @@ class InputMedia(TelegramObject): caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...], None] = None def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() if self.caption_entities: diff --git a/telegram/files/sticker.py b/telegram/files/sticker.py index 0703b6525..429a12f75 100644 --- a/telegram/files/sticker.py +++ b/telegram/files/sticker.py @@ -106,7 +106,8 @@ class Sticker(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Sticker']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -181,6 +182,7 @@ class StickerSet(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['StickerSet']: + """See :meth:`telegram.TelegramObject.de_json`.""" if not data: return None @@ -190,6 +192,7 @@ class StickerSet(TelegramObject): return cls(bot=bot, **data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['stickers'] = [s.to_dict() for s in data.get('stickers')] @@ -249,7 +252,8 @@ class MaskPosition(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MaskPosition']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if data is None: return None diff --git a/telegram/files/venue.py b/telegram/files/venue.py index ee94a211a..28e9fa107 100644 --- a/telegram/files/venue.py +++ b/telegram/files/venue.py @@ -85,7 +85,8 @@ class Venue(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Venue']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/files/video.py b/telegram/files/video.py index 48d59afc6..b21738cf4 100644 --- a/telegram/files/video.py +++ b/telegram/files/video.py @@ -97,7 +97,8 @@ class Video(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Video']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/files/videonote.py b/telegram/files/videonote.py index 497936ba5..6052b9a1d 100644 --- a/telegram/files/videonote.py +++ b/telegram/files/videonote.py @@ -86,7 +86,8 @@ class VideoNote(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VideoNote']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/games/game.py b/telegram/games/game.py index c3952f9f1..dbbb6e5c8 100644 --- a/telegram/games/game.py +++ b/telegram/games/game.py @@ -90,7 +90,8 @@ class Game(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Game']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -102,6 +103,7 @@ class Game(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['photo'] = [p.to_dict() for p in self.photo] diff --git a/telegram/games/gamehighscore.py b/telegram/games/gamehighscore.py index ba7506307..8eef2d6bd 100644 --- a/telegram/games/gamehighscore.py +++ b/telegram/games/gamehighscore.py @@ -54,7 +54,8 @@ class GameHighScore(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['GameHighScore']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/inline/inlinekeyboardmarkup.py b/telegram/inline/inlinekeyboardmarkup.py index 8116a314d..70c57ec9a 100644 --- a/telegram/inline/inlinekeyboardmarkup.py +++ b/telegram/inline/inlinekeyboardmarkup.py @@ -52,6 +52,7 @@ class InlineKeyboardMarkup(ReplyMarkup): self._id_attrs = (self.inline_keyboard,) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['inline_keyboard'] = [] @@ -62,7 +63,8 @@ class InlineKeyboardMarkup(ReplyMarkup): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboardMarkup']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/inline/inlinequery.py b/telegram/inline/inlinequery.py index a16e7a221..e4dd03c21 100644 --- a/telegram/inline/inlinequery.py +++ b/telegram/inline/inlinequery.py @@ -97,7 +97,8 @@ class InlineQuery(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineQuery']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/inline/inlinequeryresult.py b/telegram/inline/inlinequeryresult.py index 929b44b4c..d1e06866a 100644 --- a/telegram/inline/inlinequeryresult.py +++ b/telegram/inline/inlinequeryresult.py @@ -54,6 +54,7 @@ class InlineQueryResult(TelegramObject): self._id_attrs = (self.id,) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() # pylint: disable=E1101 diff --git a/telegram/inline/inputinvoicemessagecontent.py b/telegram/inline/inputinvoicemessagecontent.py index d27a31ffd..849451fbd 100644 --- a/telegram/inline/inputinvoicemessagecontent.py +++ b/telegram/inline/inputinvoicemessagecontent.py @@ -196,6 +196,7 @@ class InputInvoiceMessageContent(InputMessageContent): ) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['prices'] = [price.to_dict() for price in self.prices] @@ -206,7 +207,8 @@ class InputInvoiceMessageContent(InputMessageContent): def de_json( cls, data: Optional[JSONDict], bot: 'Bot' ) -> Optional['InputInvoiceMessageContent']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/inline/inputlocationmessagecontent.py b/telegram/inline/inputlocationmessagecontent.py index b964f75e5..397acd0a0 100644 --- a/telegram/inline/inputlocationmessagecontent.py +++ b/telegram/inline/inputlocationmessagecontent.py @@ -58,6 +58,7 @@ class InputLocationMessageContent(InputMessageContent): proximity alerts about approaching another chat member, in meters. """ + # fmt: on def __init__( diff --git a/telegram/inline/inputtextmessagecontent.py b/telegram/inline/inputtextmessagecontent.py index 4d4696131..b71f5b51f 100644 --- a/telegram/inline/inputtextmessagecontent.py +++ b/telegram/inline/inputtextmessagecontent.py @@ -77,6 +77,7 @@ class InputTextMessageContent(InputMessageContent): self._id_attrs = (self.message_text,) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() if self.entities: diff --git a/telegram/message.py b/telegram/message.py index c2bc860ef..7bb34486a 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -331,6 +331,7 @@ class Message(TelegramObject): bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. """ + # fmt: on _effective_attachment = _UNDEFINED @@ -506,7 +507,8 @@ class Message(TelegramObject): @property def link(self) -> Optional[str]: """:obj:`str`: Convenience property. If the chat of the message is not - a private chat or normal group, returns a t.me link of the message.""" + a private chat or normal group, returns a t.me link of the message. + """ if self.chat.type not in [Chat.PRIVATE, Chat.GROUP]: if self.chat.username: to_link = self.chat.username @@ -518,7 +520,8 @@ class Message(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Message']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -629,6 +632,7 @@ class Message(TelegramObject): return self.chat.id def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() # Required diff --git a/telegram/messageautodeletetimerchanged.py b/telegram/messageautodeletetimerchanged.py index 842bd162a..6a198ce30 100644 --- a/telegram/messageautodeletetimerchanged.py +++ b/telegram/messageautodeletetimerchanged.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 change in the Telegram message auto -deletion.""" +deletion. +""" from typing import Any diff --git a/telegram/messageentity.py b/telegram/messageentity.py index 04947cb95..59a9f2118 100644 --- a/telegram/messageentity.py +++ b/telegram/messageentity.py @@ -82,7 +82,8 @@ class MessageEntity(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MessageEntity']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/passport/credentials.py b/telegram/passport/credentials.py index bc5026c3a..861edb53f 100644 --- a/telegram/passport/credentials.py +++ b/telegram/passport/credentials.py @@ -49,9 +49,7 @@ if TYPE_CHECKING: class TelegramDecryptionError(TelegramError): - """ - Something went wrong with decryption. - """ + """Something went wrong with decryption.""" def __init__(self, message: Union[str, Exception]): super().__init__(f"TelegramDecryptionError: {message}") @@ -181,7 +179,7 @@ class EncryptedCredentials(TelegramObject): try: self._decrypted_secret = self.bot.private_key.decrypt( b64decode(self.secret), - OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None), + OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None), # skipcq ) except ValueError as exception: # If decryption fails raise exception @@ -223,7 +221,8 @@ class Credentials(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Credentials']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -294,7 +293,8 @@ class SecureData(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureData']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -367,7 +367,8 @@ class SecureValue(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureValue']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -382,6 +383,7 @@ class SecureValue(TelegramObject): return cls(bot=bot, **data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['files'] = [p.to_dict() for p in self.files] @@ -422,6 +424,7 @@ class DataCredentials(_CredentialsBase): super().__init__(data_hash, secret, **_kwargs) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() del data['file_hash'] @@ -448,6 +451,7 @@ class FileCredentials(_CredentialsBase): super().__init__(file_hash, secret, **_kwargs) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() del data['data_hash'] diff --git a/telegram/passport/encryptedpassportelement.py b/telegram/passport/encryptedpassportelement.py index 6068d4e3c..e25b14494 100644 --- a/telegram/passport/encryptedpassportelement.py +++ b/telegram/passport/encryptedpassportelement.py @@ -162,7 +162,8 @@ class EncryptedPassportElement(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['EncryptedPassportElement']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -179,6 +180,18 @@ class EncryptedPassportElement(TelegramObject): def de_json_decrypted( cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'Credentials' ) -> Optional['EncryptedPassportElement']: + """Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account + passport credentials. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`): The bot associated with this object. + credentials (:class:`telegram.FileCredentials`): The credentials + + Returns: + :class:`telegram.EncryptedPassportElement`: + + """ if not data: return None @@ -227,6 +240,7 @@ class EncryptedPassportElement(TelegramObject): return cls(bot=bot, **data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() if self.files: diff --git a/telegram/passport/passportdata.py b/telegram/passport/passportdata.py index dd6a4b612..81479d452 100644 --- a/telegram/passport/passportdata.py +++ b/telegram/passport/passportdata.py @@ -67,7 +67,8 @@ class PassportData(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PassportData']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -78,6 +79,7 @@ class PassportData(TelegramObject): return cls(bot=bot, **data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['data'] = [e.to_dict() for e in self.data] diff --git a/telegram/passport/passportfile.py b/telegram/passport/passportfile.py index 2506a80e9..dce3f176f 100644 --- a/telegram/passport/passportfile.py +++ b/telegram/passport/passportfile.py @@ -83,7 +83,19 @@ class PassportFile(TelegramObject): def de_json_decrypted( cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'FileCredentials' ) -> Optional['PassportFile']: - data = cls.parse_data(data) + """Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account + passport credentials. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`): The bot associated with this object. + credentials (:class:`telegram.FileCredentials`): The credentials + + Returns: + :class:`telegram.PassportFile`: + + """ + data = cls._parse_data(data) if not data: return None @@ -96,6 +108,18 @@ class PassportFile(TelegramObject): def de_list_decrypted( cls, data: Optional[List[JSONDict]], bot: 'Bot', credentials: List['FileCredentials'] ) -> List[Optional['PassportFile']]: + """Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account + passport credentials. + + Args: + data (Dict[:obj:`str`, ...]): The JSON data. + bot (:class:`telegram.Bot`): The bot associated with these objects. + credentials (:class:`telegram.FileCredentials`): The credentials + + Returns: + List[:class:`telegram.PassportFile`]: + + """ if not data: return [] diff --git a/telegram/payment/orderinfo.py b/telegram/payment/orderinfo.py index 142d90098..11d044833 100644 --- a/telegram/payment/orderinfo.py +++ b/telegram/payment/orderinfo.py @@ -66,7 +66,8 @@ class OrderInfo(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['OrderInfo']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return cls() diff --git a/telegram/payment/precheckoutquery.py b/telegram/payment/precheckoutquery.py index 264a822dc..b4d66338a 100644 --- a/telegram/payment/precheckoutquery.py +++ b/telegram/payment/precheckoutquery.py @@ -93,7 +93,8 @@ class PreCheckoutQuery(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PreCheckoutQuery']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/payment/shippingoption.py b/telegram/payment/shippingoption.py index 1fa9855b8..1be8a8871 100644 --- a/telegram/payment/shippingoption.py +++ b/telegram/payment/shippingoption.py @@ -60,6 +60,7 @@ class ShippingOption(TelegramObject): self._id_attrs = (self.id,) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['prices'] = [p.to_dict() for p in self.prices] diff --git a/telegram/payment/shippingquery.py b/telegram/payment/shippingquery.py index f9b372302..198a73ecd 100644 --- a/telegram/payment/shippingquery.py +++ b/telegram/payment/shippingquery.py @@ -74,7 +74,8 @@ class ShippingQuery(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ShippingQuery']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/payment/successfulpayment.py b/telegram/payment/successfulpayment.py index 881900cde..2d6541873 100644 --- a/telegram/payment/successfulpayment.py +++ b/telegram/payment/successfulpayment.py @@ -85,7 +85,8 @@ class SuccessfulPayment(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SuccessfulPayment']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/poll.py b/telegram/poll.py index 0ff6d474d..9a59bf303 100644 --- a/telegram/poll.py +++ b/telegram/poll.py @@ -87,7 +87,8 @@ class PollAnswer(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PollAnswer']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -180,7 +181,8 @@ class Poll(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Poll']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -192,6 +194,7 @@ class Poll(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['options'] = [x.to_dict() for x in self.options] diff --git a/telegram/proximityalerttriggered.py b/telegram/proximityalerttriggered.py index 2ec7c4ea9..fae6169d8 100644 --- a/telegram/proximityalerttriggered.py +++ b/telegram/proximityalerttriggered.py @@ -55,7 +55,8 @@ class ProximityAlertTriggered(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ProximityAlertTriggered']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/replykeyboardmarkup.py b/telegram/replykeyboardmarkup.py index 428c63d35..c41c29724 100644 --- a/telegram/replykeyboardmarkup.py +++ b/telegram/replykeyboardmarkup.py @@ -92,6 +92,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): self._id_attrs = (self.keyboard,) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['keyboard'] = [] diff --git a/telegram/update.py b/telegram/update.py index 8ff7b40cb..58e9d9338 100644 --- a/telegram/update.py +++ b/telegram/update.py @@ -342,7 +342,8 @@ class Update(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Update']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None diff --git a/telegram/user.py b/telegram/user.py index 32b61a8d1..487d8d487 100644 --- a/telegram/user.py +++ b/telegram/user.py @@ -128,7 +128,8 @@ class User(TelegramObject): @property def name(self) -> str: """:obj:`str`: Convenience property. If available, returns the user's :attr:`username` - prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`.""" + prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`. + """ if self.username: return f'@{self.username}' return self.full_name @@ -136,8 +137,8 @@ class User(TelegramObject): @property def full_name(self) -> str: """:obj:`str`: Convenience property. The user's :attr:`first_name`, followed by (if - available) :attr:`last_name`.""" - + available) :attr:`last_name`. + """ if self.last_name: return f'{self.first_name} {self.last_name}' return self.first_name @@ -145,8 +146,8 @@ class User(TelegramObject): @property def link(self) -> Optional[str]: """:obj:`str`: Convenience property. If :attr:`username` is available, returns a t.me link - of the user.""" - + of the user. + """ if self.username: return f"https://t.me/{self.username}" return None @@ -167,7 +168,6 @@ class User(TelegramObject): :meth:`telegram.Bot.get_user_profile_photos`. """ - return self.bot.get_user_profile_photos( user_id=self.id, offset=offset, diff --git a/telegram/userprofilephotos.py b/telegram/userprofilephotos.py index 1f99d44c8..ccf6e5bf1 100644 --- a/telegram/userprofilephotos.py +++ b/telegram/userprofilephotos.py @@ -53,7 +53,8 @@ class UserProfilePhotos(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['UserProfilePhotos']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -63,6 +64,7 @@ class UserProfilePhotos(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data['photos'] = [] diff --git a/telegram/utils/deprecate.py b/telegram/utils/deprecate.py index cf1d55de8..23fd1ca10 100644 --- a/telegram/utils/deprecate.py +++ b/telegram/utils/deprecate.py @@ -18,32 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module facilitates the deprecation of functions.""" -import warnings -from typing import Callable, TypeVar - -RT = TypeVar('RT') - # We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it # seem like it's the user that issued the warning # We name it something else so that you don't get confused when you attempt to suppress it class TelegramDeprecationWarning(Warning): - pass - - -def warn_deprecate_obj(old: str, new: str, stacklevel: int = 3) -> None: - warnings.warn( - f'{old} is being deprecated, please use {new} from now on.', - category=TelegramDeprecationWarning, - stacklevel=stacklevel, - ) - - -def deprecate(func: Callable[..., RT], old: str, new: str) -> Callable[..., RT]: - """Warn users invoking old to switch to the new function.""" - - def wrapped(*args: object, **kwargs: object) -> RT: - warn_deprecate_obj(old, new) - return func(*args, **kwargs) - - return wrapped + """Custom warning class for deprecations in this library.""" diff --git a/telegram/utils/helpers.py b/telegram/utils/helpers.py index 19ee462f1..4e326172f 100644 --- a/telegram/utils/helpers.py +++ b/telegram/utils/helpers.py @@ -186,9 +186,7 @@ def _datetime_to_float_timestamp(dt_obj: dtm.datetime) -> float: def _localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime: - """ - Localize the datetime, where UTC is handled depending on whether pytz is available or not - """ + """Localize the datetime, where UTC is handled depending on whether pytz is available or not""" if tzinfo is DTM_UTC: return datetime.replace(tzinfo=DTM_UTC) return tzinfo.localize(datetime) # type: ignore[attr-defined] @@ -250,7 +248,6 @@ def to_float_timestamp( ValueError: If ``t`` is a :obj:`datetime.datetime` and :obj:`reference_timestamp` is not :obj:`None`. """ - if reference_timestamp is None: reference_timestamp = time.time() elif isinstance(time_object, dtm.datetime): @@ -369,7 +366,6 @@ def effective_message_type(entity: Union['Message', 'Update']) -> Optional[str]: :obj:`str`: One of ``Message.MESSAGE_TYPES`` """ - # Importing on file-level yields cyclic Import Errors from telegram import Message, Update # pylint: disable=C0415 @@ -482,7 +478,6 @@ def decode_user_chat_data_from_json(data: str) -> DefaultDict[int, Dict[object, Returns: :obj:`dict`: The user/chat_data defaultdict after decoding """ - tmp: DefaultDict[int, Dict[object, object]] = defaultdict(dict) decoded_data = json.loads(data) for user, user_data in decoded_data.items(): diff --git a/telegram/utils/promise.py b/telegram/utils/promise.py index fd0e33053..c25d56d46 100644 --- a/telegram/utils/promise.py +++ b/telegram/utils/promise.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 :class:`telegram.ext.utils.promise.Promise` class for backwards -compatibility.""" +compatibility. +""" import warnings import telegram.ext.utils.promise as promise diff --git a/telegram/utils/request.py b/telegram/utils/request.py index b71e57de8..beb0c7b85 100644 --- a/telegram/utils/request.py +++ b/telegram/utils/request.py @@ -73,7 +73,7 @@ from telegram.utils.types import JSONDict def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: disable=W0613 - """ + r""" Monkey patch urllib3.urllib3.fields.RequestField to make it *not* support RFC2231 compliant Content-Disposition headers since telegram servers don't understand it. Instead just escape \\ and " and replace any \n and \r with a space. @@ -195,6 +195,7 @@ class Request: return self._con_pool_size def stop(self) -> None: + """Performs cleanup on shutdown.""" self._con_pool.clear() # type: ignore @staticmethod @@ -205,7 +206,6 @@ class Request: dict: A JSON parsed as Python dict with results - on error this dict will be empty. """ - decoded_s = json_data.decode('utf-8', 'replace') try: data = json.loads(decoded_s) diff --git a/telegram/utils/webhookhandler.py b/telegram/utils/webhookhandler.py index 25ccc053d..727eecbc7 100644 --- a/telegram/utils/webhookhandler.py +++ b/telegram/utils/webhookhandler.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 :class:`telegram.ext.utils.webhookhandler.WebhookHandler` class for -backwards compatibility.""" +backwards compatibility. +""" import warnings import telegram.ext.utils.webhookhandler as webhook_handler diff --git a/telegram/voicechat.py b/telegram/voicechat.py index 203e2a4d1..9ea91e2a2 100644 --- a/telegram/voicechat.py +++ b/telegram/voicechat.py @@ -38,7 +38,7 @@ class VoiceChatStarted(TelegramObject): .. versionadded:: 13.4 """ - def __init__(self, **_kwargs: Any): + def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049 pass @@ -100,7 +100,8 @@ class VoiceChatParticipantsInvited(TelegramObject): def de_json( cls, data: Optional[JSONDict], bot: 'Bot' ) -> Optional['VoiceChatParticipantsInvited']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -109,6 +110,7 @@ class VoiceChatParticipantsInvited(TelegramObject): return cls(**data) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() data["users"] = [u.to_dict() for u in self.users] @@ -139,7 +141,8 @@ class VoiceChatScheduled(TelegramObject): @classmethod def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VoiceChatScheduled']: - data = cls.parse_data(data) + """See :meth:`telegram.TelegramObject.de_json`.""" + data = cls._parse_data(data) if not data: return None @@ -149,6 +152,7 @@ class VoiceChatScheduled(TelegramObject): return cls(**data, bot=bot) def to_dict(self) -> JSONDict: + """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() # Required diff --git a/tests/conftest.py b/tests/conftest.py index 621bc9af2..21674eac8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -97,10 +97,9 @@ def default_bot(request, bot_info): default_bot = DEFAULT_BOTS.get(defaults) if default_bot: return default_bot - else: - default_bot = make_bot(bot_info, **{'defaults': defaults}) - DEFAULT_BOTS[defaults] = default_bot - return default_bot + default_bot = make_bot(bot_info, **{'defaults': defaults}) + DEFAULT_BOTS[defaults] = default_bot + return default_bot @pytest.fixture(scope='function') @@ -109,10 +108,9 @@ def tz_bot(timezone, bot_info): default_bot = DEFAULT_BOTS.get(defaults) if default_bot: return default_bot - else: - default_bot = make_bot(bot_info, **{'defaults': defaults}) - DEFAULT_BOTS[defaults] = default_bot - return default_bot + default_bot = make_bot(bot_info, **{'defaults': defaults}) + DEFAULT_BOTS[defaults] = default_bot + return default_bot @pytest.fixture(scope='session') diff --git a/tests/plugin_github_group.py b/tests/plugin_github_group.py index e00c9ca42..cb0430987 100644 --- a/tests/plugin_github_group.py +++ b/tests/plugin_github_group.py @@ -36,7 +36,7 @@ def terminal_summary_wrapper(original, plugin_name): @pytest.mark.trylast def pytest_configure(config): for hookimpl in config.pluginmanager.hook.pytest_terminal_summary._nonwrappers: - if hookimpl.plugin_name in fold_plugins.keys(): + if hookimpl.plugin_name in fold_plugins: hookimpl.function = terminal_summary_wrapper(hookimpl.function, hookimpl.plugin_name) diff --git a/tests/test_animation.py b/tests/test_animation.py index e6d74522c..4df3f96e7 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -260,7 +260,6 @@ class TestAnimation: animation = Animation.de_json(json_dict, bot) assert animation.file_id == self.animation_file_id assert animation.file_unique_id == self.animation_file_unique_id - assert animation.thumb == animation.thumb assert animation.file_name == self.file_name assert animation.mime_type == self.mime_type assert animation.file_size == self.file_size diff --git a/tests/test_bot.py b/tests/test_bot.py index d12a50b6d..d1009c96a 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -81,7 +81,7 @@ def chat_permissions(): def inline_results_callback(page=None): if not page: return [InlineQueryResultArticle(i, str(i), None) for i in range(1, 254)] - elif page <= 5: + if page <= 5: return [ InlineQueryResultArticle(i, str(i), None) for i in range(page * 5 + 1, (page + 1) * 5 + 1) @@ -217,11 +217,13 @@ class TestBot: @flaky(3, 1) def test_forward_message(self, bot, chat_id, message): - message = bot.forward_message(chat_id, from_chat_id=chat_id, message_id=message.message_id) + forward_message = bot.forward_message( + chat_id, from_chat_id=chat_id, message_id=message.message_id + ) - assert message.text == message.text - assert message.forward_from.username == message.from_user.username - assert isinstance(message.forward_date, dtm.datetime) + assert forward_message.text == message.text + assert forward_message.forward_from.username == message.from_user.username + assert isinstance(forward_message.forward_date, dtm.datetime) @flaky(3, 1) def test_delete_message(self, bot, chat_id): @@ -787,7 +789,7 @@ class TestBot: def make_assertion(url, data, *args, **kwargs): results = data['results'] length_matches = len(results) == num_results - ids_match = all([int(res['id']) == id_offset + i for i, res in enumerate(results)]) + ids_match = all(int(res['id']) == id_offset + i for i, res in enumerate(results)) next_offset_matches = data['next_offset'] == str(expected_next_offset) return length_matches and ids_match and next_offset_matches @@ -800,7 +802,7 @@ class TestBot: def make_assertion(url, data, *args, **kwargs): results = data['results'] length_matches = len(results) == MAX_INLINE_QUERY_RESULTS - ids_match = all([int(res['id']) == 1 + i for i, res in enumerate(results)]) + ids_match = all(int(res['id']) == 1 + i for i, res in enumerate(results)) next_offset_matches = data['next_offset'] == '1' return length_matches and ids_match and next_offset_matches @@ -813,7 +815,7 @@ class TestBot: def make_assertion(url, data, *args, **kwargs): results = data['results'] length_matches = len(results) == 30 - ids_match = all([int(res['id']) == 1 + i for i, res in enumerate(results)]) + ids_match = all(int(res['id']) == 1 + i for i, res in enumerate(results)) next_offset_matches = data['next_offset'] == '' return length_matches and ids_match and next_offset_matches @@ -826,7 +828,7 @@ class TestBot: def make_assertion(url, data, *args, **kwargs): results = data['results'] length = len(results) == 5 - ids = all([int(res['id']) == 6 + i for i, res in enumerate(results)]) + ids = all(int(res['id']) == 6 + i for i, res in enumerate(results)) next_offset = data['next_offset'] == '2' return length and ids and next_offset @@ -1910,14 +1912,13 @@ class TestBot: reply_to_message_id=reply_to_message.message_id, ) return - else: - returned = default_bot.copy_message( - chat_id, - from_chat_id=chat_id, - message_id=media_message.message_id, - caption="Test", - reply_to_message_id=reply_to_message.message_id, - ) + returned = default_bot.copy_message( + chat_id, + from_chat_id=chat_id, + message_id=media_message.message_id, + caption="Test", + reply_to_message_id=reply_to_message.message_id, + ) # we send a temp message which replies to the returned message id in order to get a # message object temp_message = default_bot.send_message( diff --git a/tests/test_commandhandler.py b/tests/test_commandhandler.py index e6ac81911..1539a7320 100644 --- a/tests/test_commandhandler.py +++ b/tests/test_commandhandler.py @@ -102,13 +102,13 @@ class BaseTest: def callback_context_regex1(self, update, context): if context.matches: - types = all([type(res) == self.SRE_TYPE for res in context.matches]) + types = all(type(res) is self.SRE_TYPE for res in context.matches) num = len(context.matches) == 1 self.test_flag = types and num def callback_context_regex2(self, update, context): if context.matches: - types = all([type(res) == self.SRE_TYPE for res in context.matches]) + types = all(type(res) is self.SRE_TYPE for res in context.matches) num = len(context.matches) == 2 self.test_flag = types and num diff --git a/tests/test_constants.py b/tests/test_constants.py index a128e11b5..58d1cbc97 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -45,6 +45,5 @@ class TestConstants: with pytest.raises( BadRequest, match="Media_caption_too_long", - ): - with open('tests/data/telegram.png', 'rb') as f: - bot.send_photo(photo=f, caption=bad_caption, chat_id=chat_id) + ), open('tests/data/telegram.png', 'rb') as f: + bot.send_photo(photo=f, caption=bad_caption, chat_id=chat_id) diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 348aeffd5..2e5da4ca2 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -72,8 +72,7 @@ def raise_dphs(func): result = func(self, *args, **kwargs) if self.raise_dp_handler_stop: raise DispatcherHandlerStop(result) - else: - return result + return result return decorator @@ -168,8 +167,7 @@ class TestConversationHandler: def start(self, bot, update): if isinstance(update, Update): return self._set_state(update, self.THIRSTY) - else: - return self._set_state(bot, self.THIRSTY) + return self._set_state(bot, self.THIRSTY) @raise_dphs def end(self, bot, update): @@ -187,8 +185,7 @@ class TestConversationHandler: def brew(self, bot, update): if isinstance(update, Update): return self._set_state(update, self.BREWING) - else: - return self._set_state(bot, self.BREWING) + return self._set_state(bot, self.BREWING) @raise_dphs def drink(self, bot, update): diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index 375174b12..43b1a2dba 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -683,7 +683,7 @@ class TestDispatcher: assert self.received == 'Unauthorized.' def test_sensible_worker_thread_names(self, dp2): - thread_names = [thread.name for thread in getattr(dp2, '_Dispatcher__async_threads')] + thread_names = [thread.name for thread in dp2._Dispatcher__async_threads] print(thread_names) for thread_name in thread_names: assert thread_name.startswith(f"Bot:{dp2.bot.id}:worker:") diff --git a/tests/test_document.py b/tests/test_document.py index cedae0a85..eb2867053 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -273,9 +273,8 @@ class TestDocument: @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): - with open(os.devnull, 'rb') as f: - with pytest.raises(TelegramError): - bot.send_document(chat_id=chat_id, document=f) + with open(os.devnull, 'rb') as f, pytest.raises(TelegramError): + bot.send_document(chat_id=chat_id, document=f) @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): diff --git a/tests/test_filters.py b/tests/test_filters.py index 05709ffe4..b3e741e00 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -133,19 +133,19 @@ class TestFilters: assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.regex('deep') | Filters.regex(r'linked param'))(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.regex('not int') | Filters.regex(r'linked param'))(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.regex('not int') & Filters.regex(r'linked param'))(update) assert not result @@ -158,19 +158,19 @@ class TestFilters: assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.regex(r'linked param') & Filters.command)(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.regex(r'linked param') | Filters.command)(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) # Should not give a match since it's a or filter and it short circuits result = (Filters.command | Filters.regex(r'linked param'))(update) assert result is True @@ -178,91 +178,91 @@ class TestFilters: def test_regex_complex_merges(self, update): SRE_TYPE = type(re.match("", "")) update.message.text = 'test it out' - filter = Filters.regex('test') & ( + test_filter = Filters.regex('test') & ( (Filters.status_update | Filters.forwarded) | Filters.regex('out') ) - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 2 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.forward_date = datetime.datetime.utcnow() - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.text = 'test it' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.forward_date = None - result = filter(update) + result = test_filter(update) assert not result update.message.text = 'test it out' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.pinned_message = True - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.text = 'it out' - result = filter(update) + result = test_filter(update) assert not result update.message.text = 'test it out' update.message.forward_date = None update.message.pinned_message = None - filter = (Filters.regex('test') | Filters.command) & ( + test_filter = (Filters.regex('test') | Filters.command) & ( Filters.regex('it') | Filters.status_update ) - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 2 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.text = 'test' - result = filter(update) + result = test_filter(update) assert not result update.message.pinned_message = True - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 1 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.text = 'nothing' - result = filter(update) + result = test_filter(update) assert not result update.message.text = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, bool) update.message.text = '/start it' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 1 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) def test_regex_inverted(self, update): update.message.text = '/start deep-linked param' @@ -336,13 +336,13 @@ class TestFilters: assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.caption_regex('deep') | Filters.caption_regex(r'linked param'))(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.caption_regex('not int') | Filters.caption_regex(r'linked param'))( update ) @@ -350,7 +350,7 @@ class TestFilters: assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.caption_regex('not int') & Filters.caption_regex(r'linked param'))( update ) @@ -365,19 +365,19 @@ class TestFilters: assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.caption_regex(r'linked param') & Filters.command)(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) result = (Filters.caption_regex(r'linked param') | Filters.command)(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) # Should not give a match since it's a or filter and it short circuits result = (Filters.command | Filters.caption_regex(r'linked param'))(update) assert result is True @@ -385,130 +385,130 @@ class TestFilters: def test_caption_regex_complex_merges(self, update): SRE_TYPE = type(re.match("", "")) update.message.caption = 'test it out' - filter = Filters.caption_regex('test') & ( + test_filter = Filters.caption_regex('test') & ( (Filters.status_update | Filters.forwarded) | Filters.caption_regex('out') ) - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 2 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.forward_date = datetime.datetime.utcnow() - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.caption = 'test it' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.forward_date = None - result = filter(update) + result = test_filter(update) assert not result update.message.caption = 'test it out' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.pinned_message = True - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.caption = 'it out' - result = filter(update) + result = test_filter(update) assert not result update.message.caption = 'test it out' update.message.forward_date = None update.message.pinned_message = None - filter = (Filters.caption_regex('test') | Filters.command) & ( + test_filter = (Filters.caption_regex('test') | Filters.command) & ( Filters.caption_regex('it') | Filters.status_update ) - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 2 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.caption = 'test' - result = filter(update) + result = test_filter(update) assert not result update.message.pinned_message = True - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 1 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) update.message.caption = 'nothing' - result = filter(update) + result = test_filter(update) assert not result update.message.caption = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, bool) update.message.caption = '/start it' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, dict) matches = result['matches'] assert isinstance(matches, list) assert len(matches) == 1 - assert all([type(res) == SRE_TYPE for res in matches]) + assert all(type(res) is SRE_TYPE for res in matches) def test_caption_regex_inverted(self, update): update.message.caption = '/start deep-linked param' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] - filter = ~Filters.caption_regex(r'deep-linked param') - result = filter(update) + test_filter = ~Filters.caption_regex(r'deep-linked param') + result = test_filter(update) assert not result update.message.caption = 'not it' - result = filter(update) + result = test_filter(update) assert result assert isinstance(result, bool) - filter = ~Filters.caption_regex('linked') & Filters.command + test_filter = ~Filters.caption_regex('linked') & Filters.command update.message.caption = "it's linked" - result = filter(update) + result = test_filter(update) assert not result update.message.caption = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] - result = filter(update) + result = test_filter(update) assert result update.message.caption = '/linked' - result = filter(update) + result = test_filter(update) assert not result - filter = ~Filters.caption_regex('linked') | Filters.command + test_filter = ~Filters.caption_regex('linked') | Filters.command update.message.caption = "it's linked" update.message.entities = [] - result = filter(update) + result = test_filter(update) assert not result update.message.caption = '/start linked' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] - result = filter(update) + result = test_filter(update) assert result update.message.caption = '/start' - result = filter(update) + result = test_filter(update) assert result update.message.caption = 'nothig' update.message.entities = [] - result = filter(update) + result = test_filter(update) assert result def test_filters_reply(self, update): diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index ce3e21e45..f53335f3e 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -406,8 +406,8 @@ class TestSendMediaGroup: messages = bot.send_media_group(chat_id, media_group) assert isinstance(messages, list) assert len(messages) == 3 - assert all([isinstance(mes, Message) for mes in messages]) - assert all([mes.media_group_id == messages[0].media_group_id for mes in messages]) + assert all(isinstance(mes, Message) for mes in messages) + assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) assert all(mes.caption == f'photo {idx+1}' for idx, mes in enumerate(messages)) assert all( mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages @@ -421,8 +421,8 @@ class TestSendMediaGroup: ) assert isinstance(messages, list) assert len(messages) == 3 - assert all([isinstance(mes, Message) for mes in messages]) - assert all([mes.media_group_id == messages[0].media_group_id for mes in messages]) + assert all(isinstance(mes, Message) for mes in messages) + assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) assert all(mes.caption == f'photo {idx+1}' for idx, mes in enumerate(messages)) assert all( mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages @@ -491,8 +491,8 @@ class TestSendMediaGroup: assert isinstance(messages, list) assert len(messages) == 3 - assert all([isinstance(mes, Message) for mes in messages]) - assert all([mes.media_group_id == messages[0].media_group_id for mes in messages]) + assert all(isinstance(mes, Message) for mes in messages) + assert all(mes.media_group_id == messages[0].media_group_id for mes in messages) @flaky(3, 1) @pytest.mark.parametrize( diff --git a/tests/test_invoice.py b/tests/test_invoice.py index fb0c2d4c9..b60c2726b 100644 --- a/tests/test_invoice.py +++ b/tests/test_invoice.py @@ -185,10 +185,8 @@ class TestInvoice: def test_send_object_as_provider_data(self, monkeypatch, bot, chat_id, provider_token): def test(url, data, **kwargs): - return ( - data['provider_data'] == '{"test_data": 123456789}' # Depends if using - or data['provider_data'] == '{"test_data":123456789}' - ) # ujson or not + # depends on whether we're using ujson + return data['provider_data'] in ['{"test_data": 123456789}', '{"test_data":123456789}'] monkeypatch.setattr(bot.request, 'post', test) diff --git a/tests/test_message.py b/tests/test_message.py index b7b6be4bb..64b33475f 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -606,26 +606,26 @@ class TestMessage: def test_chat_id(self, message): assert message.chat_id == message.chat.id - @pytest.mark.parametrize('type', argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) - def test_link_with_username(self, message, type): + @pytest.mark.parametrize('_type', argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) + def test_link_with_username(self, message, _type): message.chat.username = 'username' - message.chat.type = type + message.chat.type = _type assert message.link == f'https://t.me/{message.chat.username}/{message.message_id}' @pytest.mark.parametrize( - 'type, id', argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)] + '_type, _id', argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)] ) - def test_link_with_id(self, message, type, id): + def test_link_with_id(self, message, _type, _id): message.chat.username = None - message.chat.id = id - message.chat.type = type + message.chat.id = _id + message.chat.type = _type # The leading - for group ids/ -100 for supergroup ids isn't supposed to be in the link assert message.link == f'https://t.me/c/{3}/{message.message_id}' - @pytest.mark.parametrize('id, username', argvalues=[(None, 'username'), (-3, None)]) - def test_link_private_chats(self, message, id, username): + @pytest.mark.parametrize('_id, username', argvalues=[(None, 'username'), (-3, None)]) + def test_link_private_chats(self, message, _id, username): message.chat.type = Chat.PRIVATE - message.chat.id = id + message.chat.id = _id message.chat.username = username assert message.link is None message.chat.type = Chat.GROUP diff --git a/tests/test_messagehandler.py b/tests/test_messagehandler.py index 3ee5da820..e7a9e4e87 100644 --- a/tests/test_messagehandler.py +++ b/tests/test_messagehandler.py @@ -121,13 +121,13 @@ class TestMessageHandler: def callback_context_regex1(self, update, context): if context.matches: - types = all([type(res) == self.SRE_TYPE for res in context.matches]) + types = all(type(res) is self.SRE_TYPE for res in context.matches) num = len(context.matches) == 1 self.test_flag = types and num def callback_context_regex2(self, update, context): if context.matches: - types = all([type(res) == self.SRE_TYPE for res in context.matches]) + types = all(type(res) is self.SRE_TYPE for res in context.matches) num = len(context.matches) == 2 self.test_flag = types and num diff --git a/tests/test_official.py b/tests/test_official.py index 996ac624d..33ca6b67c 100644 --- a/tests/test_official.py +++ b/tests/test_official.py @@ -121,9 +121,9 @@ def check_object(h4): name.startswith('InlineQueryResult') or name.startswith('InputMedia') ) and field == 'type': continue - elif name.startswith('PassportElementError') and field == 'source': - continue - elif field == 'remove_keyboard': + elif ( + name.startswith('PassportElementError') and field == 'source' + ) or field == 'remove_keyboard': continue param = sig.parameters.get(field) @@ -135,7 +135,7 @@ def check_object(h4): ignored = IGNORED_PARAMETERS.copy() if name == 'InputFile': return - elif name == 'InlineQueryResult': + if name == 'InlineQueryResult': ignored |= {'id', 'type'} elif name == 'User': ignored |= {'type'} # TODO: Deprecation diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 474225082..82bd22388 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -1195,9 +1195,6 @@ class TestPickelPersistence: h2 = MessageHandler(None, second, pass_user_data=True, pass_chat_data=True) dp.add_handler(h1) dp.process_update(update) - del dp - del u - del pickle_persistence pickle_persistence_2 = PicklePersistence( filename='pickletest', store_user_data=True, @@ -1218,10 +1215,7 @@ class TestPickelPersistence: dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['test'] = 'Working3!' - u.signal_handler(signal.SIGINT, None) - del dp - del u - del pickle_persistence + u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filename='pickletest', store_user_data=True, @@ -1240,10 +1234,7 @@ class TestPickelPersistence: dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['my_test3'] = 'Working3!' - u.signal_handler(signal.SIGINT, None) - del dp - del u - del pickle_persistence_only_bot + u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filename='pickletest', store_user_data=False, @@ -1262,10 +1253,7 @@ class TestPickelPersistence: u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - u.signal_handler(signal.SIGINT, None) - del dp - del u - del pickle_persistence_only_chat + u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filename='pickletest', store_user_data=False, @@ -1284,10 +1272,7 @@ class TestPickelPersistence: u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - u.signal_handler(signal.SIGINT, None) - del dp - del u - del pickle_persistence_only_user + u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filename='pickletest', store_user_data=True, @@ -1310,10 +1295,10 @@ class TestPickelPersistence: start = CommandHandler('start', start) - def next(update, context): + def next_callback(update, context): return NEXT2 - next = MessageHandler(None, next) + next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END @@ -1321,7 +1306,7 @@ class TestPickelPersistence: next2 = MessageHandler(None, next2) ch = ConversationHandler( - [start], {NEXT: [next], NEXT2: [next2]}, [], name='name2', persistent=True + [start], {NEXT: [next_handler], NEXT2: [next2]}, [], name='name2', persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 @@ -1345,10 +1330,10 @@ class TestPickelPersistence: start = CommandHandler('start', start) - def next(update, context): + def next_callback(update, context): return NEXT2 - next = MessageHandler(None, next) + next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END @@ -1356,7 +1341,7 @@ class TestPickelPersistence: next2 = MessageHandler(None, next2) nested_ch = ConversationHandler( - [next], + [next_handler], {NEXT2: [next2]}, [], name='name3', @@ -1612,12 +1597,9 @@ class TestDictPersistence: h2 = MessageHandler(None, second, pass_user_data=True, pass_chat_data=True) dp.add_handler(h1) dp.process_update(update) - del dp - del u user_data = dict_persistence.user_data_json chat_data = dict_persistence.chat_data_json bot_data = dict_persistence.bot_data_json - del dict_persistence dict_persistence_2 = DictPersistence( user_data_json=user_data, chat_data_json=chat_data, bot_data_json=bot_data ) @@ -1638,10 +1620,10 @@ class TestDictPersistence: start = CommandHandler('start', start) - def next(update, context): + def next_callback(update, context): return NEXT2 - next = MessageHandler(None, next) + next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END @@ -1649,7 +1631,7 @@ class TestDictPersistence: next2 = MessageHandler(None, next2) ch = ConversationHandler( - [start], {NEXT: [next], NEXT2: [next2]}, [], name='name2', persistent=True + [start], {NEXT: [next_handler], NEXT2: [next2]}, [], name='name2', persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 @@ -1672,10 +1654,10 @@ class TestDictPersistence: start = CommandHandler('start', start) - def next(update, context): + def next_callback(update, context): return NEXT2 - next = MessageHandler(None, next) + next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END @@ -1683,7 +1665,7 @@ class TestDictPersistence: next2 = MessageHandler(None, next2) nested_ch = ConversationHandler( - [next], + [next_handler], {NEXT2: [next2]}, [], name='name3', diff --git a/tests/test_update.py b/tests/test_update.py index 103263969..b3b7c06dd 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -102,10 +102,10 @@ class TestUpdate: # Make sure only one thing in the update (other than update_id) is not None i = 0 - for type in all_types: - if getattr(update, type) is not None: + for _type in all_types: + if getattr(update, _type) is not None: i += 1 - assert getattr(update, type) == paramdict[type] + assert getattr(update, _type) == paramdict[_type] assert i == 1 def test_update_de_json_empty(self, bot): @@ -118,9 +118,9 @@ class TestUpdate: assert isinstance(update_dict, dict) assert update_dict['update_id'] == update.update_id - for type in all_types: - if getattr(update, type) is not None: - assert update_dict[type] == getattr(update, type).to_dict() + for _type in all_types: + if getattr(update, _type) is not None: + assert update_dict[_type] == getattr(update, _type).to_dict() def test_effective_chat(self, update): # Test that it's sometimes None per docstring