diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst index 7aaf44360cf..c73dc34dd07 100644 --- a/.github/CONTRIBUTING.rst +++ b/.github/CONTRIBUTING.rst @@ -73,27 +73,7 @@ Here's how to make a one-off code change. - Provide static typing with signature annotations. The documentation of `MyPy`_ will be a good start, the cheat sheet is `here`_. We also have some custom type aliases in ``telegram.utils.helpers.typing``. - - Document your code. This project uses `sphinx`_ to generate static HTML docs. To build them, first make sure you have the required dependencies: - - .. code-block:: bash - - $ pip install -r docs/requirements-docs.txt - - then run the following from the PTB root directory: - - .. code-block:: bash - - $ make -C docs html - - or, if you don't have ``make`` available (e.g. on Windows): - - .. code-block:: bash - - $ sphinx-build docs/source docs/build/html - - Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser. - - - Add ``.. versionadded:: version``, ``.. versionchanged:: version`` or ``.. deprecated:: version`` to the associated documentation of your changes, depending on what kind of change you made. This only applies if the change you made is visible to an end user. The directives should be added to class/method descriptions if their general behaviour changed and to the description of all arguments & attributes that changed. + - Document your code. This step is pretty important to us, so it has its own `section`_. - For consistency, please conform to `Google Python Style Guide`_ and `Google Python Style Docstrings`_. @@ -105,6 +85,8 @@ Here's how to make a one-off code change. - Please ensure that the code you write is well-tested. + - In addition to that, we provide the `dev` marker for pytest. If you write one or multiple tests and want to run only those, you can decorate them via `@pytest.mark.dev` and then run it with minimal overhead with `pytest ./path/to/test_file.py -m dev`. + - Don’t break backward compatibility. - Add yourself to the AUTHORS.rst_ file in an alphabetical fashion. @@ -151,7 +133,7 @@ Here's how to make a one-off code change. 5. **Address review comments until all reviewers give LGTM ('looks good to me').** - - When your reviewer has reviewed the code, you'll get an email. You'll need to respond in two ways: + - When your reviewer has reviewed the code, you'll get a notification. You'll need to respond in two ways: - Make a new commit addressing the comments you agree with, and push it to the same branch. Ideally, the commit message would explain what the commit does (e.g. "Fix lint error"), but if there are lots of disparate review comments, it's fine to refer to the original commit message and add something like "(address review comments)". @@ -186,6 +168,49 @@ Here's how to make a one-off code change. 7. **Celebrate.** Congratulations, you have contributed to ``python-telegram-bot``! +Documenting +=========== + +The documentation of this project is separated in two sections: User facing and dev facing. + +User facing docs are hosted at `RTD`_. They are the main way the users of our library are supposed to get information about the objects. They don't care about the internals, they just want to know +what they have to pass to make it work, what it actually does. You can/should provide examples for non obvious cases (like the Filter module), and notes/warnings. + +Dev facing, on the other side, is for the devs/maintainers of this project. These +doc strings don't have a separate documentation site they generate, instead, they document the actual code. + +User facing documentation +------------------------- +We use `sphinx`_ to generate static HTML docs. To build them, first make sure you have the required dependencies: + +.. code-block:: bash + + $ pip install -r docs/requirements-docs.txt + +then run the following from the PTB root directory: + +.. code-block:: bash + + $ make -C docs html + +or, if you don't have ``make`` available (e.g. on Windows): + +.. code-block:: bash + + $ sphinx-build docs/source docs/build/html + +Once the process terminates, you can view the built documentation by opening ``docs/build/html/index.html`` with a browser. + +- Add ``.. versionadded:: version``, ``.. versionchanged:: version`` or ``.. deprecated:: version`` to the associated documentation of your changes, depending on what kind of change you made. This only applies if the change you made is visible to an end user. The directives should be added to class/method descriptions if their general behaviour changed and to the description of all arguments & attributes that changed. + +Dev facing documentation +------------------------ +We adhere to the `CSI`_ standard. This documentation is not fully implemented in the project, yet, but new code changes should comply with the `CSI` standard. +The idea behind this is to make it very easy for you/a random maintainer or even a totally foreign person to drop anywhere into the code and more or less immediately understand what a particular line does. This will make it easier +for new to make relevant changes if said lines don't do what they are supposed to. + + + Style commandments ------------------ @@ -252,4 +277,7 @@ break the API classes. For example: .. _`here`: https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html .. _`Black`: https://black.readthedocs.io/en/stable/index.html .. _`popular editors`: https://black.readthedocs.io/en/stable/editor_integration.html +.. _`RTD`: https://python-telegram-bot.readthedocs.io/ .. _`RTD build`: https://python-telegram-bot.readthedocs.io/en/doc-fixes +.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html +.. _`section`: #documenting diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index aa027df29f9..3d42f80bc10 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,6 +6,7 @@ Hey! You're PRing? Cool! Please have a look at the below checklist. It's here to - [ ] Added `.. versionadded:: version`, `.. versionchanged:: version` or `.. deprecated:: version` to the docstrings for user facing changes (for methods/class descriptions, arguments and attributes) - [ ] Created new or adapted existing unit tests +- [ ] Documented code changes according to the [CSI standard](https://standards.mousepawmedia.com/en/stable/csi.html) - [ ] Added myself alphabetically to `AUTHORS.rst` (optional) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f9dbe68851d..368600092dd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,9 +3,11 @@ on: pull_request: branches: - master + - v14 push: branches: - master + - v14 jobs: pytest: @@ -13,7 +15,7 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9] os: [ubuntu-latest, windows-latest, macos-latest] fail-fast: False steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66f5b9b118b..e3ad3ff4388 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,6 +20,8 @@ repos: files: ^(telegram|examples)/.*\.py$ args: - --rcfile=setup.cfg + # run pylint across multiple cpu cores to speed it up- + - --jobs=0 # See https://pylint.pycqa.org/en/latest/user_guide/run.html?#parallel-execution to know more additional_dependencies: - certifi - tornado>=6.1 @@ -56,4 +58,4 @@ repos: - id: pyupgrade files: ^(telegram|examples|tests)/.*\.py$ args: - - --py36-plus + - --py37-plus diff --git a/README.rst b/README.rst index 41ce1c86d94..db73aa3d9a5 100644 --- a/README.rst +++ b/README.rst @@ -93,7 +93,7 @@ Introduction This library provides a pure Python interface for the `Telegram Bot API `_. -It's compatible with Python versions 3.6.8+. PTB might also work on `PyPy `_, though there have been a lot of issues before. Hence, PyPy is not officially supported. +It's compatible with Python versions **3.7+**. PTB might also work on `PyPy `_, though there have been a lot of issues before. Hence, PyPy is not officially supported. In addition to the pure API implementation, this library features a number of high-level classes to make the development of bots easy and straightforward. These classes are contained in the diff --git a/README_RAW.rst b/README_RAW.rst index 7a8c8fd5e6d..60c20693186 100644 --- a/README_RAW.rst +++ b/README_RAW.rst @@ -91,7 +91,7 @@ Introduction This library provides a pure Python, lightweight interface for the `Telegram Bot API `_. -It's compatible with Python versions 3.6.8+. PTB-Raw might also work on `PyPy `_, though there have been a lot of issues before. Hence, PyPy is not officially supported. +It's compatible with Python versions **3.7+**. PTB-Raw might also work on `PyPy `_, though there have been a lot of issues before. Hence, PyPy is not officially supported. ``python-telegram-bot-raw`` is part of the `python-telegram-bot `_ ecosystem and provides the pure API functionality extracted from PTB. It therefore does *not* have independent release schedules, changelogs or documentation. Please consult the PTB resources. diff --git a/docs/source/telegram.ext.delayqueue.rst b/docs/source/telegram.ext.delayqueue.rst deleted file mode 100644 index cf64f2bc780..00000000000 --- a/docs/source/telegram.ext.delayqueue.rst +++ /dev/null @@ -1,9 +0,0 @@ -:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/messagequeue.py - -telegram.ext.DelayQueue -======================= - -.. autoclass:: telegram.ext.DelayQueue - :members: - :show-inheritance: - :special-members: diff --git a/docs/source/telegram.ext.messagequeue.rst b/docs/source/telegram.ext.messagequeue.rst deleted file mode 100644 index 0b824f1e9bf..00000000000 --- a/docs/source/telegram.ext.messagequeue.rst +++ /dev/null @@ -1,9 +0,0 @@ -:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/messagequeue.py - -telegram.ext.MessageQueue -========================= - -.. autoclass:: telegram.ext.MessageQueue - :members: - :show-inheritance: - :special-members: diff --git a/docs/source/telegram.ext.persistenceinput.rst b/docs/source/telegram.ext.persistenceinput.rst new file mode 100644 index 00000000000..ea5a0b38c83 --- /dev/null +++ b/docs/source/telegram.ext.persistenceinput.rst @@ -0,0 +1,7 @@ +:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/basepersistence.py + +telegram.ext.PersistenceInput +============================= + +.. autoclass:: telegram.ext.PersistenceInput + :show-inheritance: diff --git a/docs/source/telegram.ext.regexhandler.rst b/docs/source/telegram.ext.regexhandler.rst deleted file mode 100644 index efe40ef29c7..00000000000 --- a/docs/source/telegram.ext.regexhandler.rst +++ /dev/null @@ -1,8 +0,0 @@ -:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/regexhandler.py - -telegram.ext.RegexHandler -========================= - -.. autoclass:: telegram.ext.RegexHandler - :members: - :show-inheritance: diff --git a/docs/source/telegram.ext.rst b/docs/source/telegram.ext.rst index f4b7bceb067..dc995e0a9ad 100644 --- a/docs/source/telegram.ext.rst +++ b/docs/source/telegram.ext.rst @@ -10,8 +10,6 @@ telegram.ext package telegram.ext.callbackcontext telegram.ext.job telegram.ext.jobqueue - telegram.ext.messagequeue - telegram.ext.delayqueue telegram.ext.contexttypes telegram.ext.defaults @@ -33,7 +31,6 @@ Handlers telegram.ext.pollhandler telegram.ext.precheckoutqueryhandler telegram.ext.prefixhandler - telegram.ext.regexhandler telegram.ext.shippingqueryhandler telegram.ext.stringcommandhandler telegram.ext.stringregexhandler @@ -45,6 +42,7 @@ Persistence .. toctree:: telegram.ext.basepersistence + telegram.ext.persistenceinput telegram.ext.picklepersistence telegram.ext.dictpersistence diff --git a/examples/arbitrarycallbackdatabot.py b/examples/arbitrarycallbackdatabot.py index 6d1139ce984..6803caf3550 100644 --- a/examples/arbitrarycallbackdatabot.py +++ b/examples/arbitrarycallbackdatabot.py @@ -20,7 +20,7 @@ ) logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -28,7 +28,7 @@ def start(update: Update, context: CallbackContext) -> None: """Sends a message with 5 inline buttons attached.""" number_list: List[int] = [] - update.message.reply_text('Please choose:', reply_markup=build_keyboard(number_list)) + update.message.reply_text("Please choose:", reply_markup=build_keyboard(number_list)) def help_command(update: Update, context: CallbackContext) -> None: @@ -43,7 +43,7 @@ def clear(update: Update, context: CallbackContext) -> None: """Clears the callback data cache""" context.bot.callback_data_cache.clear_callback_data() # type: ignore[attr-defined] context.bot.callback_data_cache.clear_callback_queries() # type: ignore[attr-defined] - update.effective_message.reply_text('All clear!') + update.effective_message.reply_text("All clear!") def build_keyboard(current_list: List[int]) -> InlineKeyboardMarkup: @@ -77,22 +77,20 @@ def handle_invalid_button(update: Update, context: CallbackContext) -> None: """Informs the user that the button is no longer available.""" update.callback_query.answer() update.effective_message.edit_text( - 'Sorry, I could not process this button click πŸ˜• Please send /start to get a new keyboard.' + "Sorry, I could not process this button click πŸ˜• Please send /start to get a new keyboard." ) def main() -> None: """Run the bot.""" # We use persistence to demonstrate how buttons can still work after the bot was restarted - persistence = PicklePersistence( - filename='arbitrarycallbackdatabot.pickle', store_callback_data=True - ) + persistence = PicklePersistence(filename="arbitrarycallbackdatabot.pickle") # Create the Updater and pass it your bot's token. updater = Updater("TOKEN", persistence=persistence, arbitrary_callback_data=True) - updater.dispatcher.add_handler(CommandHandler('start', start)) - updater.dispatcher.add_handler(CommandHandler('help', help_command)) - updater.dispatcher.add_handler(CommandHandler('clear', clear)) + updater.dispatcher.add_handler(CommandHandler("start", start)) + updater.dispatcher.add_handler(CommandHandler("help", help_command)) + updater.dispatcher.add_handler(CommandHandler("clear", clear)) updater.dispatcher.add_handler( CallbackQueryHandler(handle_invalid_button, pattern=InvalidCallbackData) ) @@ -106,5 +104,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/contexttypesbot.py b/examples/contexttypesbot.py index cfe485a61f8..828e860d85f 100644 --- a/examples/contexttypesbot.py +++ b/examples/contexttypesbot.py @@ -43,7 +43,7 @@ def __init__(self, dispatcher: Dispatcher): @property def bot_user_ids(self) -> Set[int]: """Custom shortcut to access a value stored in the bot_data dict""" - return self.bot_data.setdefault('user_ids', set()) + return self.bot_data.setdefault("user_ids", set()) @property def message_clicks(self) -> Optional[int]: @@ -56,11 +56,11 @@ def message_clicks(self) -> Optional[int]: def message_clicks(self, value: int) -> None: """Allow to change the count""" if not self._message_id: - raise RuntimeError('There is no message associated with this context obejct.') + raise RuntimeError("There is no message associated with this context obejct.") self.chat_data.clicks_per_message[self._message_id] = value @classmethod - def from_update(cls, update: object, dispatcher: 'Dispatcher') -> 'CustomContext': + def from_update(cls, update: object, dispatcher: "Dispatcher") -> "CustomContext": """Override from_update to set _message_id.""" # Make sure to call super() context = super().from_update(update, dispatcher) @@ -75,9 +75,9 @@ def from_update(cls, update: object, dispatcher: 'Dispatcher') -> 'CustomContext def start(update: Update, context: CustomContext) -> None: """Display a message with a button.""" update.message.reply_html( - 'This button was clicked 0 times.', + "This button was clicked 0 times.", reply_markup=InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='Click me!', callback_data='button') + InlineKeyboardButton(text="Click me!", callback_data="button") ), ) @@ -87,9 +87,9 @@ def count_click(update: Update, context: CustomContext) -> None: context.message_clicks += 1 update.callback_query.answer() update.effective_message.edit_text( - f'This button was clicked {context.message_clicks} times.', + f"This button was clicked {context.message_clicks} times.", reply_markup=InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='Click me!', callback_data='button') + InlineKeyboardButton(text="Click me!", callback_data="button") ), parse_mode=ParseMode.HTML, ) @@ -98,7 +98,7 @@ def count_click(update: Update, context: CustomContext) -> None: def print_users(update: Update, context: CustomContext) -> None: """Show which users have been using this bot.""" update.message.reply_text( - 'The following user IDs have used this bot: ' + "The following user IDs have used this bot: " f'{", ".join(map(str, context.bot_user_ids))}' ) @@ -125,5 +125,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/conversationbot.py b/examples/conversationbot.py index 4e5f62efb5b..4ad8ddf6c00 100644 --- a/examples/conversationbot.py +++ b/examples/conversationbot.py @@ -28,7 +28,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -38,14 +38,14 @@ def start(update: Update, context: CallbackContext) -> int: """Starts the conversation and asks the user about their gender.""" - reply_keyboard = [['Boy', 'Girl', 'Other']] + reply_keyboard = [["Boy", "Girl", "Other"]] update.message.reply_text( - 'Hi! My name is Professor Bot. I will hold a conversation with you. ' - 'Send /cancel to stop talking to me.\n\n' - 'Are you a boy or a girl?', + "Hi! My name is Professor Bot. I will hold a conversation with you. " + "Send /cancel to stop talking to me.\n\n" + "Are you a boy or a girl?", reply_markup=ReplyKeyboardMarkup( - reply_keyboard, one_time_keyboard=True, input_field_placeholder='Boy or Girl?' + reply_keyboard, one_time_keyboard=True, input_field_placeholder="Boy or Girl?" ), ) @@ -57,8 +57,8 @@ def gender(update: Update, context: CallbackContext) -> int: user = update.message.from_user logger.info("Gender of %s: %s", user.first_name, update.message.text) update.message.reply_text( - 'I see! Please send me a photo of yourself, ' - 'so I know what you look like, or send /skip if you don\'t want to.', + "I see! Please send me a photo of yourself, " + "so I know what you look like, or send /skip if you don't want to.", reply_markup=ReplyKeyboardRemove(), ) @@ -69,10 +69,10 @@ def photo(update: Update, context: CallbackContext) -> int: """Stores the photo and asks for a location.""" user = update.message.from_user photo_file = update.message.photo[-1].get_file() - photo_file.download('user_photo.jpg') - logger.info("Photo of %s: %s", user.first_name, 'user_photo.jpg') + photo_file.download("user_photo.jpg") + logger.info("Photo of %s: %s", user.first_name, "user_photo.jpg") update.message.reply_text( - 'Gorgeous! Now, send me your location please, or send /skip if you don\'t want to.' + "Gorgeous! Now, send me your location please, or send /skip if you don't want to." ) return LOCATION @@ -83,7 +83,7 @@ def skip_photo(update: Update, context: CallbackContext) -> int: user = update.message.from_user logger.info("User %s did not send a photo.", user.first_name) update.message.reply_text( - 'I bet you look great! Now, send me your location please, or send /skip.' + "I bet you look great! Now, send me your location please, or send /skip." ) return LOCATION @@ -97,7 +97,7 @@ def location(update: Update, context: CallbackContext) -> int: "Location of %s: %f / %f", user.first_name, user_location.latitude, user_location.longitude ) update.message.reply_text( - 'Maybe I can visit you sometime! At last, tell me something about yourself.' + "Maybe I can visit you sometime! At last, tell me something about yourself." ) return BIO @@ -108,7 +108,7 @@ def skip_location(update: Update, context: CallbackContext) -> int: user = update.message.from_user logger.info("User %s did not send a location.", user.first_name) update.message.reply_text( - 'You seem a bit paranoid! At last, tell me something about yourself.' + "You seem a bit paranoid! At last, tell me something about yourself." ) return BIO @@ -118,7 +118,7 @@ def bio(update: Update, context: CallbackContext) -> int: """Stores the info about the user and ends the conversation.""" user = update.message.from_user logger.info("Bio of %s: %s", user.first_name, update.message.text) - update.message.reply_text('Thank you! I hope we can talk again some day.') + update.message.reply_text("Thank you! I hope we can talk again some day.") return ConversationHandler.END @@ -128,7 +128,7 @@ def cancel(update: Update, context: CallbackContext) -> int: user = update.message.from_user logger.info("User %s canceled the conversation.", user.first_name) update.message.reply_text( - 'Bye! I hope we can talk again some day.', reply_markup=ReplyKeyboardRemove() + "Bye! I hope we can talk again some day.", reply_markup=ReplyKeyboardRemove() ) return ConversationHandler.END @@ -144,17 +144,17 @@ def main() -> None: # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO conv_handler = ConversationHandler( - entry_points=[CommandHandler('start', start)], + entry_points=[CommandHandler("start", start)], states={ - GENDER: [MessageHandler(Filters.regex('^(Boy|Girl|Other)$'), gender)], - PHOTO: [MessageHandler(Filters.photo, photo), CommandHandler('skip', skip_photo)], + GENDER: [MessageHandler(Filters.regex("^(Boy|Girl|Other)$"), gender)], + PHOTO: [MessageHandler(Filters.photo, photo), CommandHandler("skip", skip_photo)], LOCATION: [ MessageHandler(Filters.location, location), - CommandHandler('skip', skip_location), + CommandHandler("skip", skip_location), ], BIO: [MessageHandler(Filters.text & ~Filters.command, bio)], }, - fallbacks=[CommandHandler('cancel', cancel)], + fallbacks=[CommandHandler("cancel", cancel)], ) dispatcher.add_handler(conv_handler) @@ -168,5 +168,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/conversationbot2.py b/examples/conversationbot2.py index aef62fe485c..d0973ef583b 100644 --- a/examples/conversationbot2.py +++ b/examples/conversationbot2.py @@ -29,7 +29,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -37,17 +37,17 @@ CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3) reply_keyboard = [ - ['Age', 'Favourite colour'], - ['Number of siblings', 'Something else...'], - ['Done'], + ["Age", "Favourite colour"], + ["Number of siblings", "Something else..."], + ["Done"], ] markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) def facts_to_str(user_data: Dict[str, str]) -> str: """Helper function for formatting the gathered user info.""" - facts = [f'{key} - {value}' for key, value in user_data.items()] - return "\n".join(facts).join(['\n', '\n']) + facts = [f"{key} - {value}" for key, value in user_data.items()] + return "\n".join(facts).join(["\n", "\n"]) def start(update: Update, context: CallbackContext) -> int: @@ -64,8 +64,8 @@ def start(update: Update, context: CallbackContext) -> int: def regular_choice(update: Update, context: CallbackContext) -> int: """Ask the user for info about the selected predefined choice.""" text = update.message.text - context.user_data['choice'] = text - update.message.reply_text(f'Your {text.lower()}? Yes, I would love to hear about that!') + context.user_data["choice"] = text + update.message.reply_text(f"Your {text.lower()}? Yes, I would love to hear about that!") return TYPING_REPLY @@ -83,9 +83,9 @@ def received_information(update: Update, context: CallbackContext) -> int: """Store info provided by user and ask for the next category.""" user_data = context.user_data text = update.message.text - category = user_data['choice'] + category = user_data["choice"] user_data[category] = text - del user_data['choice'] + del user_data["choice"] update.message.reply_text( "Neat! Just so you know, this is what you already told me:" @@ -100,8 +100,8 @@ def received_information(update: Update, context: CallbackContext) -> int: def done(update: Update, context: CallbackContext) -> int: """Display the gathered info and end the conversation.""" user_data = context.user_data - if 'choice' in user_data: - del user_data['choice'] + if "choice" in user_data: + del user_data["choice"] update.message.reply_text( f"I learned these facts about you: {facts_to_str(user_data)}Until next time!", @@ -122,27 +122,27 @@ def main() -> None: # Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY conv_handler = ConversationHandler( - entry_points=[CommandHandler('start', start)], + entry_points=[CommandHandler("start", start)], states={ CHOOSING: [ MessageHandler( - Filters.regex('^(Age|Favourite colour|Number of siblings)$'), regular_choice + Filters.regex("^(Age|Favourite colour|Number of siblings)$"), regular_choice ), - MessageHandler(Filters.regex('^Something else...$'), custom_choice), + MessageHandler(Filters.regex("^Something else...$"), custom_choice), ], TYPING_CHOICE: [ MessageHandler( - Filters.text & ~(Filters.command | Filters.regex('^Done$')), regular_choice + Filters.text & ~(Filters.command | Filters.regex("^Done$")), regular_choice ) ], TYPING_REPLY: [ MessageHandler( - Filters.text & ~(Filters.command | Filters.regex('^Done$')), + Filters.text & ~(Filters.command | Filters.regex("^Done$")), received_information, ) ], }, - fallbacks=[MessageHandler(Filters.regex('^Done$'), done)], + fallbacks=[MessageHandler(Filters.regex("^Done$"), done)], ) dispatcher.add_handler(conv_handler) @@ -156,5 +156,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/echobot.py b/examples/echobot.py index e6954b7a1d6..4147ee1d048 100644 --- a/examples/echobot.py +++ b/examples/echobot.py @@ -22,7 +22,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -34,14 +34,14 @@ def start(update: Update, context: CallbackContext) -> None: """Send a message when the command /start is issued.""" user = update.effective_user update.message.reply_markdown_v2( - fr'Hi {user.mention_markdown_v2()}\!', + fr"Hi {user.mention_markdown_v2()}\!", reply_markup=ForceReply(selective=True), ) def help_command(update: Update, context: CallbackContext) -> None: """Send a message when the command /help is issued.""" - update.message.reply_text('Help!') + update.message.reply_text("Help!") def echo(update: Update, context: CallbackContext) -> None: @@ -73,5 +73,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/errorhandlerbot.py b/examples/errorhandlerbot.py index 08504a6cd87..8c601b5b332 100644 --- a/examples/errorhandlerbot.py +++ b/examples/errorhandlerbot.py @@ -12,7 +12,7 @@ from telegram.ext import Updater, CallbackContext, CommandHandler logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -33,18 +33,18 @@ def error_handler(update: object, context: CallbackContext) -> None: # traceback.format_exception returns the usual python message about an exception, but as a # list of strings rather than a single string, so we have to join them together. tb_list = traceback.format_exception(None, context.error, context.error.__traceback__) - tb_string = ''.join(tb_list) + tb_string = "".join(tb_list) # Build the message with some markup and additional information about what happened. # You might need to add some logic to deal with messages longer than the 4096 character limit. update_str = update.to_dict() if isinstance(update, Update) else str(update) message = ( - f'An exception was raised while handling an update\n' - f'
update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}'
-        '
\n\n' - f'
context.chat_data = {html.escape(str(context.chat_data))}
\n\n' - f'
context.user_data = {html.escape(str(context.user_data))}
\n\n' - f'
{html.escape(tb_string)}
' + f"An exception was raised while handling an update\n" + f"
update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}"
+        "
\n\n" + f"
context.chat_data = {html.escape(str(context.chat_data))}
\n\n" + f"
context.user_data = {html.escape(str(context.user_data))}
\n\n" + f"
{html.escape(tb_string)}
" ) # Finally, send the message @@ -59,8 +59,8 @@ def bad_command(update: Update, context: CallbackContext) -> None: def start(update: Update, context: CallbackContext) -> None: """Displays info on how to trigger an error.""" update.effective_message.reply_html( - 'Use /bad_command to cause an error.\n' - f'Your chat id is {update.effective_chat.id}.' + "Use /bad_command to cause an error.\n" + f"Your chat id is {update.effective_chat.id}." ) @@ -73,8 +73,8 @@ def main() -> None: dispatcher = updater.dispatcher # Register the commands... - dispatcher.add_handler(CommandHandler('start', start)) - dispatcher.add_handler(CommandHandler('bad_command', bad_command)) + dispatcher.add_handler(CommandHandler("start", start)) + dispatcher.add_handler(CommandHandler("bad_command", bad_command)) # ...and the error handler dispatcher.add_error_handler(error_handler) @@ -88,5 +88,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/inlinebot.py b/examples/inlinebot.py index 85a3de553c7..edae8e42a17 100644 --- a/examples/inlinebot.py +++ b/examples/inlinebot.py @@ -21,7 +21,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -31,12 +31,12 @@ # context. Error handlers also receive the raised TelegramError object in error. def start(update: Update, context: CallbackContext) -> None: """Send a message when the command /start is issued.""" - update.message.reply_text('Hi!') + update.message.reply_text("Hi!") def help_command(update: Update, context: CallbackContext) -> None: """Send a message when the command /help is issued.""" - update.message.reply_text('Help!') + update.message.reply_text("Help!") def inlinequery(update: Update, context: CallbackContext) -> None: @@ -95,5 +95,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/inlinekeyboard.py b/examples/inlinekeyboard.py index a3799d207ec..602fe68ec63 100644 --- a/examples/inlinekeyboard.py +++ b/examples/inlinekeyboard.py @@ -12,7 +12,7 @@ from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -21,15 +21,15 @@ def start(update: Update, context: CallbackContext) -> None: """Sends a message with three inline buttons attached.""" keyboard = [ [ - InlineKeyboardButton("Option 1", callback_data='1'), - InlineKeyboardButton("Option 2", callback_data='2'), + InlineKeyboardButton("Option 1", callback_data="1"), + InlineKeyboardButton("Option 2", callback_data="2"), ], - [InlineKeyboardButton("Option 3", callback_data='3')], + [InlineKeyboardButton("Option 3", callback_data="3")], ] reply_markup = InlineKeyboardMarkup(keyboard) - update.message.reply_text('Please choose:', reply_markup=reply_markup) + update.message.reply_text("Please choose:", reply_markup=reply_markup) def button(update: Update, context: CallbackContext) -> None: @@ -53,9 +53,9 @@ def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") - updater.dispatcher.add_handler(CommandHandler('start', start)) + updater.dispatcher.add_handler(CommandHandler("start", start)) updater.dispatcher.add_handler(CallbackQueryHandler(button)) - updater.dispatcher.add_handler(CommandHandler('help', help_command)) + updater.dispatcher.add_handler(CommandHandler("help", help_command)) # Start the Bot updater.start_polling() @@ -65,5 +65,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/inlinekeyboard2.py b/examples/inlinekeyboard2.py index 2276238e413..32b31f84470 100644 --- a/examples/inlinekeyboard2.py +++ b/examples/inlinekeyboard2.py @@ -26,7 +26,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -174,20 +174,20 @@ def main() -> None: # $ means "end of line/string" # So ^ABC$ will only allow 'ABC' conv_handler = ConversationHandler( - entry_points=[CommandHandler('start', start)], + entry_points=[CommandHandler("start", start)], states={ FIRST: [ - CallbackQueryHandler(one, pattern='^' + str(ONE) + '$'), - CallbackQueryHandler(two, pattern='^' + str(TWO) + '$'), - CallbackQueryHandler(three, pattern='^' + str(THREE) + '$'), - CallbackQueryHandler(four, pattern='^' + str(FOUR) + '$'), + CallbackQueryHandler(one, pattern="^" + str(ONE) + "$"), + CallbackQueryHandler(two, pattern="^" + str(TWO) + "$"), + CallbackQueryHandler(three, pattern="^" + str(THREE) + "$"), + CallbackQueryHandler(four, pattern="^" + str(FOUR) + "$"), ], SECOND: [ - CallbackQueryHandler(start_over, pattern='^' + str(ONE) + '$'), - CallbackQueryHandler(end, pattern='^' + str(TWO) + '$'), + CallbackQueryHandler(start_over, pattern="^" + str(ONE) + "$"), + CallbackQueryHandler(end, pattern="^" + str(TWO) + "$"), ], }, - fallbacks=[CommandHandler('start', start)], + fallbacks=[CommandHandler("start", start)], ) # Add ConversationHandler to dispatcher that will be used for handling updates @@ -202,5 +202,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/nestedconversationbot.py b/examples/nestedconversationbot.py index e00e2fc3da6..1b2e14b119e 100644 --- a/examples/nestedconversationbot.py +++ b/examples/nestedconversationbot.py @@ -30,7 +30,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -66,8 +66,8 @@ # Helper def _name_switcher(level: str) -> Tuple[str, str]: if level == PARENTS: - return 'Father', 'Mother' - return 'Brother', 'Sister' + return "Father", "Mother" + return "Brother", "Sister" # Top level conversation callbacks @@ -80,12 +80,12 @@ def start(update: Update, context: CallbackContext) -> str: buttons = [ [ - InlineKeyboardButton(text='Add family member', callback_data=str(ADDING_MEMBER)), - InlineKeyboardButton(text='Add yourself', callback_data=str(ADDING_SELF)), + InlineKeyboardButton(text="Add family member", callback_data=str(ADDING_MEMBER)), + InlineKeyboardButton(text="Add yourself", callback_data=str(ADDING_SELF)), ], [ - InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)), - InlineKeyboardButton(text='Done', callback_data=str(END)), + InlineKeyboardButton(text="Show data", callback_data=str(SHOWING)), + InlineKeyboardButton(text="Done", callback_data=str(END)), ], ] keyboard = InlineKeyboardMarkup(buttons) @@ -107,8 +107,8 @@ def start(update: Update, context: CallbackContext) -> str: def adding_self(update: Update, context: CallbackContext) -> str: """Add information about yourself.""" context.user_data[CURRENT_LEVEL] = SELF - text = 'Okay, please tell me about yourself.' - button = InlineKeyboardButton(text='Add info', callback_data=str(MALE)) + text = "Okay, please tell me about yourself." + button = InlineKeyboardButton(text="Add info", callback_data=str(MALE)) keyboard = InlineKeyboardMarkup.from_button(button) update.callback_query.answer() @@ -123,9 +123,9 @@ def show_data(update: Update, context: CallbackContext) -> str: def prettyprint(user_data: Dict[str, Any], level: str) -> str: people = user_data.get(level) if not people: - return '\nNo information yet.' + return "\nNo information yet." - text = '' + text = "" if level == SELF: for person in user_data[level]: text += f"\nName: {person.get(NAME, '-')}, Age: {person.get(AGE, '-')}" @@ -142,7 +142,7 @@ def prettyprint(user_data: Dict[str, Any], level: str) -> str: text += f"\n\nParents:{prettyprint(user_data, PARENTS)}" text += f"\n\nChildren:{prettyprint(user_data, CHILDREN)}" - buttons = [[InlineKeyboardButton(text='Back', callback_data=str(END))]] + buttons = [[InlineKeyboardButton(text="Back", callback_data=str(END))]] keyboard = InlineKeyboardMarkup(buttons) update.callback_query.answer() @@ -154,7 +154,7 @@ def prettyprint(user_data: Dict[str, Any], level: str) -> str: def stop(update: Update, context: CallbackContext) -> int: """End Conversation by command.""" - update.message.reply_text('Okay, bye.') + update.message.reply_text("Okay, bye.") return END @@ -163,7 +163,7 @@ def end(update: Update, context: CallbackContext) -> int: """End conversation from InlineKeyboardButton.""" update.callback_query.answer() - text = 'See you around!' + text = "See you around!" update.callback_query.edit_message_text(text=text) return END @@ -172,15 +172,15 @@ def end(update: Update, context: CallbackContext) -> int: # Second level conversation callbacks def select_level(update: Update, context: CallbackContext) -> str: """Choose to add a parent or a child.""" - text = 'You may add a parent or a child. Also you can show the gathered data or go back.' + text = "You may add a parent or a child. Also you can show the gathered data or go back." buttons = [ [ - InlineKeyboardButton(text='Add parent', callback_data=str(PARENTS)), - InlineKeyboardButton(text='Add child', callback_data=str(CHILDREN)), + InlineKeyboardButton(text="Add parent", callback_data=str(PARENTS)), + InlineKeyboardButton(text="Add child", callback_data=str(CHILDREN)), ], [ - InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)), - InlineKeyboardButton(text='Back', callback_data=str(END)), + InlineKeyboardButton(text="Show data", callback_data=str(SHOWING)), + InlineKeyboardButton(text="Back", callback_data=str(END)), ], ] keyboard = InlineKeyboardMarkup(buttons) @@ -196,18 +196,18 @@ def select_gender(update: Update, context: CallbackContext) -> str: level = update.callback_query.data context.user_data[CURRENT_LEVEL] = level - text = 'Please choose, whom to add.' + text = "Please choose, whom to add." male, female = _name_switcher(level) buttons = [ [ - InlineKeyboardButton(text=f'Add {male}', callback_data=str(MALE)), - InlineKeyboardButton(text=f'Add {female}', callback_data=str(FEMALE)), + InlineKeyboardButton(text=f"Add {male}", callback_data=str(MALE)), + InlineKeyboardButton(text=f"Add {female}", callback_data=str(FEMALE)), ], [ - InlineKeyboardButton(text='Show data', callback_data=str(SHOWING)), - InlineKeyboardButton(text='Back', callback_data=str(END)), + InlineKeyboardButton(text="Show data", callback_data=str(SHOWING)), + InlineKeyboardButton(text="Back", callback_data=str(END)), ], ] keyboard = InlineKeyboardMarkup(buttons) @@ -231,9 +231,9 @@ def select_feature(update: Update, context: CallbackContext) -> str: """Select a feature to update for the person.""" buttons = [ [ - InlineKeyboardButton(text='Name', callback_data=str(NAME)), - InlineKeyboardButton(text='Age', callback_data=str(AGE)), - InlineKeyboardButton(text='Done', callback_data=str(END)), + InlineKeyboardButton(text="Name", callback_data=str(NAME)), + InlineKeyboardButton(text="Age", callback_data=str(AGE)), + InlineKeyboardButton(text="Done", callback_data=str(END)), ] ] keyboard = InlineKeyboardMarkup(buttons) @@ -241,13 +241,13 @@ def select_feature(update: Update, context: CallbackContext) -> str: # If we collect features for a new person, clear the cache and save the gender if not context.user_data.get(START_OVER): context.user_data[FEATURES] = {GENDER: update.callback_query.data} - text = 'Please select a feature to update.' + text = "Please select a feature to update." update.callback_query.answer() update.callback_query.edit_message_text(text=text, reply_markup=keyboard) # But after we do that, we need to send a new message else: - text = 'Got it! Please select a feature to update.' + text = "Got it! Please select a feature to update." update.message.reply_text(text=text, reply_markup=keyboard) context.user_data[START_OVER] = False @@ -257,7 +257,7 @@ def select_feature(update: Update, context: CallbackContext) -> str: def ask_for_input(update: Update, context: CallbackContext) -> str: """Prompt user to input data for selected feature.""" context.user_data[CURRENT_FEATURE] = update.callback_query.data - text = 'Okay, tell me.' + text = "Okay, tell me." update.callback_query.answer() update.callback_query.edit_message_text(text=text) @@ -295,7 +295,7 @@ def end_describing(update: Update, context: CallbackContext) -> int: def stop_nested(update: Update, context: CallbackContext) -> str: """Completely end conversation from within nested conversation.""" - update.message.reply_text('Okay, bye.') + update.message.reply_text("Okay, bye.") return STOPPING @@ -312,18 +312,18 @@ def main() -> None: description_conv = ConversationHandler( entry_points=[ CallbackQueryHandler( - select_feature, pattern='^' + str(MALE) + '$|^' + str(FEMALE) + '$' + select_feature, pattern="^" + str(MALE) + "$|^" + str(FEMALE) + "$" ) ], states={ SELECTING_FEATURE: [ - CallbackQueryHandler(ask_for_input, pattern='^(?!' + str(END) + ').*$') + CallbackQueryHandler(ask_for_input, pattern="^(?!" + str(END) + ").*$") ], TYPING: [MessageHandler(Filters.text & ~Filters.command, save_input)], }, fallbacks=[ - CallbackQueryHandler(end_describing, pattern='^' + str(END) + '$'), - CommandHandler('stop', stop_nested), + CallbackQueryHandler(end_describing, pattern="^" + str(END) + "$"), + CommandHandler("stop", stop_nested), ], map_to_parent={ # Return to second level menu @@ -335,17 +335,17 @@ def main() -> None: # Set up second level ConversationHandler (adding a person) add_member_conv = ConversationHandler( - entry_points=[CallbackQueryHandler(select_level, pattern='^' + str(ADDING_MEMBER) + '$')], + entry_points=[CallbackQueryHandler(select_level, pattern="^" + str(ADDING_MEMBER) + "$")], states={ SELECTING_LEVEL: [ - CallbackQueryHandler(select_gender, pattern=f'^{PARENTS}$|^{CHILDREN}$') + CallbackQueryHandler(select_gender, pattern=f"^{PARENTS}$|^{CHILDREN}$") ], SELECTING_GENDER: [description_conv], }, fallbacks=[ - CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'), - CallbackQueryHandler(end_second_level, pattern='^' + str(END) + '$'), - CommandHandler('stop', stop_nested), + CallbackQueryHandler(show_data, pattern="^" + str(SHOWING) + "$"), + CallbackQueryHandler(end_second_level, pattern="^" + str(END) + "$"), + CommandHandler("stop", stop_nested), ], map_to_parent={ # After showing data return to top level menu @@ -362,20 +362,20 @@ def main() -> None: # conversation, we need to make sure the top level conversation can also handle them selection_handlers = [ add_member_conv, - CallbackQueryHandler(show_data, pattern='^' + str(SHOWING) + '$'), - CallbackQueryHandler(adding_self, pattern='^' + str(ADDING_SELF) + '$'), - CallbackQueryHandler(end, pattern='^' + str(END) + '$'), + CallbackQueryHandler(show_data, pattern="^" + str(SHOWING) + "$"), + CallbackQueryHandler(adding_self, pattern="^" + str(ADDING_SELF) + "$"), + CallbackQueryHandler(end, pattern="^" + str(END) + "$"), ] conv_handler = ConversationHandler( - entry_points=[CommandHandler('start', start)], + entry_points=[CommandHandler("start", start)], states={ - SHOWING: [CallbackQueryHandler(start, pattern='^' + str(END) + '$')], + SHOWING: [CallbackQueryHandler(start, pattern="^" + str(END) + "$")], SELECTING_ACTION: selection_handlers, SELECTING_LEVEL: selection_handlers, DESCRIBING_SELF: [description_conv], - STOPPING: [CommandHandler('start', start)], + STOPPING: [CommandHandler("start", start)], }, - fallbacks=[CommandHandler('stop', stop)], + fallbacks=[CommandHandler("stop", stop)], ) dispatcher.add_handler(conv_handler) @@ -389,5 +389,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/passportbot.py b/examples/passportbot.py index dc563e90ba1..d1a483de540 100644 --- a/examples/passportbot.py +++ b/examples/passportbot.py @@ -17,7 +17,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.DEBUG + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.DEBUG ) logger = logging.getLogger(__name__) @@ -29,7 +29,7 @@ def msg(update: Update, context: CallbackContext) -> None: passport_data = update.message.passport_data # If our nonce doesn't match what we think, this Update did not originate from us # Ideally you would randomize the nonce on the server - if passport_data.decrypted_credentials.nonce != 'thisisatest': + if passport_data.decrypted_credentials.nonce != "thisisatest": return # Print the decrypted credential data @@ -37,61 +37,61 @@ def msg(update: Update, context: CallbackContext) -> None: # Print their decrypted data # Files will be downloaded to current directory for data in passport_data.decrypted_data: # This is where the data gets decrypted - if data.type == 'phone_number': - print('Phone: ', data.phone_number) - elif data.type == 'email': - print('Email: ', data.email) + if data.type == "phone_number": + print("Phone: ", data.phone_number) + elif data.type == "email": + print("Email: ", data.email) if data.type in ( - 'personal_details', - 'passport', - 'driver_license', - 'identity_card', - 'internal_passport', - 'address', + "personal_details", + "passport", + "driver_license", + "identity_card", + "internal_passport", + "address", ): print(data.type, data.data) if data.type in ( - 'utility_bill', - 'bank_statement', - 'rental_agreement', - 'passport_registration', - 'temporary_registration', + "utility_bill", + "bank_statement", + "rental_agreement", + "passport_registration", + "temporary_registration", ): - print(data.type, len(data.files), 'files') + print(data.type, len(data.files), "files") for file in data.files: actual_file = file.get_file() print(actual_file) actual_file.download() if ( - data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport') + 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: + 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') + 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', - 'identity_card', - 'internal_passport', - 'utility_bill', - 'bank_statement', - 'rental_agreement', - 'passport_registration', - 'temporary_registration', + "passport", + "driver_license", + "identity_card", + "internal_passport", + "utility_bill", + "bank_statement", + "rental_agreement", + "passport_registration", + "temporary_registration", ): - print(data.type, len(data.translation), 'translation') + print(data.type, len(data.translation), "translation") for file in data.translation: actual_file = file.get_file() print(actual_file) @@ -101,7 +101,7 @@ def msg(update: Update, context: CallbackContext) -> None: def main() -> None: """Start the bot.""" # Create the Updater and pass it your token and private key - with open('private.key', 'rb') as private_key: + with open("private.key", "rb") as private_key: updater = Updater("TOKEN", private_key=private_key.read()) # Get the dispatcher to register handlers @@ -119,5 +119,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/paymentbot.py b/examples/paymentbot.py index a619a795083..44ccc970378 100644 --- a/examples/paymentbot.py +++ b/examples/paymentbot.py @@ -19,7 +19,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -95,16 +95,16 @@ def shipping_callback(update: Update, context: CallbackContext) -> None: """Answers the ShippingQuery with ShippingOptions""" query = update.shipping_query # check the payload, is this from your bot? - if query.invoice_payload != 'Custom-Payload': + if query.invoice_payload != "Custom-Payload": # answer False pre_checkout_query query.answer(ok=False, error_message="Something went wrong...") return # First option has a single LabeledPrice - options = [ShippingOption('1', 'Shipping Option A', [LabeledPrice('A', 100)])] + options = [ShippingOption("1", "Shipping Option A", [LabeledPrice("A", 100)])] # second option has an array of LabeledPrice objects - price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)] - options.append(ShippingOption('2', 'Shipping Option B', price_list)) + price_list = [LabeledPrice("B1", 150), LabeledPrice("B2", 200)] + options.append(ShippingOption("2", "Shipping Option B", price_list)) query.answer(ok=True, shipping_options=options) @@ -113,7 +113,7 @@ def precheckout_callback(update: Update, context: CallbackContext) -> None: """Answers the PreQecheckoutQuery""" query = update.pre_checkout_query # check the payload, is this from your bot? - if query.invoice_payload != 'Custom-Payload': + if query.invoice_payload != "Custom-Payload": # answer False pre_checkout_query query.answer(ok=False, error_message="Something went wrong...") else: @@ -160,5 +160,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/persistentconversationbot.py b/examples/persistentconversationbot.py index 4a156acfb4a..38aa8e798af 100644 --- a/examples/persistentconversationbot.py +++ b/examples/persistentconversationbot.py @@ -30,7 +30,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -38,17 +38,17 @@ CHOOSING, TYPING_REPLY, TYPING_CHOICE = range(3) reply_keyboard = [ - ['Age', 'Favourite colour'], - ['Number of siblings', 'Something else...'], - ['Done'], + ["Age", "Favourite colour"], + ["Number of siblings", "Something else..."], + ["Done"], ] markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) def facts_to_str(user_data: Dict[str, str]) -> str: """Helper function for formatting the gathered user info.""" - facts = [f'{key} - {value}' for key, value in user_data.items()] - return "\n".join(facts).join(['\n', '\n']) + facts = [f"{key} - {value}" for key, value in user_data.items()] + return "\n".join(facts).join(["\n", "\n"]) def start(update: Update, context: CallbackContext) -> int: @@ -72,13 +72,13 @@ def start(update: Update, context: CallbackContext) -> int: def regular_choice(update: Update, context: CallbackContext) -> int: """Ask the user for info about the selected predefined choice.""" text = update.message.text.lower() - context.user_data['choice'] = text + context.user_data["choice"] = text if context.user_data.get(text): reply_text = ( - f'Your {text}? I already know the following about that: {context.user_data[text]}' + f"Your {text}? I already know the following about that: {context.user_data[text]}" ) else: - reply_text = f'Your {text}? Yes, I would love to hear about that!' + reply_text = f"Your {text}? Yes, I would love to hear about that!" update.message.reply_text(reply_text) return TYPING_REPLY @@ -96,9 +96,9 @@ def custom_choice(update: Update, context: CallbackContext) -> int: def received_information(update: Update, context: CallbackContext) -> int: """Store info provided by user and ask for the next category.""" text = update.message.text - category = context.user_data['choice'] + category = context.user_data["choice"] context.user_data[category] = text.lower() - del context.user_data['choice'] + del context.user_data["choice"] update.message.reply_text( "Neat! Just so you know, this is what you already told me:" @@ -119,8 +119,8 @@ def show_data(update: Update, context: CallbackContext) -> None: def done(update: Update, context: CallbackContext) -> int: """Display the gathered info and end the conversation.""" - if 'choice' in context.user_data: - del context.user_data['choice'] + if "choice" in context.user_data: + del context.user_data["choice"] update.message.reply_text( f"I learned these facts about you: {facts_to_str(context.user_data)}Until next time!", @@ -132,7 +132,7 @@ def done(update: Update, context: CallbackContext) -> int: def main() -> None: """Run the bot.""" # Create the Updater and pass it your bot's token. - persistence = PicklePersistence(filename='conversationbot') + persistence = PicklePersistence(filename="conversationbot") updater = Updater("TOKEN", persistence=persistence) # Get the dispatcher to register handlers @@ -140,34 +140,34 @@ def main() -> None: # Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY conv_handler = ConversationHandler( - entry_points=[CommandHandler('start', start)], + entry_points=[CommandHandler("start", start)], states={ CHOOSING: [ MessageHandler( - Filters.regex('^(Age|Favourite colour|Number of siblings)$'), regular_choice + Filters.regex("^(Age|Favourite colour|Number of siblings)$"), regular_choice ), - MessageHandler(Filters.regex('^Something else...$'), custom_choice), + MessageHandler(Filters.regex("^Something else...$"), custom_choice), ], TYPING_CHOICE: [ MessageHandler( - Filters.text & ~(Filters.command | Filters.regex('^Done$')), regular_choice + Filters.text & ~(Filters.command | Filters.regex("^Done$")), regular_choice ) ], TYPING_REPLY: [ MessageHandler( - Filters.text & ~(Filters.command | Filters.regex('^Done$')), + Filters.text & ~(Filters.command | Filters.regex("^Done$")), received_information, ) ], }, - fallbacks=[MessageHandler(Filters.regex('^Done$'), done)], + fallbacks=[MessageHandler(Filters.regex("^Done$"), done)], name="my_conversation", persistent=True, ) dispatcher.add_handler(conv_handler) - show_data_handler = CommandHandler('show_data', show_data) + show_data_handler = CommandHandler("show_data", show_data) dispatcher.add_handler(show_data_handler) # Start the Bot @@ -179,5 +179,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/pollbot.py b/examples/pollbot.py index f7521c56e77..f71c71d0a3e 100644 --- a/examples/pollbot.py +++ b/examples/pollbot.py @@ -29,7 +29,7 @@ ) logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -37,8 +37,8 @@ def start(update: Update, context: CallbackContext) -> None: """Inform user about what this bot can do""" update.message.reply_text( - 'Please select /poll to get a Poll, /quiz to get a Quiz or /preview' - ' to generate a preview for your poll' + "Please select /poll to get a Poll, /quiz to get a Quiz or /preview" + " to generate a preview for your poll" ) @@ -155,14 +155,14 @@ def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") dispatcher = updater.dispatcher - dispatcher.add_handler(CommandHandler('start', start)) - dispatcher.add_handler(CommandHandler('poll', poll)) + dispatcher.add_handler(CommandHandler("start", start)) + dispatcher.add_handler(CommandHandler("poll", poll)) dispatcher.add_handler(PollAnswerHandler(receive_poll_answer)) - dispatcher.add_handler(CommandHandler('quiz', quiz)) + dispatcher.add_handler(CommandHandler("quiz", quiz)) dispatcher.add_handler(PollHandler(receive_quiz_answer)) - dispatcher.add_handler(CommandHandler('preview', preview)) + dispatcher.add_handler(CommandHandler("preview", preview)) dispatcher.add_handler(MessageHandler(Filters.poll, receive_poll)) - dispatcher.add_handler(CommandHandler('help', help_handler)) + dispatcher.add_handler(CommandHandler("help", help_handler)) # Start the Bot updater.start_polling() @@ -172,5 +172,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/rawapibot.py b/examples/rawapibot.py index fed61b3d6de..38fcee3204a 100644 --- a/examples/rawapibot.py +++ b/examples/rawapibot.py @@ -21,7 +21,7 @@ def main() -> NoReturn: """Run the bot.""" global UPDATE_ID # Telegram Bot Authorization Token - bot = telegram.Bot('TOKEN') + bot = telegram.Bot("TOKEN") # get the first pending update_id, this is so we can skip over it in case # we get an "Unauthorized" exception. @@ -30,7 +30,7 @@ def main() -> NoReturn: except IndexError: UPDATE_ID = None - logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') + logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s") while True: try: @@ -56,5 +56,5 @@ def echo(bot: telegram.Bot) -> None: update.message.reply_text(update.message.text) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/examples/timerbot.py b/examples/timerbot.py index 9643f30abec..977645e19eb 100644 --- a/examples/timerbot.py +++ b/examples/timerbot.py @@ -25,7 +25,7 @@ # Enable logging logging.basicConfig( - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) @@ -39,13 +39,13 @@ # we decided to have it present as context. def start(update: Update, context: CallbackContext) -> None: """Sends explanation on how to use the bot.""" - update.message.reply_text('Hi! Use /set to set a timer') + update.message.reply_text("Hi! Use /set to set a timer") def alarm(context: CallbackContext) -> None: """Send the alarm message.""" job = context.job - context.bot.send_message(job.context, text='Beep!') + context.bot.send_message(job.context, text="Beep!") def remove_job_if_exists(name: str, context: CallbackContext) -> bool: @@ -65,26 +65,26 @@ def set_timer(update: Update, context: CallbackContext) -> None: # args[0] should contain the time for the timer in seconds due = int(context.args[0]) if due < 0: - update.message.reply_text('Sorry we can not go back to future!') + update.message.reply_text("Sorry we can not go back to future!") return job_removed = remove_job_if_exists(str(chat_id), context) context.job_queue.run_once(alarm, due, context=chat_id, name=str(chat_id)) - text = 'Timer successfully set!' + text = "Timer successfully set!" if job_removed: - text += ' Old one was removed.' + text += " Old one was removed." update.message.reply_text(text) except (IndexError, ValueError): - update.message.reply_text('Usage: /set ') + update.message.reply_text("Usage: /set ") def unset(update: Update, context: CallbackContext) -> None: """Remove the job if the user changed their mind.""" chat_id = update.message.chat_id job_removed = remove_job_if_exists(str(chat_id), context) - text = 'Timer successfully cancelled!' if job_removed else 'You have no active timer.' + text = "Timer successfully cancelled!" if job_removed else "You have no active timer." update.message.reply_text(text) @@ -111,5 +111,5 @@ def main() -> None: updater.idle() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml index 956c606237c..7dfd0aaad73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,9 @@ [tool.black] line-length = 99 -target-version = ['py36'] -skip-string-normalization = true - +target-version = ['py37'] # We need to force-exclude the negated include pattern # so that pre-commit run --all-files does the correct thing # see https://github.com/psf/black/issues/1778 force-exclude = '^(?!/(telegram|examples|tests)/).*\.py$' include = '(telegram|examples|tests)/.*\.py$' -exclude = 'telegram/vendor' \ No newline at end of file +exclude = 'telegram/vendor' diff --git a/setup.cfg b/setup.cfg index f013075113f..98748321afb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ filterwarnings = ; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here ; and instead do a trick directly in tests/conftest.py ; ignore::telegram.utils.deprecate.TelegramDeprecationWarning +markers = dev: If you want to test a specific test, use this [coverage:run] branch = True diff --git a/setup.py b/setup.py index acffecc18ea..63a786a32e1 100644 --- a/setup.py +++ b/setup.py @@ -98,12 +98,11 @@ def get_setup_kwargs(raw=False): 'Topic :: Internet', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', ], - python_requires='>=3.6' + python_requires='>=3.7' ) return kwargs diff --git a/telegram/__init__.py b/telegram/__init__.py index 59179e8ae3e..68c19c36a65 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -56,7 +56,7 @@ from .replykeyboardmarkup import ReplyKeyboardMarkup from .replykeyboardremove import ReplyKeyboardRemove from .forcereply import ForceReply -from .error import TelegramError +from .error import TelegramError, PassportDecryptionError from .files.inputfile import InputFile from .files.file import File from .parsemode import ParseMode @@ -159,7 +159,6 @@ SecureData, SecureValue, FileCredentials, - TelegramDecryptionError, ) from .botcommandscope import ( BotCommandScope, @@ -174,153 +173,153 @@ from .bot import Bot from .version import __version__, bot_api_version # noqa: F401 -__author__ = 'devs@python-telegram-bot.org' +__author__ = "devs@python-telegram-bot.org" __all__ = ( # Keep this alphabetically ordered - 'Animation', - 'Audio', - 'Bot', - 'BotCommand', - 'BotCommandScope', - 'BotCommandScopeAllChatAdministrators', - 'BotCommandScopeAllGroupChats', - 'BotCommandScopeAllPrivateChats', - 'BotCommandScopeChat', - 'BotCommandScopeChatAdministrators', - 'BotCommandScopeChatMember', - 'BotCommandScopeDefault', - 'CallbackGame', - 'CallbackQuery', - 'Chat', - 'ChatAction', - 'ChatInviteLink', - 'ChatLocation', - 'ChatMember', - 'ChatMemberOwner', - 'ChatMemberAdministrator', - 'ChatMemberMember', - 'ChatMemberRestricted', - 'ChatMemberLeft', - 'ChatMemberBanned', - 'ChatMemberUpdated', - 'ChatPermissions', - 'ChatPhoto', - 'ChosenInlineResult', - 'Contact', - 'Credentials', - 'DataCredentials', - 'Dice', - 'Document', - 'EncryptedCredentials', - 'EncryptedPassportElement', - 'File', - 'FileCredentials', - 'ForceReply', - 'Game', - 'GameHighScore', - 'IdDocumentData', - 'InlineKeyboardButton', - 'InlineKeyboardMarkup', - 'InlineQuery', - 'InlineQueryResult', - 'InlineQueryResultArticle', - 'InlineQueryResultAudio', - 'InlineQueryResultCachedAudio', - 'InlineQueryResultCachedDocument', - 'InlineQueryResultCachedGif', - 'InlineQueryResultCachedMpeg4Gif', - 'InlineQueryResultCachedPhoto', - 'InlineQueryResultCachedSticker', - 'InlineQueryResultCachedVideo', - 'InlineQueryResultCachedVoice', - 'InlineQueryResultContact', - 'InlineQueryResultDocument', - 'InlineQueryResultGame', - 'InlineQueryResultGif', - 'InlineQueryResultLocation', - 'InlineQueryResultMpeg4Gif', - 'InlineQueryResultPhoto', - 'InlineQueryResultVenue', - 'InlineQueryResultVideo', - 'InlineQueryResultVoice', - 'InputContactMessageContent', - 'InputFile', - 'InputInvoiceMessageContent', - 'InputLocationMessageContent', - 'InputMedia', - 'InputMediaAnimation', - 'InputMediaAudio', - 'InputMediaDocument', - 'InputMediaPhoto', - 'InputMediaVideo', - 'InputMessageContent', - 'InputTextMessageContent', - 'InputVenueMessageContent', - 'Invoice', - 'KeyboardButton', - 'KeyboardButtonPollType', - 'LabeledPrice', - 'Location', - 'LoginUrl', - 'MAX_CAPTION_LENGTH', - 'MAX_FILESIZE_DOWNLOAD', - 'MAX_FILESIZE_UPLOAD', - 'MAX_MESSAGES_PER_MINUTE_PER_GROUP', - 'MAX_MESSAGES_PER_SECOND', - 'MAX_MESSAGES_PER_SECOND_PER_CHAT', - 'MAX_MESSAGE_LENGTH', - 'MaskPosition', - 'Message', - 'MessageAutoDeleteTimerChanged', - 'MessageEntity', - 'MessageId', - 'OrderInfo', - 'ParseMode', - 'PassportData', - 'PassportElementError', - 'PassportElementErrorDataField', - 'PassportElementErrorFile', - 'PassportElementErrorFiles', - 'PassportElementErrorFrontSide', - 'PassportElementErrorReverseSide', - 'PassportElementErrorSelfie', - 'PassportElementErrorTranslationFile', - 'PassportElementErrorTranslationFiles', - 'PassportElementErrorUnspecified', - 'PassportFile', - 'PersonalDetails', - 'PhotoSize', - 'Poll', - 'PollAnswer', - 'PollOption', - 'PreCheckoutQuery', - 'ProximityAlertTriggered', - 'ReplyKeyboardMarkup', - 'ReplyKeyboardRemove', - 'ReplyMarkup', - 'ResidentialAddress', - 'SUPPORTED_WEBHOOK_PORTS', - 'SecureData', - 'SecureValue', - 'ShippingAddress', - 'ShippingOption', - 'ShippingQuery', - 'Sticker', - 'StickerSet', - 'SuccessfulPayment', - 'TelegramDecryptionError', - 'TelegramError', - 'TelegramObject', - 'Update', - 'User', - 'UserProfilePhotos', - 'Venue', - 'Video', - 'VideoNote', - 'Voice', - 'VoiceChatStarted', - 'VoiceChatEnded', - 'VoiceChatScheduled', - 'VoiceChatParticipantsInvited', - 'WebhookInfo', + "Animation", + "Audio", + "Bot", + "BotCommand", + "BotCommandScope", + "BotCommandScopeAllChatAdministrators", + "BotCommandScopeAllGroupChats", + "BotCommandScopeAllPrivateChats", + "BotCommandScopeChat", + "BotCommandScopeChatAdministrators", + "BotCommandScopeChatMember", + "BotCommandScopeDefault", + "CallbackGame", + "CallbackQuery", + "Chat", + "ChatAction", + "ChatInviteLink", + "ChatLocation", + "ChatMember", + "ChatMemberOwner", + "ChatMemberAdministrator", + "ChatMemberMember", + "ChatMemberRestricted", + "ChatMemberLeft", + "ChatMemberBanned", + "ChatMemberUpdated", + "ChatPermissions", + "ChatPhoto", + "ChosenInlineResult", + "Contact", + "Credentials", + "DataCredentials", + "Dice", + "Document", + "EncryptedCredentials", + "EncryptedPassportElement", + "File", + "FileCredentials", + "ForceReply", + "Game", + "GameHighScore", + "IdDocumentData", + "InlineKeyboardButton", + "InlineKeyboardMarkup", + "InlineQuery", + "InlineQueryResult", + "InlineQueryResultArticle", + "InlineQueryResultAudio", + "InlineQueryResultCachedAudio", + "InlineQueryResultCachedDocument", + "InlineQueryResultCachedGif", + "InlineQueryResultCachedMpeg4Gif", + "InlineQueryResultCachedPhoto", + "InlineQueryResultCachedSticker", + "InlineQueryResultCachedVideo", + "InlineQueryResultCachedVoice", + "InlineQueryResultContact", + "InlineQueryResultDocument", + "InlineQueryResultGame", + "InlineQueryResultGif", + "InlineQueryResultLocation", + "InlineQueryResultMpeg4Gif", + "InlineQueryResultPhoto", + "InlineQueryResultVenue", + "InlineQueryResultVideo", + "InlineQueryResultVoice", + "InputContactMessageContent", + "InputFile", + "InputInvoiceMessageContent", + "InputLocationMessageContent", + "InputMedia", + "InputMediaAnimation", + "InputMediaAudio", + "InputMediaDocument", + "InputMediaPhoto", + "InputMediaVideo", + "InputMessageContent", + "InputTextMessageContent", + "InputVenueMessageContent", + "Invoice", + "KeyboardButton", + "KeyboardButtonPollType", + "LabeledPrice", + "Location", + "LoginUrl", + "MAX_CAPTION_LENGTH", + "MAX_FILESIZE_DOWNLOAD", + "MAX_FILESIZE_UPLOAD", + "MAX_MESSAGES_PER_MINUTE_PER_GROUP", + "MAX_MESSAGES_PER_SECOND", + "MAX_MESSAGES_PER_SECOND_PER_CHAT", + "MAX_MESSAGE_LENGTH", + "MaskPosition", + "Message", + "MessageAutoDeleteTimerChanged", + "MessageEntity", + "MessageId", + "OrderInfo", + "ParseMode", + "PassportData", + "PassportElementError", + "PassportElementErrorDataField", + "PassportElementErrorFile", + "PassportElementErrorFiles", + "PassportElementErrorFrontSide", + "PassportElementErrorReverseSide", + "PassportElementErrorSelfie", + "PassportElementErrorTranslationFile", + "PassportElementErrorTranslationFiles", + "PassportElementErrorUnspecified", + "PassportFile", + "PersonalDetails", + "PhotoSize", + "Poll", + "PollAnswer", + "PollOption", + "PreCheckoutQuery", + "ProximityAlertTriggered", + "ReplyKeyboardMarkup", + "ReplyKeyboardRemove", + "ReplyMarkup", + "ResidentialAddress", + "SUPPORTED_WEBHOOK_PORTS", + "SecureData", + "SecureValue", + "ShippingAddress", + "ShippingOption", + "ShippingQuery", + "Sticker", + "StickerSet", + "SuccessfulPayment", + "PassportDecryptionError", + "TelegramError", + "TelegramObject", + "Update", + "User", + "UserProfilePhotos", + "Venue", + "Video", + "VideoNote", + "Voice", + "VoiceChatStarted", + "VoiceChatEnded", + "VoiceChatScheduled", + "VoiceChatParticipantsInvited", + "WebhookInfo", ) diff --git a/telegram/__main__.py b/telegram/__main__.py index 0e8db82761e..195aa04953b 100644 --- a/telegram/__main__.py +++ b/telegram/__main__.py @@ -39,16 +39,16 @@ def _git_revision() -> Optional[str]: 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}') - print(f'certifi {certifi.__version__}') # type: ignore[attr-defined] - sys_version = sys.version.replace('\n', ' ') - print(f'Python {sys_version}') + print(f"python-telegram-bot {telegram_ver}" + (f" ({git_revision})" if git_revision else "")) + print(f"Bot API {BOT_API_VERSION}") + print(f"certifi {certifi.__version__}") # type: ignore[attr-defined] + sys_version = sys.version.replace("\n", " ") + print(f"Python {sys_version}") def main() -> None: # skipcq: PY-D0003 print_ver_info() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/telegram/base.py b/telegram/base.py index 0f906e9a4ad..2cadd422999 100644 --- a/telegram/base.py +++ b/telegram/base.py @@ -23,26 +23,34 @@ import json # type: ignore[no-redef] import warnings -from typing import TYPE_CHECKING, List, Optional, Tuple, Type, TypeVar +from typing import TYPE_CHECKING, List, Optional, Type, TypeVar, Tuple from telegram.utils.types import JSONDict -from telegram.utils.deprecate import set_new_attribute_deprecated if TYPE_CHECKING: from telegram import Bot -TO = TypeVar('TO', bound='TelegramObject', covariant=True) +TO = TypeVar("TO", bound="TelegramObject", covariant=True) class TelegramObject: """Base class for most Telegram objects.""" - _id_attrs: Tuple[object, ...] = () - + # type hints in __new__ are not read by mypy (https://github.com/python/mypy/issues/1021). As a + # workaround we can type hint instance variables in __new__ using a syntax defined in PEP 526 - + # https://www.python.org/dev/peps/pep-0526/#class-and-instance-variable-annotations + if TYPE_CHECKING: + _id_attrs: Tuple[object, ...] # Adding slots reduces memory usage & allows for faster attribute access. # Only instance variables should be added to __slots__. - # We add __dict__ here for backward compatibility & also to avoid repetition for subclasses. - __slots__ = ('__dict__',) + __slots__ = ("_id_attrs",) + + def __new__(cls, *args: object, **kwargs: object) -> "TelegramObject": # pylint: disable=W0613 + # We add _id_attrs in __new__ instead of __init__ since we want to add this to the slots + # w/o calling __init__ in all of the subclasses. This is what we also do in BaseFilter. + instance = super().__new__(cls) + instance._id_attrs = () + return instance def __str__(self) -> str: return str(self.to_dict()) @@ -50,15 +58,12 @@ def __str__(self) -> str: def __getitem__(self, item: str) -> object: return getattr(self, item, None) - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - @staticmethod def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]: return None if data is None else data.copy() @classmethod - def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO]: + def de_json(cls: Type[TO], data: Optional[JSONDict], bot: "Bot") -> Optional[TO]: """Converts JSON data to a Telegram object. Args: @@ -76,10 +81,10 @@ def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO] if cls == TelegramObject: return cls() - return cls(bot=bot, **data) # type: ignore[call-arg] + return cls(bot=bot, **data) @classmethod - def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]: + 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: @@ -117,21 +122,22 @@ def to_dict(self) -> JSONDict: # TelegramObject class itself. attrs = {attr for cls in self.__class__.__mro__[:-2] for attr in cls.__slots__} for key in attrs: - if key == 'bot' or key.startswith('_'): + if key == "bot" or key.startswith("_"): continue value = getattr(self, key, None) if value is not None: - if hasattr(value, 'to_dict'): + if hasattr(value, "to_dict"): data[key] = value.to_dict() else: data[key] = value - if data.get('from_user'): - data['from'] = data.pop('from_user', None) + if data.get("from_user"): + data["from"] = data.pop("from_user", None) return data def __eq__(self, other: object) -> bool: + # pylint: disable=no-member if isinstance(other, self.__class__): if self._id_attrs == (): warnings.warn( @@ -144,9 +150,10 @@ def __eq__(self, other: object) -> bool: " for equivalence." ) return self._id_attrs == other._id_attrs - return super().__eq__(other) # pylint: disable=no-member + return super().__eq__(other) def __hash__(self) -> int: + # pylint: disable=no-member if self._id_attrs: - return hash((self.__class__, self._id_attrs)) # pylint: disable=no-member + return hash((self.__class__, self._id_attrs)) return super().__hash__() diff --git a/telegram/bot.py b/telegram/bot.py index 63fbd7556d3..1813fe4089b 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -116,7 +116,7 @@ MessageEntity, ) -RT = TypeVar('RT') +RT = TypeVar("RT") def log( # skipcq: PY-D0003 @@ -126,10 +126,10 @@ def log( # skipcq: PY-D0003 @functools.wraps(func) def decorator(*args: object, **kwargs: object) -> RT: # pylint: disable=W0613 - logger.debug('Entering: %s', func.__name__) + logger.debug("Entering: %s", func.__name__) result = func(*args, **kwargs) logger.debug(result) - logger.debug('Exiting: %s', func.__name__) + logger.debug("Exiting: %s", func.__name__) return result return decorator @@ -148,6 +148,11 @@ class Bot(TelegramObject): incorporated into PTB. However, this is not guaranteed to work, i.e. it will fail for passing files. + .. versionchanged:: 14.0 + * Removed the deprecated methods ``kick_chat_member``, ``kickChatMember``, + ``get_chat_members_count`` and ``getChatMembersCount``. + * Removed the deprecated property ``commands``. + Args: token (:obj:`str`): Bot's unique authentication. base_url (:obj:`str`, optional): Telegram Bot API service URL. @@ -167,15 +172,14 @@ class Bot(TelegramObject): """ __slots__ = ( - 'token', - 'base_url', - 'base_file_url', - 'private_key', - 'defaults', - '_bot', - '_commands', - '_request', - 'logger', + "token", + "base_url", + "base_file_url", + "private_key", + "defaults", + "_bot", + "_request", + "logger", ) def __init__( @@ -183,10 +187,10 @@ def __init__( token: str, base_url: str = None, base_file_url: str = None, - request: 'Request' = None, + request: "Request" = None, private_key: bytes = None, private_key_password: bytes = None, - defaults: 'Defaults' = None, + defaults: "Defaults" = None, ): self.token = self._validate_token(token) @@ -195,21 +199,20 @@ def __init__( if self.defaults: warnings.warn( - 'Passing Defaults to telegram.Bot is deprecated. Use telegram.ext.ExtBot instead.', + "Passing Defaults to telegram.Bot is deprecated. Use telegram.ext.ExtBot instead.", TelegramDeprecationWarning, stacklevel=3, ) if base_url is None: - base_url = 'https://api.telegram.org/bot' + base_url = "https://api.telegram.org/bot" if base_file_url is None: - base_file_url = 'https://api.telegram.org/file/bot' + base_file_url = "https://api.telegram.org/file/bot" self.base_url = str(base_url) + str(self.token) self.base_file_url = str(base_file_url) + str(self.token) self._bot: Optional[User] = None - self._commands: Optional[List[BotCommand]] = None self._request = request or Request() self.private_key = None self.logger = logging.getLogger(__name__) @@ -217,21 +220,13 @@ def __init__( if private_key: if not CRYPTO_INSTALLED: raise RuntimeError( - 'To use Telegram Passports, PTB must be installed via `pip install ' - 'python-telegram-bot[passport]`.' + "To use Telegram Passports, PTB must be installed via `pip install " + "python-telegram-bot[passport]`." ) self.private_key = serialization.load_pem_private_key( private_key, password=private_key_password, backend=default_backend() ) - # The ext_bot argument is a little hack to get warnings handled correctly. - # It's not very clean, but the warnings will be dropped at some point anyway. - def __setattr__(self, key: str, value: object, ext_bot: bool = False) -> None: - if issubclass(self.__class__, Bot) and self.__class__ is not Bot and not ext_bot: - object.__setattr__(self, key, value) - return - super().__setattr__(key, value) - def _insert_defaults( self, data: Dict[str, object], timeout: ODVInput[float] ) -> Optional[float]: @@ -285,7 +280,7 @@ def _post( data = api_kwargs # Insert is in-place, so no return value for data - if endpoint != 'getUpdates': + if endpoint != "getUpdates": effective_timeout = self._insert_defaults(data, timeout) else: effective_timeout = cast(float, timeout) @@ -293,7 +288,7 @@ def _post( data = {key: value for key, value in data.items() if value is not None} return self.request.post( - f'{self.base_url}/{endpoint}', data=data, timeout=effective_timeout + f"{self.base_url}/{endpoint}", data=data, timeout=effective_timeout ) def _message( @@ -308,26 +303,26 @@ def _message( api_kwargs: JSONDict = None, ) -> Union[bool, Message]: if reply_to_message_id is not None: - data['reply_to_message_id'] = reply_to_message_id + data["reply_to_message_id"] = reply_to_message_id # We don't check if (DEFAULT_)None here, so that _put is able to insert the defaults # correctly, if necessary - data['disable_notification'] = disable_notification - data['allow_sending_without_reply'] = allow_sending_without_reply + data["disable_notification"] = disable_notification + data["allow_sending_without_reply"] = allow_sending_without_reply if reply_markup is not None: if isinstance(reply_markup, ReplyMarkup): # We need to_json() instead of to_dict() here, because reply_markups may be # attached to media messages, which aren't json dumped by utils.request - data['reply_markup'] = reply_markup.to_json() + data["reply_markup"] = reply_markup.to_json() else: - data['reply_markup'] = reply_markup + data["reply_markup"] = reply_markup - if data.get('media') and (data['media'].parse_mode == DEFAULT_NONE): + if data.get("media") and (data["media"].parse_mode == DEFAULT_NONE): if self.defaults: - data['media'].parse_mode = DefaultValue.get_value(self.defaults.parse_mode) + data["media"].parse_mode = DefaultValue.get_value(self.defaults.parse_mode) else: - data['media'].parse_mode = None + data["media"].parse_mode = None result = self._post(endpoint, data, timeout=timeout, api_kwargs=api_kwargs) @@ -346,7 +341,7 @@ def _validate_token(token: str) -> str: if any(x.isspace() for x in token): raise InvalidToken() - left, sep, _right = token.partition(':') + left, sep, _right = token.partition(":") if (not sep) or (not left.isdigit()) or (len(left) < 3): raise InvalidToken() @@ -399,30 +394,10 @@ 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 as available in the default scope. - - .. deprecated:: 13.7 - This property has been deprecated since there can be different commands available for - different scopes. - """ - warnings.warn( - "Bot.commands has been deprecated since there can be different command " - "lists for different scopes.", - TelegramDeprecationWarning, - stacklevel=2, - ) - - if self._commands is None: - self._commands = self.get_my_commands() - return self._commands - @property def name(self) -> str: """:obj:`str`: Bot's @username.""" - return f'@{self.username}' + return f"@{self.username}" @log def get_me(self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None) -> User: @@ -443,7 +418,7 @@ def get_me(self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = :class:`telegram.error.TelegramError` """ - result = self._post('getMe', timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getMe", timeout=timeout, api_kwargs=api_kwargs) self._bot = User.de_json(result, self) # type: ignore[return-value, arg-type] @@ -462,7 +437,7 @@ def send_message( timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Message: """Use this method to send text messages. @@ -501,17 +476,17 @@ def send_message( """ data: JSONDict = { - 'chat_id': chat_id, - 'text': text, - 'parse_mode': parse_mode, - 'disable_web_page_preview': disable_web_page_preview, + "chat_id": chat_id, + "text": text, + "parse_mode": parse_mode, + "disable_web_page_preview": disable_web_page_preview, } if entities: - data['entities'] = [me.to_dict() for me in entities] + data["entities"] = [me.to_dict() for me in entities] return self._message( # type: ignore[return-value] - 'sendMessage', + "sendMessage", data, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, @@ -561,9 +536,9 @@ def delete_message( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'message_id': message_id} + data: JSONDict = {"chat_id": chat_id, "message_id": message_id} - result = self._post('deleteMessage', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("deleteMessage", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -603,14 +578,14 @@ def forward_message( data: JSONDict = {} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if from_chat_id: - data['from_chat_id'] = from_chat_id + data["from_chat_id"] = from_chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id return self._message( # type: ignore[return-value] - 'forwardMessage', + "forwardMessage", data, disable_notification=disable_notification, timeout=timeout, @@ -621,7 +596,7 @@ def forward_message( def send_photo( self, chat_id: Union[int, str], - photo: Union[FileInput, 'PhotoSize'], + photo: Union[FileInput, "PhotoSize"], caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, @@ -630,7 +605,7 @@ def send_photo( parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, ) -> Message: """Use this method to send photos. @@ -685,19 +660,19 @@ def send_photo( """ data: JSONDict = { - 'chat_id': chat_id, - 'photo': parse_file_input(photo, PhotoSize, filename=filename), - 'parse_mode': parse_mode, + "chat_id": chat_id, + "photo": parse_file_input(photo, PhotoSize, filename=filename), + "parse_mode": parse_mode, } if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] return self._message( # type: ignore[return-value] - 'sendPhoto', + "sendPhoto", data, timeout=timeout, disable_notification=disable_notification, @@ -711,7 +686,7 @@ def send_photo( def send_audio( self, chat_id: Union[int, str], - audio: Union[FileInput, 'Audio'], + audio: Union[FileInput, "Audio"], duration: int = None, performer: str = None, title: str = None, @@ -724,7 +699,7 @@ def send_audio( thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, ) -> Message: """ @@ -798,27 +773,27 @@ def send_audio( """ data: JSONDict = { - 'chat_id': chat_id, - 'audio': parse_file_input(audio, Audio, filename=filename), - 'parse_mode': parse_mode, + "chat_id": chat_id, + "audio": parse_file_input(audio, Audio, filename=filename), + "parse_mode": parse_mode, } if duration: - data['duration'] = duration + data["duration"] = duration if performer: - data['performer'] = performer + data["performer"] = performer if title: - data['title'] = title + data["title"] = title if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) + data["thumb"] = parse_file_input(thumb, attach=True) return self._message( # type: ignore[return-value] - 'sendAudio', + "sendAudio", data, timeout=timeout, disable_notification=disable_notification, @@ -832,7 +807,7 @@ def send_audio( def send_document( self, chat_id: Union[int, str], - document: Union[FileInput, 'Document'], + document: Union[FileInput, "Document"], filename: str = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -844,7 +819,7 @@ def send_document( api_kwargs: JSONDict = None, disable_content_type_detection: bool = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Message: """ Use this method to send general files. @@ -911,23 +886,23 @@ def send_document( """ data: JSONDict = { - 'chat_id': chat_id, - 'document': parse_file_input(document, Document, filename=filename), - 'parse_mode': parse_mode, + "chat_id": chat_id, + "document": parse_file_input(document, Document, filename=filename), + "parse_mode": parse_mode, } if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] if disable_content_type_detection is not None: - data['disable_content_type_detection'] = disable_content_type_detection + data["disable_content_type_detection"] = disable_content_type_detection if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) + data["thumb"] = parse_file_input(thumb, attach=True) return self._message( # type: ignore[return-value] - 'sendDocument', + "sendDocument", data, timeout=timeout, disable_notification=disable_notification, @@ -941,7 +916,7 @@ def send_document( def send_sticker( self, chat_id: Union[int, str], - sticker: Union[FileInput, 'Sticker'], + sticker: Union[FileInput, "Sticker"], disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, reply_markup: ReplyMarkup = None, @@ -988,10 +963,10 @@ def send_sticker( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'sticker': parse_file_input(sticker, Sticker)} + data: JSONDict = {"chat_id": chat_id, "sticker": parse_file_input(sticker, Sticker)} return self._message( # type: ignore[return-value] - 'sendSticker', + "sendSticker", data, timeout=timeout, disable_notification=disable_notification, @@ -1005,7 +980,7 @@ def send_sticker( def send_video( self, chat_id: Union[int, str], - video: Union[FileInput, 'Video'], + video: Union[FileInput, "Video"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1019,7 +994,7 @@ def send_video( thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, ) -> Message: """ @@ -1096,28 +1071,28 @@ def send_video( """ data: JSONDict = { - 'chat_id': chat_id, - 'video': parse_file_input(video, Video, filename=filename), - 'parse_mode': parse_mode, + "chat_id": chat_id, + "video": parse_file_input(video, Video, filename=filename), + "parse_mode": parse_mode, } if duration: - data['duration'] = duration + data["duration"] = duration if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] if supports_streaming: - data['supports_streaming'] = supports_streaming + data["supports_streaming"] = supports_streaming if width: - data['width'] = width + data["width"] = width if height: - data['height'] = height + data["height"] = height if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) + data["thumb"] = parse_file_input(thumb, attach=True) return self._message( # type: ignore[return-value] - 'sendVideo', + "sendVideo", data, timeout=timeout, disable_notification=disable_notification, @@ -1131,7 +1106,7 @@ def send_video( def send_video_note( self, chat_id: Union[int, str], - video_note: Union[FileInput, 'VideoNote'], + video_note: Union[FileInput, "VideoNote"], duration: int = None, length: int = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1204,19 +1179,19 @@ def send_video_note( """ data: JSONDict = { - 'chat_id': chat_id, - 'video_note': parse_file_input(video_note, VideoNote, filename=filename), + "chat_id": chat_id, + "video_note": parse_file_input(video_note, VideoNote, filename=filename), } if duration is not None: - data['duration'] = duration + data["duration"] = duration if length is not None: - data['length'] = length + data["length"] = length if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) + data["thumb"] = parse_file_input(thumb, attach=True) return self._message( # type: ignore[return-value] - 'sendVideoNote', + "sendVideoNote", data, timeout=timeout, disable_notification=disable_notification, @@ -1230,7 +1205,7 @@ def send_video_note( def send_animation( self, chat_id: Union[int, str], - animation: Union[FileInput, 'Animation'], + animation: Union[FileInput, "Animation"], duration: int = None, width: int = None, height: int = None, @@ -1243,7 +1218,7 @@ def send_animation( timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, ) -> Message: """ @@ -1314,26 +1289,26 @@ def send_animation( """ data: JSONDict = { - 'chat_id': chat_id, - 'animation': parse_file_input(animation, Animation, filename=filename), - 'parse_mode': parse_mode, + "chat_id": chat_id, + "animation": parse_file_input(animation, Animation, filename=filename), + "parse_mode": parse_mode, } if duration: - data['duration'] = duration + data["duration"] = duration if width: - data['width'] = width + data["width"] = width if height: - data['height'] = height + data["height"] = height if thumb: - data['thumb'] = parse_file_input(thumb, attach=True) + data["thumb"] = parse_file_input(thumb, attach=True) if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] return self._message( # type: ignore[return-value] - 'sendAnimation', + "sendAnimation", data, timeout=timeout, disable_notification=disable_notification, @@ -1347,7 +1322,7 @@ def send_animation( def send_voice( self, chat_id: Union[int, str], - voice: Union[FileInput, 'Voice'], + voice: Union[FileInput, "Voice"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1357,7 +1332,7 @@ def send_voice( parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, ) -> Message: """ @@ -1417,21 +1392,21 @@ def send_voice( """ data: JSONDict = { - 'chat_id': chat_id, - 'voice': parse_file_input(voice, Voice, filename=filename), - 'parse_mode': parse_mode, + "chat_id": chat_id, + "voice": parse_file_input(voice, Voice, filename=filename), + "parse_mode": parse_mode, } if duration: - data['duration'] = duration + data["duration"] = duration if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] return self._message( # type: ignore[return-value] - 'sendVoice', + "sendVoice", data, timeout=timeout, disable_notification=disable_notification, @@ -1446,7 +1421,7 @@ def send_media_group( self, chat_id: Union[int, str], media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] ], disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, @@ -1479,13 +1454,13 @@ def send_media_group( :class:`telegram.error.TelegramError` """ data: JSONDict = { - 'chat_id': chat_id, - 'media': media, - 'disable_notification': disable_notification, - 'allow_sending_without_reply': allow_sending_without_reply, + "chat_id": chat_id, + "media": media, + "disable_notification": disable_notification, + "allow_sending_without_reply": allow_sending_without_reply, } - for med in data['media']: + for med in data["media"]: if med.parse_mode == DEFAULT_NONE: if self.defaults: med.parse_mode = DefaultValue.get_value(self.defaults.parse_mode) @@ -1493,9 +1468,9 @@ def send_media_group( med.parse_mode = None if reply_to_message_id: - data['reply_to_message_id'] = reply_to_message_id + data["reply_to_message_id"] = reply_to_message_id - result = self._post('sendMediaGroup', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("sendMediaGroup", data, timeout=timeout, api_kwargs=api_kwargs) return Message.de_list(result, self) # type: ignore @@ -1573,19 +1548,19 @@ def send_location( latitude = location.latitude longitude = location.longitude - data: JSONDict = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} + data: JSONDict = {"chat_id": chat_id, "latitude": latitude, "longitude": longitude} if live_period: - data['live_period'] = live_period + data["live_period"] = live_period if horizontal_accuracy: - data['horizontal_accuracy'] = horizontal_accuracy + data["horizontal_accuracy"] = horizontal_accuracy if heading: - data['heading'] = heading + data["heading"] = heading if proximity_alert_radius: - data['proximity_alert_radius'] = proximity_alert_radius + data["proximity_alert_radius"] = proximity_alert_radius return self._message( # type: ignore[return-value] - 'sendLocation', + "sendLocation", data, timeout=timeout, disable_notification=disable_notification, @@ -1661,23 +1636,23 @@ def edit_message_live_location( latitude = location.latitude longitude = location.longitude - data: JSONDict = {'latitude': latitude, 'longitude': longitude} + data: JSONDict = {"latitude": latitude, "longitude": longitude} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id if horizontal_accuracy: - data['horizontal_accuracy'] = horizontal_accuracy + data["horizontal_accuracy"] = horizontal_accuracy if heading: - data['heading'] = heading + data["heading"] = heading if proximity_alert_radius: - data['proximity_alert_radius'] = proximity_alert_radius + data["proximity_alert_radius"] = proximity_alert_radius return self._message( - 'editMessageLiveLocation', + "editMessageLiveLocation", data, timeout=timeout, reply_markup=reply_markup, @@ -1720,14 +1695,14 @@ def stop_message_live_location( data: JSONDict = {} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id return self._message( - 'stopMessageLiveLocation', + "stopMessageLiveLocation", data, timeout=timeout, reply_markup=reply_markup, @@ -1761,7 +1736,7 @@ def send_venue( :obj:`title` and :obj:`address` and optionally :obj:`foursquare_id` and :obj:`foursquare_type` or optionally :obj:`google_place_id` and :obj:`google_place_type`. - * Foursquare details and Google Pace details are mutually exclusive. However, this + * Foursquare details and Google Place details are mutually exclusive. However, this behaviour is undocumented and might be changed by Telegram. Args: @@ -1819,24 +1794,24 @@ def send_venue( google_place_type = venue.google_place_type data: JSONDict = { - 'chat_id': chat_id, - 'latitude': latitude, - 'longitude': longitude, - 'address': address, - 'title': title, + "chat_id": chat_id, + "latitude": latitude, + "longitude": longitude, + "address": address, + "title": title, } if foursquare_id: - data['foursquare_id'] = foursquare_id + data["foursquare_id"] = foursquare_id if foursquare_type: - data['foursquare_type'] = foursquare_type + data["foursquare_type"] = foursquare_type if google_place_id: - data['google_place_id'] = google_place_id + data["google_place_id"] = google_place_id if google_place_type: - data['google_place_type'] = google_place_type + data["google_place_type"] = google_place_type return self._message( # type: ignore[return-value] - 'sendVenue', + "sendVenue", data, timeout=timeout, disable_notification=disable_notification, @@ -1911,18 +1886,18 @@ def send_contact( vcard = contact.vcard data: JSONDict = { - 'chat_id': chat_id, - 'phone_number': phone_number, - 'first_name': first_name, + "chat_id": chat_id, + "phone_number": phone_number, + "first_name": first_name, } if last_name: - data['last_name'] = last_name + data["last_name"] = last_name if vcard: - data['vcard'] = vcard + data["vcard"] = vcard return self._message( # type: ignore[return-value] - 'sendContact', + "sendContact", data, timeout=timeout, disable_notification=disable_notification, @@ -1972,10 +1947,10 @@ def send_game( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'game_short_name': game_short_name} + data: JSONDict = {"chat_id": chat_id, "game_short_name": game_short_name} return self._message( # type: ignore[return-value] - 'sendGame', + "sendGame", data, timeout=timeout, disable_notification=disable_notification, @@ -2018,20 +1993,20 @@ def send_chat_action( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'action': action} + data: JSONDict = {"chat_id": chat_id, "action": action} - result = self._post('sendChatAction', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("sendChatAction", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] def _effective_inline_results( # pylint: disable=R0201 self, results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] ], next_offset: str = None, current_offset: str = None, - ) -> Tuple[Sequence['InlineQueryResult'], Optional[str]]: + ) -> Tuple[Sequence["InlineQueryResult"], Optional[str]]: """ Builds the effective results from the results input. We make this a stand-alone method so tg.ext.ExtBot can wrap it. @@ -2041,23 +2016,23 @@ def _effective_inline_results( # pylint: disable=R0201 """ if current_offset is not None and next_offset is not None: - raise ValueError('`current_offset` and `next_offset` are mutually exclusive!') + raise ValueError("`current_offset` and `next_offset` are mutually exclusive!") if current_offset is not None: # Convert the string input to integer - if current_offset == '': + if current_offset == "": current_offset_int = 0 else: current_offset_int = int(current_offset) # for now set to empty string, stating that there are no more results # might change later - next_offset = '' + next_offset = "" if callable(results): callable_output = results(current_offset_int) if not callable_output: - effective_results: Sequence['InlineQueryResult'] = [] + effective_results: Sequence["InlineQueryResult"] = [] else: effective_results = callable_output # the callback *might* return more results on the next call, so we increment @@ -2085,7 +2060,7 @@ def answer_inline_query( self, inline_query_id: str, results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] ], cache_time: int = 300, is_personal: bool = None, @@ -2157,14 +2132,14 @@ def answer_inline_query( @no_type_check def _set_defaults(res): # pylint: disable=W0212 - if hasattr(res, 'parse_mode') and res.parse_mode == DEFAULT_NONE: + if hasattr(res, "parse_mode") and res.parse_mode == DEFAULT_NONE: if self.defaults: res.parse_mode = self.defaults.parse_mode else: res.parse_mode = None - if hasattr(res, 'input_message_content') and res.input_message_content: + if hasattr(res, "input_message_content") and res.input_message_content: if ( - hasattr(res.input_message_content, 'parse_mode') + hasattr(res.input_message_content, "parse_mode") and res.input_message_content.parse_mode == DEFAULT_NONE ): if self.defaults: @@ -2174,7 +2149,7 @@ def _set_defaults(res): else: res.input_message_content.parse_mode = None if ( - hasattr(res.input_message_content, 'disable_web_page_preview') + hasattr(res.input_message_content, "disable_web_page_preview") and res.input_message_content.disable_web_page_preview == DEFAULT_NONE ): if self.defaults: @@ -2194,21 +2169,21 @@ def _set_defaults(res): results_dicts = [res.to_dict() for res in effective_results] - data: JSONDict = {'inline_query_id': inline_query_id, 'results': results_dicts} + data: JSONDict = {"inline_query_id": inline_query_id, "results": results_dicts} if cache_time or cache_time == 0: - data['cache_time'] = cache_time + data["cache_time"] = cache_time if is_personal: - data['is_personal'] = is_personal + data["is_personal"] = is_personal if next_offset is not None: - data['next_offset'] = next_offset + data["next_offset"] = next_offset if switch_pm_text: - data['switch_pm_text'] = switch_pm_text + data["switch_pm_text"] = switch_pm_text if switch_pm_parameter: - data['switch_pm_parameter'] = switch_pm_parameter + data["switch_pm_parameter"] = switch_pm_parameter return self._post( # type: ignore[return-value] - 'answerInlineQuery', + "answerInlineQuery", data, timeout=timeout, api_kwargs=api_kwargs, @@ -2244,14 +2219,14 @@ def get_user_profile_photos( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'user_id': user_id} + data: JSONDict = {"user_id": user_id} if offset is not None: - data['offset'] = offset + data["offset"] = offset if limit: - data['limit'] = limit + data["limit"] = limit - result = self._post('getUserProfilePhotos', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getUserProfilePhotos", data, timeout=timeout, api_kwargs=api_kwargs) return UserProfilePhotos.de_json(result, self) # type: ignore[return-value, arg-type] @@ -2302,49 +2277,19 @@ def get_file( except AttributeError: pass - data: JSONDict = {'file_id': file_id} + data: JSONDict = {"file_id": file_id} - result = self._post('getFile', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getFile", data, timeout=timeout, api_kwargs=api_kwargs) - if result.get('file_path') and not is_local_file( # type: ignore[union-attr] - result['file_path'] # type: ignore[index] + if result.get("file_path") and not is_local_file( # type: ignore[union-attr] + result["file_path"] # type: ignore[index] ): - result['file_path'] = '{}/{}'.format( # type: ignore[index] - self.base_file_url, result['file_path'] # type: ignore[index] + result["file_path"] = "{}/{}".format( # type: ignore[index] + self.base_file_url, result["file_path"] # type: ignore[index] ) return File.de_json(result, self) # type: ignore[return-value, arg-type] - @log - def kick_chat_member( - self, - chat_id: Union[str, int], - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Deprecated, use :func:`~telegram.Bot.ban_chat_member` instead. - - .. deprecated:: 13.7 - - """ - warnings.warn( - '`bot.kick_chat_member` is deprecated. Use `bot.ban_chat_member` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return self.ban_chat_member( - chat_id=chat_id, - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - @log def ban_chat_member( self, @@ -2392,19 +2337,19 @@ def ban_chat_member( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} if until_date is not None: if isinstance(until_date, datetime): until_date = to_timestamp( until_date, tzinfo=self.defaults.tzinfo if self.defaults else None ) - data['until_date'] = until_date + data["until_date"] = until_date if revoke_messages is not None: - data['revoke_messages'] = revoke_messages + data["revoke_messages"] = revoke_messages - result = self._post('banChatMember', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("banChatMember", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -2443,12 +2388,12 @@ def unban_chat_member( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} if only_if_banned is not None: - data['only_if_banned'] = only_if_banned + data["only_if_banned"] = only_if_banned - result = self._post('unbanChatMember', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("unbanChatMember", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -2500,18 +2445,18 @@ def answer_callback_query( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'callback_query_id': callback_query_id} + data: JSONDict = {"callback_query_id": callback_query_id} if text: - data['text'] = text + data["text"] = text if show_alert: - data['show_alert'] = show_alert + data["show_alert"] = show_alert if url: - data['url'] = url + data["url"] = url if cache_time is not None: - data['cache_time'] = cache_time + data["cache_time"] = cache_time - result = self._post('answerCallbackQuery', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("answerCallbackQuery", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -2527,7 +2472,7 @@ def edit_message_text( reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Union[Message, bool]: """ Use this method to edit text and game messages. @@ -2565,22 +2510,22 @@ def edit_message_text( """ data: JSONDict = { - 'text': text, - 'parse_mode': parse_mode, - 'disable_web_page_preview': disable_web_page_preview, + "text": text, + "parse_mode": parse_mode, + "disable_web_page_preview": disable_web_page_preview, } if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id if entities: - data['entities'] = [me.to_dict() for me in entities] + data["entities"] = [me.to_dict() for me in entities] return self._message( - 'editMessageText', + "editMessageText", data, timeout=timeout, reply_markup=reply_markup, @@ -2598,7 +2543,7 @@ def edit_message_caption( timeout: ODVInput[float] = DEFAULT_NONE, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Union[Message, bool]: """ Use this method to edit captions of messages. @@ -2637,25 +2582,25 @@ def edit_message_caption( """ if inline_message_id is None and (chat_id is None or message_id is None): raise ValueError( - 'edit_message_caption: Both chat_id and message_id are required when ' - 'inline_message_id is not specified' + "edit_message_caption: Both chat_id and message_id are required when " + "inline_message_id is not specified" ) - data: JSONDict = {'parse_mode': parse_mode} + data: JSONDict = {"parse_mode": parse_mode} if caption: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = [me.to_dict() for me in caption_entities] + data["caption_entities"] = [me.to_dict() for me in caption_entities] if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id return self._message( - 'editMessageCaption', + "editMessageCaption", data, timeout=timeout, reply_markup=reply_markup, @@ -2665,10 +2610,10 @@ def edit_message_caption( @log def edit_message_media( self, + media: "InputMedia", chat_id: Union[str, int] = None, message_id: int = None, inline_message_id: int = None, - media: 'InputMedia' = None, reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, @@ -2681,6 +2626,8 @@ def edit_message_media( ``file_id`` or specify a URL. Args: + media (:class:`telegram.InputMedia`): An object for a new media content + of the message. chat_id (:obj:`int` | :obj:`str`, optional): Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format ``@channelusername``). @@ -2688,8 +2635,6 @@ def edit_message_media( Identifier of the message to edit. inline_message_id (:obj:`str`, optional): Required if chat_id and message_id are not specified. Identifier of the inline message. - media (:class:`telegram.InputMedia`): An object for a new media content - of the message. reply_markup (:class:`telegram.InlineKeyboardMarkup`, optional): A JSON-serialized object for an inline keyboard. timeout (:obj:`int` | :obj:`float`, optional): If this value is specified, use it as @@ -2699,7 +2644,7 @@ def edit_message_media( Telegram API. Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the + :class:`telegram.Message`: On success, if edited message is not an inline message, the edited Message is returned, otherwise :obj:`True` is returned. Raises: @@ -2707,21 +2652,21 @@ def edit_message_media( """ 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 ' - 'inline_message_id is not specified' + "edit_message_media: Both chat_id and message_id are required when " + "inline_message_id is not specified" ) - data: JSONDict = {'media': media} + data: JSONDict = {"media": media} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id return self._message( - 'editMessageMedia', + "editMessageMedia", data, timeout=timeout, reply_markup=reply_markup, @@ -2734,7 +2679,7 @@ def edit_message_reply_markup( chat_id: Union[str, int] = None, message_id: int = None, inline_message_id: int = None, - reply_markup: Optional['InlineKeyboardMarkup'] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, ) -> Union[Message, bool]: @@ -2768,21 +2713,21 @@ def edit_message_reply_markup( """ if inline_message_id is None and (chat_id is None or message_id is None): raise ValueError( - 'edit_message_reply_markup: Both chat_id and message_id are required when ' - 'inline_message_id is not specified' + "edit_message_reply_markup: Both chat_id and message_id are required when " + "inline_message_id is not specified" ) data: JSONDict = {} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id return self._message( - 'editMessageReplyMarkup', + "editMessageReplyMarkup", data, timeout=timeout, reply_markup=reply_markup, @@ -2842,14 +2787,14 @@ def get_updates( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'timeout': timeout} + data: JSONDict = {"timeout": timeout} if offset: - data['offset'] = offset + data["offset"] = offset if limit: - data['limit'] = limit + data["limit"] = limit if allowed_updates is not None: - data['allowed_updates'] = allowed_updates + data["allowed_updates"] = allowed_updates # Ideally we'd use an aggressive read timeout for the polling. However, # * Short polling should return within 2 seconds. @@ -2859,7 +2804,7 @@ def get_updates( result = cast( List[JSONDict], self._post( - 'getUpdates', + "getUpdates", data, timeout=float(read_latency) + float(timeout), api_kwargs=api_kwargs, @@ -2867,16 +2812,16 @@ def get_updates( ) if result: - self.logger.debug('Getting updates: %s', [u['update_id'] for u in result]) + self.logger.debug("Getting updates: %s", [u["update_id"] for u in result]) else: - self.logger.debug('No new updates found.') + self.logger.debug("No new updates found.") return Update.de_list(result, self) # type: ignore[return-value] @log def set_webhook( self, - url: str = None, + url: str, certificate: FileInput = None, timeout: ODVInput[float] = DEFAULT_NONE, max_connections: int = 40, @@ -2947,22 +2892,20 @@ def set_webhook( .. _`guide to Webhooks`: https://core.telegram.org/bots/webhooks """ - data: JSONDict = {} + data: JSONDict = {"url": url} - if url is not None: - data['url'] = url if certificate: - data['certificate'] = parse_file_input(certificate) + data["certificate"] = parse_file_input(certificate) if max_connections is not None: - data['max_connections'] = max_connections + data["max_connections"] = max_connections if allowed_updates is not None: - data['allowed_updates'] = allowed_updates + data["allowed_updates"] = allowed_updates if ip_address: - data['ip_address'] = ip_address + data["ip_address"] = ip_address if drop_pending_updates: - data['drop_pending_updates'] = drop_pending_updates + data["drop_pending_updates"] = drop_pending_updates - result = self._post('setWebhook', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setWebhook", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -2996,9 +2939,9 @@ def delete_webhook( data = {} if drop_pending_updates: - data['drop_pending_updates'] = drop_pending_updates + data["drop_pending_updates"] = drop_pending_updates - result = self._post('deleteWebhook', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("deleteWebhook", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3027,9 +2970,9 @@ def leave_chat( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('leaveChat', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("leaveChat", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3060,9 +3003,9 @@ def get_chat( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('getChat', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getChat", data, timeout=timeout, api_kwargs=api_kwargs) return Chat.de_json(result, self) # type: ignore[return-value, arg-type] @@ -3095,32 +3038,12 @@ def get_chat_administrators( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('getChatAdministrators', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getChatAdministrators", data, timeout=timeout, api_kwargs=api_kwargs) return ChatMember.de_list(result, self) # type: ignore - @log - def get_chat_members_count( - self, - chat_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - api_kwargs: JSONDict = None, - ) -> int: - """ - Deprecated, use :func:`~telegram.Bot.get_chat_member_count` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`bot.get_chat_members_count` is deprecated. ' - 'Use `bot.get_chat_member_count` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return self.get_chat_member_count(chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs) - @log def get_chat_member_count( self, @@ -3148,9 +3071,9 @@ def get_chat_member_count( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('getChatMemberCount', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getChatMemberCount", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3181,9 +3104,9 @@ def get_chat_member( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} - result = self._post('getChatMember', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getChatMember", data, timeout=timeout, api_kwargs=api_kwargs) return ChatMember.de_json(result, self) # type: ignore[return-value, arg-type] @@ -3214,9 +3137,9 @@ def set_chat_sticker_set( Returns: :obj:`bool`: On success, :obj:`True` is returned. """ - data: JSONDict = {'chat_id': chat_id, 'sticker_set_name': sticker_set_name} + data: JSONDict = {"chat_id": chat_id, "sticker_set_name": sticker_set_name} - result = self._post('setChatStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setChatStickerSet", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3244,9 +3167,9 @@ def delete_chat_sticker_set( Returns: :obj:`bool`: On success, :obj:`True` is returned. """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('deleteChatStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("deleteChatStickerSet", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3269,7 +3192,7 @@ def get_webhook_info( :class:`telegram.WebhookInfo` """ - result = self._post('getWebhookInfo', None, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getWebhookInfo", None, timeout=timeout, api_kwargs=api_kwargs) return WebhookInfo.de_json(result, self) # type: ignore[return-value, arg-type] @@ -3317,21 +3240,21 @@ def set_game_score( current score in the chat and force is :obj:`False`. """ - data: JSONDict = {'user_id': user_id, 'score': score} + data: JSONDict = {"user_id": user_id, "score": score} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id if force is not None: - data['force'] = force + data["force"] = force if disable_edit_message is not None: - data['disable_edit_message'] = disable_edit_message + data["disable_edit_message"] = disable_edit_message return self._message( - 'setGameScore', + "setGameScore", data, timeout=timeout, api_kwargs=api_kwargs, @@ -3377,16 +3300,16 @@ def get_game_high_scores( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'user_id': user_id} + data: JSONDict = {"user_id": user_id} if chat_id: - data['chat_id'] = chat_id + data["chat_id"] = chat_id if message_id: - data['message_id'] = message_id + data["message_id"] = message_id if inline_message_id: - data['inline_message_id'] = inline_message_id + data["inline_message_id"] = inline_message_id - result = self._post('getGameHighScores', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getGameHighScores", data, timeout=timeout, api_kwargs=api_kwargs) return GameHighScore.de_list(result, self) # type: ignore @@ -3399,7 +3322,7 @@ def send_invoice( payload: str, provider_token: str, currency: str, - prices: List['LabeledPrice'], + prices: List["LabeledPrice"], start_parameter: str = None, photo_url: str = None, photo_size: int = None, @@ -3516,50 +3439,50 @@ def send_invoice( """ data: JSONDict = { - 'chat_id': chat_id, - 'title': title, - 'description': description, - 'payload': payload, - 'provider_token': provider_token, - 'currency': currency, - 'prices': [p.to_dict() for p in prices], + "chat_id": chat_id, + "title": title, + "description": description, + "payload": payload, + "provider_token": provider_token, + "currency": currency, + "prices": [p.to_dict() for p in prices], } if max_tip_amount is not None: - data['max_tip_amount'] = max_tip_amount + data["max_tip_amount"] = max_tip_amount if suggested_tip_amounts is not None: - data['suggested_tip_amounts'] = suggested_tip_amounts + data["suggested_tip_amounts"] = suggested_tip_amounts if start_parameter is not None: - data['start_parameter'] = start_parameter + data["start_parameter"] = start_parameter if provider_data is not None: if isinstance(provider_data, str): - data['provider_data'] = provider_data + data["provider_data"] = provider_data else: - data['provider_data'] = json.dumps(provider_data) + data["provider_data"] = json.dumps(provider_data) if photo_url is not None: - data['photo_url'] = photo_url + data["photo_url"] = photo_url if photo_size is not None: - data['photo_size'] = photo_size + data["photo_size"] = photo_size if photo_width is not None: - data['photo_width'] = photo_width + data["photo_width"] = photo_width if photo_height is not None: - data['photo_height'] = photo_height + data["photo_height"] = photo_height if need_name is not None: - data['need_name'] = need_name + data["need_name"] = need_name if need_phone_number is not None: - data['need_phone_number'] = need_phone_number + data["need_phone_number"] = need_phone_number if need_email is not None: - data['need_email'] = need_email + data["need_email"] = need_email if need_shipping_address is not None: - data['need_shipping_address'] = need_shipping_address + data["need_shipping_address"] = need_shipping_address if is_flexible is not None: - data['is_flexible'] = is_flexible + data["is_flexible"] = is_flexible if send_phone_number_to_provider is not None: - data['send_phone_number_to_provider'] = send_phone_number_to_provider + data["send_phone_number_to_provider"] = send_phone_number_to_provider if send_email_to_provider is not None: - data['send_email_to_provider'] = send_email_to_provider + data["send_email_to_provider"] = send_email_to_provider return self._message( # type: ignore[return-value] - 'sendInvoice', + "sendInvoice", data, timeout=timeout, disable_notification=disable_notification, @@ -3613,28 +3536,28 @@ def answer_shipping_query( # pylint: disable=C0103 if ok and (shipping_options is None or error_message is not None): raise TelegramError( - 'answerShippingQuery: If ok is True, shipping_options ' - 'should not be empty and there should not be error_message' + "answerShippingQuery: If ok is True, shipping_options " + "should not be empty and there should not be error_message" ) if not ok and (shipping_options is not None or error_message is None): raise TelegramError( - 'answerShippingQuery: If ok is False, error_message ' - 'should not be empty and there should not be shipping_options' + "answerShippingQuery: If ok is False, error_message " + "should not be empty and there should not be shipping_options" ) - data: JSONDict = {'shipping_query_id': shipping_query_id, 'ok': ok} + data: JSONDict = {"shipping_query_id": shipping_query_id, "ok": ok} if ok: if not shipping_options: # not using an assert statement directly here since they are removed in # the optimized bytecode raise AssertionError - data['shipping_options'] = [option.to_dict() for option in shipping_options] + data["shipping_options"] = [option.to_dict() for option in shipping_options] if error_message is not None: - data['error_message'] = error_message + data["error_message"] = error_message - result = self._post('answerShippingQuery', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("answerShippingQuery", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3683,17 +3606,17 @@ def answer_pre_checkout_query( # pylint: disable=C0103 if not (ok ^ (error_message is not None)): # pylint: disable=C0325 raise TelegramError( - 'answerPreCheckoutQuery: If ok is True, there should ' - 'not be error_message; if ok is False, error_message ' - 'should not be empty' + "answerPreCheckoutQuery: If ok is True, there should " + "not be error_message; if ok is False, error_message " + "should not be empty" ) - data: JSONDict = {'pre_checkout_query_id': pre_checkout_query_id, 'ok': ok} + data: JSONDict = {"pre_checkout_query_id": pre_checkout_query_id, "ok": ok} if error_message is not None: - data['error_message'] = error_message + data["error_message"] = error_message - result = self._post('answerPreCheckoutQuery', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("answerPreCheckoutQuery", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3742,9 +3665,9 @@ def restrict_chat_member( :class:`telegram.error.TelegramError` """ data: JSONDict = { - 'chat_id': chat_id, - 'user_id': user_id, - 'permissions': permissions.to_dict(), + "chat_id": chat_id, + "user_id": user_id, + "permissions": permissions.to_dict(), } if until_date is not None: @@ -3752,9 +3675,9 @@ def restrict_chat_member( until_date = to_timestamp( until_date, tzinfo=self.defaults.tzinfo if self.defaults else None ) - data['until_date'] = until_date + data["until_date"] = until_date - result = self._post('restrictChatMember', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("restrictChatMember", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3831,32 +3754,32 @@ def promote_chat_member( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id} + data: JSONDict = {"chat_id": chat_id, "user_id": user_id} if is_anonymous is not None: - data['is_anonymous'] = is_anonymous + data["is_anonymous"] = is_anonymous if can_change_info is not None: - data['can_change_info'] = can_change_info + data["can_change_info"] = can_change_info if can_post_messages is not None: - data['can_post_messages'] = can_post_messages + data["can_post_messages"] = can_post_messages if can_edit_messages is not None: - data['can_edit_messages'] = can_edit_messages + data["can_edit_messages"] = can_edit_messages if can_delete_messages is not None: - data['can_delete_messages'] = can_delete_messages + data["can_delete_messages"] = can_delete_messages if can_invite_users is not None: - data['can_invite_users'] = can_invite_users + data["can_invite_users"] = can_invite_users if can_restrict_members is not None: - data['can_restrict_members'] = can_restrict_members + data["can_restrict_members"] = can_restrict_members if can_pin_messages is not None: - data['can_pin_messages'] = can_pin_messages + data["can_pin_messages"] = can_pin_messages if can_promote_members is not None: - data['can_promote_members'] = can_promote_members + data["can_promote_members"] = can_promote_members if can_manage_chat is not None: - data['can_manage_chat'] = can_manage_chat + data["can_manage_chat"] = can_manage_chat if can_manage_voice_chats is not None: - data['can_manage_voice_chats'] = can_manage_voice_chats + data["can_manage_voice_chats"] = can_manage_voice_chats - result = self._post('promoteChatMember', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("promoteChatMember", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3890,9 +3813,9 @@ def set_chat_permissions( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'permissions': permissions.to_dict()} + data: JSONDict = {"chat_id": chat_id, "permissions": permissions.to_dict()} - result = self._post('setChatPermissions', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setChatPermissions", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -3928,10 +3851,10 @@ def set_chat_administrator_custom_title( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'user_id': user_id, 'custom_title': custom_title} + data: JSONDict = {"chat_id": chat_id, "user_id": user_id, "custom_title": custom_title} result = self._post( - 'setChatAdministratorCustomTitle', data, timeout=timeout, api_kwargs=api_kwargs + "setChatAdministratorCustomTitle", data, timeout=timeout, api_kwargs=api_kwargs ) return result # type: ignore[return-value] @@ -3971,9 +3894,9 @@ def export_chat_invite_link( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('exportChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("exportChatInviteLink", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4016,7 +3939,7 @@ def create_chat_invite_link( """ data: JSONDict = { - 'chat_id': chat_id, + "chat_id": chat_id, } if expire_date is not None: @@ -4024,12 +3947,12 @@ def create_chat_invite_link( expire_date = to_timestamp( expire_date, tzinfo=self.defaults.tzinfo if self.defaults else None ) - data['expire_date'] = expire_date + data["expire_date"] = expire_date if member_limit is not None: - data['member_limit'] = member_limit + data["member_limit"] = member_limit - result = self._post('createChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("createChatInviteLink", data, timeout=timeout, api_kwargs=api_kwargs) return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] @@ -4072,19 +3995,19 @@ def edit_chat_invite_link( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'invite_link': invite_link} + data: JSONDict = {"chat_id": chat_id, "invite_link": invite_link} if expire_date is not None: if isinstance(expire_date, datetime): expire_date = to_timestamp( expire_date, tzinfo=self.defaults.tzinfo if self.defaults else None ) - data['expire_date'] = expire_date + data["expire_date"] = expire_date if member_limit is not None: - data['member_limit'] = member_limit + data["member_limit"] = member_limit - result = self._post('editChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("editChatInviteLink", data, timeout=timeout, api_kwargs=api_kwargs) return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] @@ -4120,9 +4043,9 @@ def revoke_chat_invite_link( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'invite_link': invite_link} + data: JSONDict = {"chat_id": chat_id, "invite_link": invite_link} - result = self._post('revokeChatInviteLink', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("revokeChatInviteLink", data, timeout=timeout, api_kwargs=api_kwargs) return ChatInviteLink.de_json(result, self) # type: ignore[return-value, arg-type] @@ -4159,9 +4082,9 @@ def set_chat_photo( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'photo': parse_file_input(photo)} + data: JSONDict = {"chat_id": chat_id, "photo": parse_file_input(photo)} - result = self._post('setChatPhoto', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setChatPhoto", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4193,9 +4116,9 @@ def delete_chat_photo( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} - result = self._post('deleteChatPhoto', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("deleteChatPhoto", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4229,9 +4152,9 @@ def set_chat_title( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'title': title} + data: JSONDict = {"chat_id": chat_id, "title": title} - result = self._post('setChatTitle', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setChatTitle", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4239,7 +4162,7 @@ def set_chat_title( def set_chat_description( self, chat_id: Union[str, int], - description: str, + description: str = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, ) -> bool: @@ -4251,7 +4174,7 @@ def set_chat_description( Args: chat_id (:obj:`int` | :obj:`str`): Unique identifier for the target chat or username of the target channel (in the format ``@channelusername``). - description (:obj:`str`): New chat description, 0-255 characters. + description (:obj:`str`, optional): New chat description, 0-255 characters. 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). @@ -4265,9 +4188,12 @@ def set_chat_description( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'description': description} + data: JSONDict = {"chat_id": chat_id} + + if description is not None: + data["description"] = description - result = self._post('setChatDescription', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setChatDescription", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4307,13 +4233,13 @@ def pin_chat_message( """ data: JSONDict = { - 'chat_id': chat_id, - 'message_id': message_id, - 'disable_notification': disable_notification, + "chat_id": chat_id, + "message_id": message_id, + "disable_notification": disable_notification, } return self._post( # type: ignore[return-value] - 'pinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs + "pinChatMessage", data, timeout=timeout, api_kwargs=api_kwargs ) @log @@ -4348,13 +4274,13 @@ def unpin_chat_message( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} if message_id is not None: - data['message_id'] = message_id + data["message_id"] = message_id return self._post( # type: ignore[return-value] - 'unpinChatMessage', data, timeout=timeout, api_kwargs=api_kwargs + "unpinChatMessage", data, timeout=timeout, api_kwargs=api_kwargs ) @log @@ -4386,10 +4312,10 @@ def unpin_all_chat_messages( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id} + data: JSONDict = {"chat_id": chat_id} return self._post( # type: ignore[return-value] - 'unpinAllChatMessages', data, timeout=timeout, api_kwargs=api_kwargs + "unpinAllChatMessages", data, timeout=timeout, api_kwargs=api_kwargs ) @log @@ -4416,9 +4342,9 @@ def get_sticker_set( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'name': name} + data: JSONDict = {"name": name} - result = self._post('getStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("getStickerSet", data, timeout=timeout, api_kwargs=api_kwargs) return StickerSet.de_json(result, self) # type: ignore[return-value, arg-type] @@ -4461,9 +4387,9 @@ def upload_sticker_file( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'user_id': user_id, 'png_sticker': parse_file_input(png_sticker)} + data: JSONDict = {"user_id": user_id, "png_sticker": parse_file_input(png_sticker)} - result = self._post('uploadStickerFile', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("uploadStickerFile", data, timeout=timeout, api_kwargs=api_kwargs) return File.de_json(result, self) # type: ignore[return-value, arg-type] @@ -4539,20 +4465,20 @@ def create_new_sticker_set( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'user_id': user_id, 'name': name, 'title': title, 'emojis': emojis} + data: JSONDict = {"user_id": user_id, "name": name, "title": title, "emojis": emojis} if png_sticker is not None: - data['png_sticker'] = parse_file_input(png_sticker) + data["png_sticker"] = parse_file_input(png_sticker) if tgs_sticker is not None: - data['tgs_sticker'] = parse_file_input(tgs_sticker) + data["tgs_sticker"] = parse_file_input(tgs_sticker) if contains_masks is not None: - data['contains_masks'] = contains_masks + data["contains_masks"] = contains_masks if mask_position is not None: # We need to_json() instead of to_dict() here, because we're sending a media # message here, which isn't json dumped by utils.request - data['mask_position'] = mask_position.to_json() + data["mask_position"] = mask_position.to_json() - result = self._post('createNewStickerSet', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("createNewStickerSet", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4621,18 +4547,18 @@ def add_sticker_to_set( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'user_id': user_id, 'name': name, 'emojis': emojis} + data: JSONDict = {"user_id": user_id, "name": name, "emojis": emojis} if png_sticker is not None: - data['png_sticker'] = parse_file_input(png_sticker) + data["png_sticker"] = parse_file_input(png_sticker) if tgs_sticker is not None: - data['tgs_sticker'] = parse_file_input(tgs_sticker) + data["tgs_sticker"] = parse_file_input(tgs_sticker) if mask_position is not None: # We need to_json() instead of to_dict() here, because we're sending a media # message here, which isn't json dumped by utils.request - data['mask_position'] = mask_position.to_json() + data["mask_position"] = mask_position.to_json() - result = self._post('addStickerToSet', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("addStickerToSet", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4662,10 +4588,10 @@ def set_sticker_position_in_set( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'sticker': sticker, 'position': position} + data: JSONDict = {"sticker": sticker, "position": position} result = self._post( - 'setStickerPositionInSet', data, timeout=timeout, api_kwargs=api_kwargs + "setStickerPositionInSet", data, timeout=timeout, api_kwargs=api_kwargs ) return result # type: ignore[return-value] @@ -4694,9 +4620,9 @@ def delete_sticker_from_set( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'sticker': sticker} + data: JSONDict = {"sticker": sticker} - result = self._post('deleteStickerFromSet', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("deleteStickerFromSet", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4743,12 +4669,12 @@ def set_sticker_set_thumb( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'name': name, 'user_id': user_id} + data: JSONDict = {"name": name, "user_id": user_id} if thumb is not None: - data['thumb'] = parse_file_input(thumb) + data["thumb"] = parse_file_input(thumb) - result = self._post('setStickerSetThumb', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setStickerSetThumb", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4787,9 +4713,9 @@ def set_passport_data_errors( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'user_id': user_id, 'errors': [error.to_dict() for error in errors]} + data: JSONDict = {"user_id": user_id, "errors": [error.to_dict() for error in errors]} - result = self._post('setPassportDataErrors', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("setPassportDataErrors", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -4814,7 +4740,7 @@ def send_poll( close_date: Union[int, datetime] = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + explanation_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Message: """ Use this method to send a native poll. @@ -4874,37 +4800,37 @@ def send_poll( """ data: JSONDict = { - 'chat_id': chat_id, - 'question': question, - 'options': options, - 'explanation_parse_mode': explanation_parse_mode, + "chat_id": chat_id, + "question": question, + "options": options, + "explanation_parse_mode": explanation_parse_mode, } if not is_anonymous: - data['is_anonymous'] = is_anonymous + data["is_anonymous"] = is_anonymous if type: - data['type'] = type + data["type"] = type if allows_multiple_answers: - data['allows_multiple_answers'] = allows_multiple_answers + data["allows_multiple_answers"] = allows_multiple_answers if correct_option_id is not None: - data['correct_option_id'] = correct_option_id + data["correct_option_id"] = correct_option_id if is_closed: - data['is_closed'] = is_closed + data["is_closed"] = is_closed if explanation: - data['explanation'] = explanation + data["explanation"] = explanation if explanation_entities: - data['explanation_entities'] = [me.to_dict() for me in explanation_entities] + data["explanation_entities"] = [me.to_dict() for me in explanation_entities] if open_period: - data['open_period'] = open_period + data["open_period"] = open_period if close_date: if isinstance(close_date, datetime): close_date = to_timestamp( close_date, tzinfo=self.defaults.tzinfo if self.defaults else None ) - data['close_date'] = close_date + data["close_date"] = close_date return self._message( # type: ignore[return-value] - 'sendPoll', + "sendPoll", data, timeout=timeout, disable_notification=disable_notification, @@ -4946,17 +4872,17 @@ def stop_poll( :class:`telegram.error.TelegramError` """ - data: JSONDict = {'chat_id': chat_id, 'message_id': message_id} + data: JSONDict = {"chat_id": chat_id, "message_id": message_id} if reply_markup: if isinstance(reply_markup, ReplyMarkup): # We need to_json() instead of to_dict() here, because reply_markups may be # attached to media messages, which aren't json dumped by utils.request - data['reply_markup'] = reply_markup.to_json() + data["reply_markup"] = reply_markup.to_json() else: - data['reply_markup'] = reply_markup + data["reply_markup"] = reply_markup - result = self._post('stopPoll', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("stopPoll", data, timeout=timeout, api_kwargs=api_kwargs) return Poll.de_json(result, self) # type: ignore[return-value, arg-type] @@ -5008,14 +4934,14 @@ def send_dice( """ data: JSONDict = { - 'chat_id': chat_id, + "chat_id": chat_id, } if emoji: - data['emoji'] = emoji + data["emoji"] = emoji return self._message( # type: ignore[return-value] - 'sendDice', + "sendDice", data, timeout=timeout, disable_notification=disable_notification, @@ -5064,16 +4990,12 @@ def get_my_commands( data: JSONDict = {} if scope: - data['scope'] = scope.to_dict() + data["scope"] = scope.to_dict() if language_code: - data['language_code'] = language_code + data["language_code"] = language_code - result = self._post('getMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) - - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = BotCommand.de_list(result, self) # type: ignore[assignment,arg-type] - return self._commands # type: ignore[return-value] + result = self._post("getMyCommands", data, timeout=timeout, api_kwargs=api_kwargs) return BotCommand.de_list(result, self) # type: ignore[return-value,arg-type] @@ -5121,20 +5043,15 @@ def set_my_commands( """ cmds = [c if isinstance(c, BotCommand) else BotCommand(c[0], c[1]) for c in commands] - data: JSONDict = {'commands': [c.to_dict() for c in cmds]} + data: JSONDict = {"commands": [c.to_dict() for c in cmds]} if scope: - data['scope'] = scope.to_dict() + data["scope"] = scope.to_dict() if language_code: - data['language_code'] = language_code - - result = self._post('setMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) + data["language_code"] = language_code - # Set commands only for default scope. No need to check for outcome. - # If request failed, we won't come this far - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = cmds + result = self._post("setMyCommands", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -5176,15 +5093,12 @@ def delete_my_commands( data: JSONDict = {} if scope: - data['scope'] = scope.to_dict() + data["scope"] = scope.to_dict() if language_code: - data['language_code'] = language_code - - result = self._post('deleteMyCommands', data, timeout=timeout, api_kwargs=api_kwargs) + data["language_code"] = language_code - if (scope is None or scope.type == scope.DEFAULT) and language_code is None: - self._commands = [] + result = self._post("deleteMyCommands", data, timeout=timeout, api_kwargs=api_kwargs) return result # type: ignore[return-value] @@ -5209,7 +5123,7 @@ def log_out(self, timeout: ODVInput[float] = DEFAULT_NONE) -> bool: :class:`telegram.error.TelegramError` """ - return self._post('logOut', timeout=timeout) # type: ignore[return-value] + return self._post("logOut", timeout=timeout) # type: ignore[return-value] @log def close(self, timeout: ODVInput[float] = DEFAULT_NONE) -> bool: @@ -5231,7 +5145,7 @@ def close(self, timeout: ODVInput[float] = DEFAULT_NONE) -> bool: :class:`telegram.error.TelegramError` """ - return self._post('close', timeout=timeout) # type: ignore[return-value] + return self._post("close", timeout=timeout) # type: ignore[return-value] @log def copy_message( @@ -5241,7 +5155,7 @@ def copy_message( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, @@ -5289,36 +5203,36 @@ def copy_message( :class:`telegram.error.TelegramError` """ data: JSONDict = { - 'chat_id': chat_id, - 'from_chat_id': from_chat_id, - 'message_id': message_id, - 'parse_mode': parse_mode, - 'disable_notification': disable_notification, - 'allow_sending_without_reply': allow_sending_without_reply, + "chat_id": chat_id, + "from_chat_id": from_chat_id, + "message_id": message_id, + "parse_mode": parse_mode, + "disable_notification": disable_notification, + "allow_sending_without_reply": allow_sending_without_reply, } if caption is not None: - data['caption'] = caption + data["caption"] = caption if caption_entities: - data['caption_entities'] = caption_entities + data["caption_entities"] = caption_entities if reply_to_message_id: - data['reply_to_message_id'] = reply_to_message_id + data["reply_to_message_id"] = reply_to_message_id if reply_markup: if isinstance(reply_markup, ReplyMarkup): # We need to_json() instead of to_dict() here, because reply_markups may be # attached to media messages, which aren't json dumped by utils.request - data['reply_markup'] = reply_markup.to_json() + data["reply_markup"] = reply_markup.to_json() else: - data['reply_markup'] = reply_markup + data["reply_markup"] = reply_markup - result = self._post('copyMessage', data, timeout=timeout, api_kwargs=api_kwargs) + result = self._post("copyMessage", data, timeout=timeout, api_kwargs=api_kwargs) 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} + data: JSONDict = {"id": self.id, "username": self.username, "first_name": self.first_name} if self.last_name: - data['last_name'] = self.last_name + data["last_name"] = self.last_name return data @@ -5377,8 +5291,6 @@ def __hash__(self) -> int: """Alias for :meth:`get_file`""" banChatMember = ban_chat_member """Alias for :meth:`ban_chat_member`""" - kickChatMember = kick_chat_member - """Alias for :meth:`kick_chat_member`""" unbanChatMember = unban_chat_member """Alias for :meth:`unban_chat_member`""" answerCallbackQuery = answer_callback_query @@ -5411,8 +5323,6 @@ def __hash__(self) -> int: """Alias for :meth:`delete_chat_sticker_set`""" getChatMemberCount = get_chat_member_count """Alias for :meth:`get_chat_member_count`""" - getChatMembersCount = get_chat_members_count - """Alias for :meth:`get_chat_members_count`""" getWebhookInfo = get_webhook_info """Alias for :meth:`get_webhook_info`""" setGameScore = set_game_score diff --git a/telegram/botcommand.py b/telegram/botcommand.py index 8b36e3e2e86..00188bf765f 100644 --- a/telegram/botcommand.py +++ b/telegram/botcommand.py @@ -41,7 +41,7 @@ class BotCommand(TelegramObject): """ - __slots__ = ('description', '_id_attrs', 'command') + __slots__ = ("description", "command") def __init__(self, command: str, description: str, **_kwargs: Any): self.command = command diff --git a/telegram/botcommandscope.py b/telegram/botcommandscope.py index b4729290bd0..a29d9449f53 100644 --- a/telegram/botcommandscope.py +++ b/telegram/botcommandscope.py @@ -57,7 +57,7 @@ class BotCommandScope(TelegramObject): type (:obj:`str`): Scope type. """ - __slots__ = ('type', '_id_attrs') + __slots__ = ("type",) DEFAULT = constants.BOT_COMMAND_SCOPE_DEFAULT """:const:`telegram.constants.BOT_COMMAND_SCOPE_DEFAULT`""" @@ -79,7 +79,7 @@ def __init__(self, type: str, **_kwargs: Any): self._id_attrs = (self.type,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['BotCommandScope']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["BotCommandScope"]: """Converts JSON data to the appropriate :class:`BotCommandScope` object, i.e. takes care of selecting the correct subclass. @@ -96,7 +96,7 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['BotCommandSc if not data: return None - _class_mapping: Dict[str, Type['BotCommandScope']] = { + _class_mapping: Dict[str, Type["BotCommandScope"]] = { cls.DEFAULT: BotCommandScopeDefault, cls.ALL_PRIVATE_CHATS: BotCommandScopeAllPrivateChats, cls.ALL_GROUP_CHATS: BotCommandScopeAllGroupChats, @@ -107,7 +107,7 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['BotCommandSc } if cls is BotCommandScope: - return _class_mapping.get(data['type'], cls)(**data, bot=bot) + return _class_mapping.get(data["type"], cls)(**data, bot=bot) return cls(**data) @@ -192,12 +192,12 @@ class BotCommandScopeChat(BotCommandScope): target supergroup (in the format ``@supergroupusername``) """ - __slots__ = ('chat_id',) + __slots__ = ("chat_id",) def __init__(self, chat_id: Union[str, int], **_kwargs: Any): super().__init__(type=BotCommandScope.CHAT) self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith('@') else int(chat_id) + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) self._id_attrs = (self.type, self.chat_id) @@ -221,12 +221,12 @@ class BotCommandScopeChatAdministrators(BotCommandScope): target supergroup (in the format ``@supergroupusername``) """ - __slots__ = ('chat_id',) + __slots__ = ("chat_id",) def __init__(self, chat_id: Union[str, int], **_kwargs: Any): super().__init__(type=BotCommandScope.CHAT_ADMINISTRATORS) self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith('@') else int(chat_id) + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) self._id_attrs = (self.type, self.chat_id) @@ -252,12 +252,12 @@ class BotCommandScopeChatMember(BotCommandScope): user_id (:obj:`int`): Unique identifier of the target user. """ - __slots__ = ('chat_id', 'user_id') + __slots__ = ("chat_id", "user_id") def __init__(self, chat_id: Union[str, int], user_id: int, **_kwargs: Any): super().__init__(type=BotCommandScope.CHAT_MEMBER) self.chat_id = ( - chat_id if isinstance(chat_id, str) and chat_id.startswith('@') else int(chat_id) + chat_id if isinstance(chat_id, str) and chat_id.startswith("@") else int(chat_id) ) self.user_id = int(user_id) self._id_attrs = (self.type, self.chat_id, self.user_id) diff --git a/telegram/callbackquery.py b/telegram/callbackquery.py index 47b05b97129..01d1c6cbe23 100644 --- a/telegram/callbackquery.py +++ b/telegram/callbackquery.py @@ -93,15 +93,14 @@ class CallbackQuery(TelegramObject): """ __slots__ = ( - 'bot', - 'game_short_name', - 'message', - 'chat_instance', - 'id', - 'from_user', - 'inline_message_id', - 'data', - '_id_attrs', + "bot", + "game_short_name", + "message", + "chat_instance", + "id", + "from_user", + "inline_message_id", + "data", ) def __init__( @@ -113,7 +112,7 @@ def __init__( data: str = None, inline_message_id: str = None, game_short_name: str = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -131,15 +130,15 @@ def __init__( self._id_attrs = (self.id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['CallbackQuery']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["CallbackQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['from_user'] = User.de_json(data.get('from'), bot) - data['message'] = Message.de_json(data.get('message'), bot) + data["from_user"] = User.de_json(data.get("from"), bot) + data["message"] = Message.de_json(data.get("message"), bot) return cls(bot=bot, **data) @@ -178,10 +177,10 @@ def edit_message_text( text: str, parse_mode: ODVInput[str] = DEFAULT_NONE, disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Union[Message, bool]: """Shortcut for either:: @@ -226,11 +225,11 @@ def edit_message_text( def edit_message_caption( self, caption: str = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, ) -> Union[Message, bool]: """Shortcut for either:: @@ -273,7 +272,7 @@ def edit_message_caption( def edit_message_reply_markup( self, - reply_markup: Optional['InlineKeyboardMarkup'] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, ) -> Union[Message, bool]: @@ -320,8 +319,8 @@ def edit_message_reply_markup( def edit_message_media( self, - media: 'InputMedia' = None, - reply_markup: 'InlineKeyboardMarkup' = None, + media: "InputMedia", + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, ) -> Union[Message, bool]: @@ -338,7 +337,7 @@ def edit_message_media( :meth:`telegram.Bot.edit_message_media` and :meth:`telegram.Message.edit_media`. Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the + :class:`telegram.Message`: On success, if edited message is not an inline message, the edited Message is returned, otherwise :obj:`True` is returned. """ @@ -364,7 +363,7 @@ def edit_message_live_location( latitude: float = None, longitude: float = None, location: Location = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, horizontal_accuracy: float = None, @@ -420,7 +419,7 @@ def edit_message_live_location( def stop_message_live_location( self, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, ) -> Union[Message, bool]: @@ -511,7 +510,7 @@ def get_game_high_scores( user_id: Union[int, str], timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List['GameHighScore']: + ) -> List["GameHighScore"]: """Shortcut for either:: update.callback_query.message.get_game_high_score(*args, **kwargs) @@ -613,14 +612,14 @@ def copy_message( chat_id: Union[int, str], caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, reply_markup: ReplyMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: update.callback_query.message.copy( diff --git a/telegram/chat.py b/telegram/chat.py index 4b5b6c844ff..3fe3025a100 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -18,13 +18,11 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Chat.""" -import warnings from datetime import datetime from typing import TYPE_CHECKING, List, Optional, ClassVar, Union, Tuple, Any from telegram import ChatPhoto, TelegramObject, constants from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput -from telegram.utils.deprecate import TelegramDeprecationWarning from .chatpermissions import ChatPermissions from .chatlocation import ChatLocation @@ -65,6 +63,9 @@ class Chat(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`id` is equal. + .. versionchanged:: 14.0 + Removed the deprecated methods ``kick_member`` and ``get_members_count``. + Args: id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. @@ -146,27 +147,26 @@ class Chat(TelegramObject): """ __slots__ = ( - 'bio', - 'id', - 'type', - 'last_name', - 'bot', - 'sticker_set_name', - 'slow_mode_delay', - 'location', - 'first_name', - 'permissions', - 'invite_link', - 'pinned_message', - 'description', - 'can_set_sticker_set', - 'username', - 'title', - 'photo', - 'linked_chat_id', - 'all_members_are_administrators', - 'message_auto_delete_time', - '_id_attrs', + "bio", + "id", + "type", + "last_name", + "bot", + "sticker_set_name", + "slow_mode_delay", + "location", + "first_name", + "permissions", + "invite_link", + "pinned_message", + "description", + "can_set_sticker_set", + "username", + "title", + "photo", + "linked_chat_id", + "all_members_are_administrators", + "message_auto_delete_time", ) SENDER: ClassVar[str] = constants.CHAT_SENDER @@ -191,11 +191,11 @@ def __init__( username: str = None, first_name: str = None, last_name: str = None, - bot: 'Bot' = None, + bot: "Bot" = None, photo: ChatPhoto = None, description: str = None, invite_link: str = None, - pinned_message: 'Message' = None, + pinned_message: "Message" = None, permissions: ChatPermissions = None, sticker_set_name: str = None, can_set_sticker_set: bool = None, @@ -215,7 +215,7 @@ def __init__( self.first_name = first_name self.last_name = last_name # TODO: Remove (also from tests), when Telegram drops this completely - self.all_members_are_administrators = _kwargs.get('all_members_are_administrators') + self.all_members_are_administrators = _kwargs.get("all_members_are_administrators") self.photo = photo self.bio = bio self.description = description @@ -249,7 +249,7 @@ def full_name(self) -> Optional[str]: if not self.first_name: return None if self.last_name: - return f'{self.first_name} {self.last_name}' + return f"{self.first_name} {self.last_name}" return self.first_name @property @@ -262,19 +262,19 @@ def link(self) -> Optional[str]: return None @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Chat']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Chat"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['photo'] = ChatPhoto.de_json(data.get('photo'), bot) + data["photo"] = ChatPhoto.de_json(data.get("photo"), bot) from telegram import Message # pylint: disable=C0415 - data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) - data['permissions'] = ChatPermissions.de_json(data.get('permissions'), bot) - data['location'] = ChatLocation.de_json(data.get('location'), bot) + data["pinned_message"] = Message.de_json(data.get("pinned_message"), bot) + data["permissions"] = ChatPermissions.de_json(data.get("permissions"), bot) + data["location"] = ChatLocation.de_json(data.get("location"), bot) return cls(bot=bot, **data) @@ -297,7 +297,7 @@ def leave(self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = def get_administrators( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> List['ChatMember']: + ) -> List["ChatMember"]: """Shortcut for:: bot.get_chat_administrators(update.effective_chat.id, *args, **kwargs) @@ -318,25 +318,6 @@ def get_administrators( api_kwargs=api_kwargs, ) - def get_members_count( - self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> int: - """ - Deprecated, use :func:`~telegram.Chat.get_member_count` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`Chat.get_members_count` is deprecated. Use `Chat.get_member_count` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - - return self.get_member_count( - timeout=timeout, - api_kwargs=api_kwargs, - ) - def get_member_count( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None ) -> int: @@ -361,7 +342,7 @@ def get_member( user_id: Union[str, int], timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'ChatMember': + ) -> "ChatMember": """Shortcut for:: bot.get_chat_member(update.effective_chat.id, *args, **kwargs) @@ -379,33 +360,6 @@ def get_member( api_kwargs=api_kwargs, ) - def kick_member( - self, - user_id: Union[str, int], - timeout: ODVInput[float] = DEFAULT_NONE, - until_date: Union[int, datetime] = None, - api_kwargs: JSONDict = None, - revoke_messages: bool = None, - ) -> bool: - """ - Deprecated, use :func:`~telegram.Chat.ban_member` instead. - - .. deprecated:: 13.7 - """ - warnings.warn( - '`Chat.kick_member` is deprecated. Use `Chat.ban_member` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - - return self.ban_member( - user_id=user_id, - timeout=timeout, - until_date=until_date, - api_kwargs=api_kwargs, - revoke_messages=revoke_messages, - ) - def ban_member( self, user_id: Union[str, int], @@ -671,12 +625,12 @@ def send_message( disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> 'Message': + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> "Message": """Shortcut for:: bot.send_message(update.effective_chat.id, *args, **kwargs) @@ -704,14 +658,14 @@ def send_message( def send_media_group( self, media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] ], disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> List['Message']: + ) -> List["Message"]: """Shortcut for:: bot.send_media_group(update.effective_chat.id, *args, **kwargs) @@ -760,18 +714,18 @@ def send_chat_action( def send_photo( self, - photo: Union[FileInput, 'PhotoSize'], + photo: Union[FileInput, "PhotoSize"], caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_photo(update.effective_chat.id, *args, **kwargs) @@ -804,13 +758,13 @@ def send_contact( last_name: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, - contact: 'Contact' = None, + contact: "Contact" = None, vcard: str = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_contact(update.effective_chat.id, *args, **kwargs) @@ -838,22 +792,22 @@ def send_contact( def send_audio( self, - audio: Union[FileInput, 'Audio'], + audio: Union[FileInput, "Audio"], duration: int = None, performer: str = None, title: str = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_audio(update.effective_chat.id, *args, **kwargs) @@ -885,20 +839,20 @@ def send_audio( def send_document( self, - document: Union[FileInput, 'Document'], + document: Union[FileInput, "Document"], filename: str = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb: FileInput = None, api_kwargs: JSONDict = None, disable_content_type_detection: bool = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> 'Message': + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> "Message": """Shortcut for:: bot.send_document(update.effective_chat.id, *args, **kwargs) @@ -930,12 +884,12 @@ def send_dice( self, disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, emoji: str = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_dice(update.effective_chat.id, *args, **kwargs) @@ -962,11 +916,11 @@ def send_game( game_short_name: str, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_game(update.effective_chat.id, *args, **kwargs) @@ -995,7 +949,7 @@ def send_invoice( payload: str, provider_token: str, currency: str, - prices: List['LabeledPrice'], + prices: List["LabeledPrice"], start_parameter: str = None, photo_url: str = None, photo_size: int = None, @@ -1008,7 +962,7 @@ def send_invoice( is_flexible: bool = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, provider_data: Union[str, object] = None, send_phone_number_to_provider: bool = None, send_email_to_provider: bool = None, @@ -1017,7 +971,7 @@ def send_invoice( allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, max_tip_amount: int = None, suggested_tip_amounts: List[int] = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_invoice(update.effective_chat.id, *args, **kwargs) @@ -1073,16 +1027,16 @@ def send_location( longitude: float = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, - location: 'Location' = None, + location: "Location" = None, live_period: int = None, api_kwargs: JSONDict = None, horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_location(update.effective_chat.id, *args, **kwargs) @@ -1112,7 +1066,7 @@ def send_location( def send_animation( self, - animation: Union[FileInput, 'Animation'], + animation: Union[FileInput, "Animation"], duration: int = None, width: int = None, height: int = None, @@ -1121,13 +1075,13 @@ def send_animation( parse_mode: ODVInput[str] = DEFAULT_NONE, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_animation(update.effective_chat.id, *args, **kwargs) @@ -1159,14 +1113,14 @@ def send_animation( def send_sticker( self, - sticker: Union[FileInput, 'Sticker'], + sticker: Union[FileInput, "Sticker"], disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_sticker(update.effective_chat.id, *args, **kwargs) @@ -1197,15 +1151,15 @@ def send_venue( foursquare_id: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, - venue: 'Venue' = None, + venue: "Venue" = None, foursquare_type: str = None, api_kwargs: JSONDict = None, google_place_id: str = None, google_place_type: str = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_venue(update.effective_chat.id, *args, **kwargs) @@ -1237,12 +1191,12 @@ def send_venue( def send_video( self, - video: Union[FileInput, 'Video'], + video: Union[FileInput, "Video"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, width: int = None, height: int = None, @@ -1251,9 +1205,9 @@ def send_video( thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_video(update.effective_chat.id, *args, **kwargs) @@ -1286,18 +1240,18 @@ def send_video( def send_video_note( self, - video_note: Union[FileInput, 'VideoNote'], + video_note: Union[FileInput, "VideoNote"], duration: int = None, length: int = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_video_note(update.effective_chat.id, *args, **kwargs) @@ -1325,19 +1279,19 @@ def send_video_note( def send_voice( self, - voice: Union[FileInput, 'Voice'], + voice: Union[FileInput, "Voice"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_voice(update.effective_chat.id, *args, **kwargs) @@ -1376,7 +1330,7 @@ def send_poll( is_closed: bool = None, disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, explanation: str = None, explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, @@ -1384,8 +1338,8 @@ def send_poll( close_date: Union[int, datetime] = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> 'Message': + explanation_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> "Message": """Shortcut for:: bot.send_poll(update.effective_chat.id, *args, **kwargs) @@ -1424,14 +1378,14 @@ def send_copy( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: bot.copy_message(chat_id=update.effective_chat.id, *args, **kwargs) @@ -1463,14 +1417,14 @@ def copy_message( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: bot.copy_message(from_chat_id=update.effective_chat.id, *args, **kwargs) @@ -1524,7 +1478,7 @@ def create_invite_link( member_limit: int = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'ChatInviteLink': + ) -> "ChatInviteLink": """Shortcut for:: bot.create_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) @@ -1553,7 +1507,7 @@ def edit_invite_link( member_limit: int = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'ChatInviteLink': + ) -> "ChatInviteLink": """Shortcut for:: bot.edit_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) @@ -1581,7 +1535,7 @@ def revoke_invite_link( invite_link: str, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'ChatInviteLink': + ) -> "ChatInviteLink": """Shortcut for:: bot.revoke_chat_invite_link(chat_id=update.effective_chat.id, *args, **kwargs) diff --git a/telegram/chataction.py b/telegram/chataction.py index c737b810fbc..18b2600fd24 100644 --- a/telegram/chataction.py +++ b/telegram/chataction.py @@ -20,21 +20,18 @@ """This module contains an object that represents a Telegram ChatAction.""" from typing import ClassVar from telegram import constants -from telegram.utils.deprecate import set_new_attribute_deprecated class ChatAction: - """Helper class to provide constants for different chat actions.""" + """Helper class to provide constants for different chat actions. - __slots__ = ('__dict__',) # Adding __dict__ here since it doesn't subclass TGObject + .. versionchanged:: 14.0 + Removed the deprecated constants ``RECORD_AUDIO`` and ``UPLOAD_AUDIO``. + """ + + __slots__ = () FIND_LOCATION: ClassVar[str] = constants.CHATACTION_FIND_LOCATION """:const:`telegram.constants.CHATACTION_FIND_LOCATION`""" - RECORD_AUDIO: ClassVar[str] = constants.CHATACTION_RECORD_AUDIO - """:const:`telegram.constants.CHATACTION_RECORD_AUDIO` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :attr:`RECORD_VOICE` instead. - """ RECORD_VOICE: ClassVar[str] = constants.CHATACTION_RECORD_VOICE """:const:`telegram.constants.CHATACTION_RECORD_VOICE` @@ -46,12 +43,6 @@ class ChatAction: """:const:`telegram.constants.CHATACTION_RECORD_VIDEO_NOTE`""" TYPING: ClassVar[str] = constants.CHATACTION_TYPING """:const:`telegram.constants.CHATACTION_TYPING`""" - UPLOAD_AUDIO: ClassVar[str] = constants.CHATACTION_UPLOAD_AUDIO - """:const:`telegram.constants.CHATACTION_UPLOAD_AUDIO` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :attr:`UPLOAD_VOICE` instead. - """ UPLOAD_VOICE: ClassVar[str] = constants.CHATACTION_UPLOAD_VOICE """:const:`telegram.constants.CHATACTION_UPLOAD_VOICE` @@ -65,6 +56,3 @@ class ChatAction: """:const:`telegram.constants.CHATACTION_UPLOAD_VIDEO`""" UPLOAD_VIDEO_NOTE: ClassVar[str] = constants.CHATACTION_UPLOAD_VIDEO_NOTE """:const:`telegram.constants.CHATACTION_UPLOAD_VIDEO_NOTE`""" - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) diff --git a/telegram/chatinvitelink.py b/telegram/chatinvitelink.py index 0755853b007..5164630b5a7 100644 --- a/telegram/chatinvitelink.py +++ b/telegram/chatinvitelink.py @@ -61,13 +61,12 @@ class ChatInviteLink(TelegramObject): """ __slots__ = ( - 'invite_link', - 'creator', - 'is_primary', - 'is_revoked', - 'expire_date', - 'member_limit', - '_id_attrs', + "invite_link", + "creator", + "is_primary", + "is_revoked", + "expire_date", + "member_limit", ) def __init__( @@ -93,15 +92,15 @@ def __init__( self._id_attrs = (self.invite_link, self.creator, self.is_primary, self.is_revoked) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatInviteLink']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatInviteLink"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['creator'] = User.de_json(data.get('creator'), bot) - data['expire_date'] = from_timestamp(data.get('expire_date', None)) + data["creator"] = User.de_json(data.get("creator"), bot) + data["expire_date"] = from_timestamp(data.get("expire_date", None)) return cls(**data) @@ -109,6 +108,6 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - data['expire_date'] = to_timestamp(self.expire_date) + data["expire_date"] = to_timestamp(self.expire_date) return data diff --git a/telegram/chatlocation.py b/telegram/chatlocation.py index dcdbb6f0024..2df19986170 100644 --- a/telegram/chatlocation.py +++ b/telegram/chatlocation.py @@ -47,7 +47,7 @@ class ChatLocation(TelegramObject): """ - __slots__ = ('location', '_id_attrs', 'address') + __slots__ = ("location", "address") def __init__( self, @@ -61,13 +61,13 @@ def __init__( self._id_attrs = (self.location,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatLocation']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatLocation"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['location'] = Location.de_json(data.get('location'), bot) + data["location"] = Location.de_json(data.get("location"), bot) return cls(bot=bot, **data) diff --git a/telegram/chatmember.py b/telegram/chatmember.py index 254836bd0e1..2eac28cb8a3 100644 --- a/telegram/chatmember.py +++ b/telegram/chatmember.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatMember.""" import datetime -from typing import TYPE_CHECKING, Any, Optional, ClassVar, Dict, Type +from typing import TYPE_CHECKING, Optional, ClassVar, Dict, Type from telegram import TelegramObject, User, constants from telegram.utils.helpers import from_timestamp, to_timestamp @@ -42,10 +42,10 @@ class ChatMember(TelegramObject): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`user` and :attr:`status` are equal. - Note: - As of Bot API 5.3, :class:`ChatMember` is nothing but the base class for the subclasses + .. versionchanged:: 14.0 + As of Bot API 5.3, :class:`ChatMember` is nothing but the base class for the subclasses listed above and is no longer returned directly by :meth:`~telegram.Bot.get_chat`. - Therefore, most of the arguments and attributes were deprecated and you should no longer + Therefore, most of the arguments and attributes were removed and you should no longer use :class:`ChatMember` directly. Args: @@ -54,241 +54,14 @@ class ChatMember(TelegramObject): :attr:`~telegram.ChatMember.ADMINISTRATOR`, :attr:`~telegram.ChatMember.CREATOR`, :attr:`~telegram.ChatMember.KICKED`, :attr:`~telegram.ChatMember.LEFT`, :attr:`~telegram.ChatMember.MEMBER` or :attr:`~telegram.ChatMember.RESTRICTED`. - custom_title (:obj:`str`, optional): Owner and administrators only. - Custom title for this user. - - .. deprecated:: 13.7 - - is_anonymous (:obj:`bool`, optional): Owner and administrators only. :obj:`True`, if the - user's presence in the chat is hidden. - - .. deprecated:: 13.7 - - until_date (:class:`datetime.datetime`, optional): Restricted and kicked only. Date when - restrictions will be lifted for this user. - - .. deprecated:: 13.7 - - can_be_edited (:obj:`bool`, optional): Administrators only. :obj:`True`, if the bot is - allowed to edit administrator privileges of that user. - - .. deprecated:: 13.7 - - can_manage_chat (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can access the chat event log, chat statistics, message statistics in - channels, see channel members, see anonymous administrators in supergroups and ignore - slow mode. Implied by any other administrator privilege. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_manage_voice_chats (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can manage voice chats. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_change_info (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can change the chat title, photo and other settings. - - .. deprecated:: 13.7 - - can_post_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can post in the channel, channels only. - - .. deprecated:: 13.7 - - can_edit_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can edit messages of other users and can pin messages; channels only. - - .. deprecated:: 13.7 - - can_delete_messages (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can delete messages of other users. - - .. deprecated:: 13.7 - - can_invite_users (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can invite new users to the chat. - - .. deprecated:: 13.7 - - can_restrict_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can restrict, ban or unban chat members. - - .. deprecated:: 13.7 - - can_pin_messages (:obj:`bool`, optional): Administrators and restricted only. :obj:`True`, - if the user can pin messages, groups and supergroups only. - - .. deprecated:: 13.7 - - can_promote_members (:obj:`bool`, optional): Administrators only. :obj:`True`, if the - administrator can add new administrators with a subset of his own privileges or demote - administrators that he has promoted, directly or indirectly (promoted by administrators - that were appointed by the user). - - .. deprecated:: 13.7 - - is_member (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is a member of - the chat at the moment of the request. - - .. deprecated:: 13.7 - - can_send_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user can - send text messages, contacts, locations and venues. - - .. deprecated:: 13.7 - - can_send_media_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user - can send audios, documents, photos, videos, video notes and voice notes. - - .. deprecated:: 13.7 - - can_send_polls (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user is - allowed to send polls. - - .. deprecated:: 13.7 - - can_send_other_messages (:obj:`bool`, optional): Restricted only. :obj:`True`, if the user - can send animations, games, stickers and use inline bots. - - .. deprecated:: 13.7 - - can_add_web_page_previews (:obj:`bool`, optional): Restricted only. :obj:`True`, if user - may add web page previews to his messages. - - .. deprecated:: 13.7 Attributes: user (:class:`telegram.User`): Information about the user. status (:obj:`str`): The member's status in the chat. - custom_title (:obj:`str`): Optional. Custom title for owner and administrators. - - .. deprecated:: 13.7 - - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's presence in the chat is - hidden. - - .. deprecated:: 13.7 - - until_date (:class:`datetime.datetime`): Optional. Date when restrictions will be lifted - for this user. - - .. deprecated:: 13.7 - - can_be_edited (:obj:`bool`): Optional. If the bot is allowed to edit administrator - privileges of that user. - - .. deprecated:: 13.7 - - can_manage_chat (:obj:`bool`): Optional. If the administrator can access the chat event - log, chat statistics, message statistics in channels, see channel members, see - anonymous administrators in supergroups and ignore slow mode. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_manage_voice_chats (:obj:`bool`): Optional. if the administrator can manage - voice chats. - - .. versionadded:: 13.4 - .. deprecated:: 13.7 - - can_change_info (:obj:`bool`): Optional. If the user can change the chat title, photo and - other settings. - - .. deprecated:: 13.7 - - can_post_messages (:obj:`bool`): Optional. If the administrator can post in the channel. - - .. deprecated:: 13.7 - - can_edit_messages (:obj:`bool`): Optional. If the administrator can edit messages of other - users. - - .. deprecated:: 13.7 - - can_delete_messages (:obj:`bool`): Optional. If the administrator can delete messages of - other users. - - .. deprecated:: 13.7 - - can_invite_users (:obj:`bool`): Optional. If the user can invite new users to the chat. - - .. deprecated:: 13.7 - - can_restrict_members (:obj:`bool`): Optional. If the administrator can restrict, ban or - unban chat members. - - .. deprecated:: 13.7 - - can_pin_messages (:obj:`bool`): Optional. If the user can pin messages. - - .. deprecated:: 13.7 - - can_promote_members (:obj:`bool`): Optional. If the administrator can add new - administrators. - - .. deprecated:: 13.7 - - is_member (:obj:`bool`): Optional. Restricted only. :obj:`True`, if the user is a member of - the chat at the moment of the request. - - .. deprecated:: 13.7 - - can_send_messages (:obj:`bool`): Optional. If the user can send text messages, contacts, - locations and venues. - - .. deprecated:: 13.7 - - can_send_media_messages (:obj:`bool`): Optional. If the user can send media messages, - implies can_send_messages. - - .. deprecated:: 13.7 - - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to - send polls. - - .. deprecated:: 13.7 - - can_send_other_messages (:obj:`bool`): Optional. If the user can send animations, games, - stickers and use inline bots, implies can_send_media_messages. - - .. deprecated:: 13.7 - - can_add_web_page_previews (:obj:`bool`): Optional. If user may add web page previews to his - messages, implies can_send_media_messages - - .. deprecated:: 13.7 """ - __slots__ = ( - 'is_member', - 'can_restrict_members', - 'can_delete_messages', - 'custom_title', - 'can_be_edited', - 'can_post_messages', - 'can_send_messages', - 'can_edit_messages', - 'can_send_media_messages', - 'is_anonymous', - 'can_add_web_page_previews', - 'can_send_other_messages', - 'can_invite_users', - 'can_send_polls', - 'user', - 'can_promote_members', - 'status', - 'can_change_info', - 'can_pin_messages', - 'can_manage_chat', - 'can_manage_voice_chats', - 'until_date', - '_id_attrs', - ) + __slots__ = ("user", "status") ADMINISTRATOR: ClassVar[str] = constants.CHATMEMBER_ADMINISTRATOR """:const:`telegram.constants.CHATMEMBER_ADMINISTRATOR`""" @@ -303,72 +76,25 @@ class ChatMember(TelegramObject): RESTRICTED: ClassVar[str] = constants.CHATMEMBER_RESTRICTED """:const:`telegram.constants.CHATMEMBER_RESTRICTED`""" - def __init__( - self, - user: User, - status: str, - until_date: datetime.datetime = None, - can_be_edited: bool = None, - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - is_member: bool = None, - custom_title: str = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, - can_manage_voice_chats: bool = None, - **_kwargs: Any, - ): - # Required + def __init__(self, user: User, status: str, **_kwargs: object): + # Required by all subclasses self.user = user self.status = status - # Optionals - self.custom_title = custom_title - self.is_anonymous = is_anonymous - self.until_date = until_date - self.can_be_edited = can_be_edited - self.can_change_info = can_change_info - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_invite_users = can_invite_users - self.can_restrict_members = can_restrict_members - self.can_pin_messages = can_pin_messages - self.can_promote_members = can_promote_members - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_polls = can_send_polls - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews - self.is_member = is_member - self.can_manage_chat = can_manage_chat - self.can_manage_voice_chats = can_manage_voice_chats - self._id_attrs = (self.user, self.status) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMember']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMember"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['user'] = User.de_json(data.get('user'), bot) - data['until_date'] = from_timestamp(data.get('until_date', None)) + data["user"] = User.de_json(data.get("user"), bot) + data["until_date"] = from_timestamp(data.get("until_date", None)) - _class_mapping: Dict[str, Type['ChatMember']] = { + _class_mapping: Dict[str, Type["ChatMember"]] = { cls.CREATOR: ChatMemberOwner, cls.ADMINISTRATOR: ChatMemberAdministrator, cls.MEMBER: ChatMemberMember, @@ -378,14 +104,15 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMember'] } if cls is ChatMember: - return _class_mapping.get(data['status'], cls)(**data, bot=bot) + return _class_mapping.get(data["status"], cls)(**data, bot=bot) 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) + if data.get("until_date", False): + data["until_date"] = to_timestamp(data["until_date"]) return data @@ -399,35 +126,32 @@ class ChatMemberOwner(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - custom_title (:obj:`str`, optional): Custom title for this user. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. + custom_title (:obj:`str`, optional): Custom title for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.CREATOR`. user (:class:`telegram.User`): Information about the user. + is_anonymous (:obj:`bool`): :obj:`True`, if the user's + presence in the chat is hidden. custom_title (:obj:`str`): Optional. Custom title for this user. - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's - presence in the chat is hidden. """ - __slots__ = () + __slots__ = ("is_anonymous", "custom_title") def __init__( self, user: User, + is_anonymous: bool, custom_title: str = None, - is_anonymous: bool = None, - **_kwargs: Any, + **_kwargs: object, ): - super().__init__( - status=ChatMember.CREATOR, - user=user, - custom_title=custom_title, - is_anonymous=is_anonymous, - ) + super().__init__(status=ChatMember.CREATOR, user=user) + self.is_anonymous = is_anonymous + self.custom_title = custom_title class ChatMemberAdministrator(ChatMember): @@ -438,110 +162,121 @@ class ChatMemberAdministrator(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - can_be_edited (:obj:`bool`, optional): :obj:`True`, if the bot + can_be_edited (:obj:`bool`): :obj:`True`, if the bot is allowed to edit administrator privileges of that user. - custom_title (:obj:`str`, optional): Custom title for this user. - is_anonymous (:obj:`bool`, optional): :obj:`True`, if the user's + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. - can_manage_chat (:obj:`bool`, optional): :obj:`True`, if the administrator + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege. - can_post_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can post in the channel, channels only. - can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the - administrator can edit messages of other users and can pin - messages; channels only. - can_delete_messages (:obj:`bool`, optional): :obj:`True`, if the + can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of other users. - can_manage_voice_chats (:obj:`bool`, optional): :obj:`True`, if the + can_manage_voice_chats (:obj:`bool`): :obj:`True`, if the administrator can manage voice chats. - can_restrict_members (:obj:`bool`, optional): :obj:`True`, if the + can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or unban chat members. - can_promote_members (:obj:`bool`, optional): :obj:`True`, if the administrator + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user). - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. + can_post_messages (:obj:`bool`, optional): :obj:`True`, if the + administrator can post in the channel, channels only. + can_edit_messages (:obj:`bool`, optional): :obj:`True`, if the + administrator can edit messages of other users and can pin + messages; channels only. can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. + custom_title (:obj:`str`, optional): Custom title for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.ADMINISTRATOR`. user (:class:`telegram.User`): Information about the user. - can_be_edited (:obj:`bool`): Optional. :obj:`True`, if the bot + can_be_edited (:obj:`bool`): :obj:`True`, if the bot is allowed to edit administrator privileges of that user. - custom_title (:obj:`str`): Optional. Custom title for this user. - is_anonymous (:obj:`bool`): Optional. :obj:`True`, if the user's + is_anonymous (:obj:`bool`): :obj:`True`, if the user's presence in the chat is hidden. - can_manage_chat (:obj:`bool`): Optional. :obj:`True`, if the administrator + can_manage_chat (:obj:`bool`): :obj:`True`, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege. - can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can post in the channel, channels only. - can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the - administrator can edit messages of other users and can pin - messages; channels only. - can_delete_messages (:obj:`bool`): Optional. :obj:`True`, if the + can_delete_messages (:obj:`bool`): :obj:`True`, if the administrator can delete messages of other users. - can_manage_voice_chats (:obj:`bool`): Optional. :obj:`True`, if the + can_manage_voice_chats (:obj:`bool`): :obj:`True`, if the administrator can manage voice chats. - can_restrict_members (:obj:`bool`): Optional. :obj:`True`, if the + can_restrict_members (:obj:`bool`): :obj:`True`, if the administrator can restrict, ban or unban chat members. - can_promote_members (:obj:`bool`): Optional. :obj:`True`, if the administrator + can_promote_members (:obj:`bool`): :obj:`True`, if the administrator can add new administrators with a subset of his own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user). - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. + can_post_messages (:obj:`bool`): Optional. :obj:`True`, if the + administrator can post in the channel, channels only. + can_edit_messages (:obj:`bool`): Optional. :obj:`True`, if the + administrator can edit messages of other users and can pin + messages; channels only. can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. + custom_title (:obj:`str`): Optional. Custom title for this user. """ - __slots__ = () + __slots__ = ( + "can_be_edited", + "is_anonymous", + "can_manage_chat", + "can_delete_messages", + "can_manage_voice_chats", + "can_restrict_members", + "can_promote_members", + "can_change_info", + "can_invite_users", + "can_post_messages", + "can_edit_messages", + "can_pin_messages", + "custom_title", + ) def __init__( self, user: User, - can_be_edited: bool = None, - custom_title: str = None, - is_anonymous: bool = None, - can_manage_chat: bool = None, + can_be_edited: bool, + is_anonymous: bool, + can_manage_chat: bool, + can_delete_messages: bool, + can_manage_voice_chats: bool, + can_restrict_members: bool, + can_promote_members: bool, + can_change_info: bool, + can_invite_users: bool, can_post_messages: bool = None, can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_manage_voice_chats: bool = None, - can_restrict_members: bool = None, - can_promote_members: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, can_pin_messages: bool = None, - **_kwargs: Any, + custom_title: str = None, + **_kwargs: object, ): - super().__init__( - status=ChatMember.ADMINISTRATOR, - user=user, - can_be_edited=can_be_edited, - custom_title=custom_title, - is_anonymous=is_anonymous, - can_manage_chat=can_manage_chat, - can_post_messages=can_post_messages, - can_edit_messages=can_edit_messages, - can_delete_messages=can_delete_messages, - can_manage_voice_chats=can_manage_voice_chats, - can_restrict_members=can_restrict_members, - can_promote_members=can_promote_members, - can_change_info=can_change_info, - can_invite_users=can_invite_users, - can_pin_messages=can_pin_messages, - ) + super().__init__(status=ChatMember.ADMINISTRATOR, user=user) + self.can_be_edited = can_be_edited + self.is_anonymous = is_anonymous + self.can_manage_chat = can_manage_chat + self.can_delete_messages = can_delete_messages + self.can_manage_voice_chats = can_manage_voice_chats + self.can_restrict_members = can_restrict_members + self.can_promote_members = can_promote_members + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_post_messages = can_post_messages + self.can_edit_messages = can_edit_messages + self.can_pin_messages = can_pin_messages + self.custom_title = custom_title class ChatMemberMember(ChatMember): @@ -563,7 +298,7 @@ class ChatMemberMember(ChatMember): __slots__ = () - def __init__(self, user: User, **_kwargs: Any): + def __init__(self, user: User, **_kwargs: object): super().__init__(status=ChatMember.MEMBER, user=user) @@ -576,85 +311,93 @@ class ChatMemberRestricted(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - is_member (:obj:`bool`, optional): :obj:`True`, if the user is a + is_member (:obj:`bool`): :obj:`True`, if the user is a member of the chat at the moment of the request. - can_change_info (:obj:`bool`, optional): :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`, optional): :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. - can_pin_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. - can_send_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send text messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_media_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes. - can_send_polls (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed to send polls. - can_send_other_messages (:obj:`bool`, optional): :obj:`True`, if the user is allowed + can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send animations, games, stickers and use inline bots. - can_add_web_page_previews (:obj:`bool`, optional): :obj:`True`, if the user is + can_add_web_page_previews (:obj:`bool`): :obj:`True`, if the user is allowed to add web page previews to their messages. - until_date (:class:`datetime.datetime`, optional): Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.RESTRICTED`. user (:class:`telegram.User`): Information about the user. - is_member (:obj:`bool`): Optional. :obj:`True`, if the user is a + is_member (:obj:`bool`): :obj:`True`, if the user is a member of the chat at the moment of the request. - can_change_info (:obj:`bool`): Optional. :obj:`True`, if the user can change + can_change_info (:obj:`bool`): :obj:`True`, if the user can change the chat title, photo and other settings. - can_invite_users (:obj:`bool`): Optional. :obj:`True`, if the user can invite + can_invite_users (:obj:`bool`): :obj:`True`, if the user can invite new users to the chat. - can_pin_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_pin_messages (:obj:`bool`): :obj:`True`, if the user is allowed to pin messages; groups and supergroups only. - can_send_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send text messages, contacts, locations and venues. - can_send_media_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_media_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes. - can_send_polls (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_polls (:obj:`bool`): :obj:`True`, if the user is allowed to send polls. - can_send_other_messages (:obj:`bool`): Optional. :obj:`True`, if the user is allowed + can_send_other_messages (:obj:`bool`): :obj:`True`, if the user is allowed to send animations, games, stickers and use inline bots. - can_add_web_page_previews (:obj:`bool`): Optional. :obj:`True`, if the user is + can_add_web_page_previews (:obj:`bool`): :obj:`True`, if the user is allowed to add web page previews to their messages. - until_date (:class:`datetime.datetime`): Optional. Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. """ - __slots__ = () + __slots__ = ( + "is_member", + "can_change_info", + "can_invite_users", + "can_pin_messages", + "can_send_messages", + "can_send_media_messages", + "can_send_polls", + "can_send_other_messages", + "can_add_web_page_previews", + "until_date", + ) def __init__( self, user: User, - is_member: bool = None, - can_change_info: bool = None, - can_invite_users: bool = None, - can_pin_messages: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_polls: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None, - until_date: datetime.datetime = None, - **_kwargs: Any, + is_member: bool, + can_change_info: bool, + can_invite_users: bool, + can_pin_messages: bool, + can_send_messages: bool, + can_send_media_messages: bool, + can_send_polls: bool, + can_send_other_messages: bool, + can_add_web_page_previews: bool, + until_date: datetime.datetime, + **_kwargs: object, ): - super().__init__( - status=ChatMember.RESTRICTED, - user=user, - is_member=is_member, - can_change_info=can_change_info, - can_invite_users=can_invite_users, - can_pin_messages=can_pin_messages, - can_send_messages=can_send_messages, - can_send_media_messages=can_send_media_messages, - can_send_polls=can_send_polls, - can_send_other_messages=can_send_other_messages, - can_add_web_page_previews=can_add_web_page_previews, - until_date=until_date, - ) + super().__init__(status=ChatMember.RESTRICTED, user=user) + self.is_member = is_member + self.can_change_info = can_change_info + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_polls = can_send_polls + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews + self.until_date = until_date class ChatMemberLeft(ChatMember): @@ -675,7 +418,7 @@ class ChatMemberLeft(ChatMember): __slots__ = () - def __init__(self, user: User, **_kwargs: Any): + def __init__(self, user: User, **_kwargs: object): super().__init__(status=ChatMember.LEFT, user=user) @@ -688,28 +431,20 @@ class ChatMemberBanned(ChatMember): Args: user (:class:`telegram.User`): Information about the user. - until_date (:class:`datetime.datetime`, optional): Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. Attributes: status (:obj:`str`): The member's status in the chat, always :attr:`telegram.ChatMember.KICKED`. user (:class:`telegram.User`): Information about the user. - until_date (:class:`datetime.datetime`): Optional. Date when restrictions + until_date (:class:`datetime.datetime`): Date when restrictions will be lifted for this user. """ - __slots__ = () + __slots__ = ("until_date",) - def __init__( - self, - user: User, - until_date: datetime.datetime = None, - **_kwargs: Any, - ): - super().__init__( - status=ChatMember.KICKED, - user=user, - until_date=until_date, - ) + def __init__(self, user: User, until_date: datetime.datetime, **_kwargs: object): + super().__init__(status=ChatMember.KICKED, user=user) + self.until_date = until_date diff --git a/telegram/chatmemberupdated.py b/telegram/chatmemberupdated.py index 4d49a6c7eca..bc33f720aa8 100644 --- a/telegram/chatmemberupdated.py +++ b/telegram/chatmemberupdated.py @@ -63,13 +63,12 @@ class ChatMemberUpdated(TelegramObject): """ __slots__ = ( - 'chat', - 'from_user', - 'date', - 'old_chat_member', - 'new_chat_member', - 'invite_link', - '_id_attrs', + "chat", + "from_user", + "date", + "old_chat_member", + "new_chat_member", + "invite_link", ) def __init__( @@ -101,19 +100,19 @@ def __init__( ) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMemberUpdated']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChatMemberUpdated"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['chat'] = Chat.de_json(data.get('chat'), bot) - data['from_user'] = User.de_json(data.get('from'), bot) - data['date'] = from_timestamp(data.get('date')) - data['old_chat_member'] = ChatMember.de_json(data.get('old_chat_member'), bot) - data['new_chat_member'] = ChatMember.de_json(data.get('new_chat_member'), bot) - data['invite_link'] = ChatInviteLink.de_json(data.get('invite_link'), bot) + data["chat"] = Chat.de_json(data.get("chat"), bot) + data["from_user"] = User.de_json(data.get("from"), bot) + data["date"] = from_timestamp(data.get("date")) + data["old_chat_member"] = ChatMember.de_json(data.get("old_chat_member"), bot) + data["new_chat_member"] = ChatMember.de_json(data.get("new_chat_member"), bot) + data["invite_link"] = ChatInviteLink.de_json(data.get("invite_link"), bot) return cls(**data) @@ -122,7 +121,7 @@ def to_dict(self) -> JSONDict: data = super().to_dict() # Required - data['date'] = to_timestamp(self.date) + data["date"] = to_timestamp(self.date) return data @@ -155,9 +154,9 @@ def difference( # we first get the names of the attributes that have changed # user.to_dict() is unhashable, so that needs some special casing further down old_dict = self.old_chat_member.to_dict() - old_user_dict = old_dict.pop('user') + old_user_dict = old_dict.pop("user") new_dict = self.new_chat_member.to_dict() - new_user_dict = new_dict.pop('user') + new_user_dict = new_dict.pop("user") # Generator for speed: we only need to iterate over it once # we can't directly use the values from old_dict ^ new_dict b/c that set is unordered @@ -168,6 +167,6 @@ def difference( for attribute in attributes } if old_user_dict != new_user_dict: - result['user'] = (self.old_chat_member.user, self.new_chat_member.user) + result["user"] = (self.old_chat_member.user, self.new_chat_member.user) return result # type: ignore[return-value] diff --git a/telegram/chatpermissions.py b/telegram/chatpermissions.py index 0b5a7b956bb..006ac9d77ea 100644 --- a/telegram/chatpermissions.py +++ b/telegram/chatpermissions.py @@ -79,15 +79,14 @@ class ChatPermissions(TelegramObject): """ __slots__ = ( - 'can_send_other_messages', - 'can_invite_users', - 'can_send_polls', - '_id_attrs', - 'can_send_messages', - 'can_send_media_messages', - 'can_change_info', - 'can_pin_messages', - 'can_add_web_page_previews', + "can_send_other_messages", + "can_invite_users", + "can_send_polls", + "can_send_messages", + "can_send_media_messages", + "can_change_info", + "can_pin_messages", + "can_add_web_page_previews", ) def __init__( diff --git a/telegram/choseninlineresult.py b/telegram/choseninlineresult.py index 384d57e638e..5932c5cf1f4 100644 --- a/telegram/choseninlineresult.py +++ b/telegram/choseninlineresult.py @@ -61,7 +61,7 @@ class ChosenInlineResult(TelegramObject): """ - __slots__ = ('location', 'result_id', 'from_user', 'inline_message_id', '_id_attrs', 'query') + __slots__ = ("location", "result_id", "from_user", "inline_message_id", "query") def __init__( self, @@ -83,7 +83,7 @@ def __init__( self._id_attrs = (self.result_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChosenInlineResult']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ChosenInlineResult"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) @@ -91,8 +91,8 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChosenInline return None # Required - data['from_user'] = User.de_json(data.pop('from'), bot) + data["from_user"] = User.de_json(data.pop("from"), bot) # Optionals - data['location'] = Location.de_json(data.get('location'), bot) + data["location"] = Location.de_json(data.get("location"), bot) return cls(**data) diff --git a/telegram/constants.py b/telegram/constants.py index 795f37203c1..f9dceaebed6 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -66,12 +66,11 @@ :class:`telegram.ChatAction`: +.. versionchanged:: 14.0 + Removed the deprecated constants ``CHATACTION_RECORD_AUDIO`` and ``CHATACTION_UPLOAD_AUDIO``. + Attributes: CHATACTION_FIND_LOCATION (:obj:`str`): ``'find_location'`` - CHATACTION_RECORD_AUDIO (:obj:`str`): ``'record_audio'`` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :const:`CHATACTION_RECORD_VOICE` instead. CHATACTION_RECORD_VOICE (:obj:`str`): ``'record_voice'`` .. versionadded:: 13.5 @@ -79,9 +78,6 @@ CHATACTION_RECORD_VIDEO_NOTE (:obj:`str`): ``'record_video_note'`` CHATACTION_TYPING (:obj:`str`): ``'typing'`` CHATACTION_UPLOAD_AUDIO (:obj:`str`): ``'upload_audio'`` - - .. deprecated:: 13.5 - Deprecated by Telegram. Use :const:`CHATACTION_UPLOAD_VOICE` instead. CHATACTION_UPLOAD_VOICE (:obj:`str`): ``'upload_voice'`` .. versionadded:: 13.5 @@ -233,7 +229,7 @@ """ from typing import List -BOT_API_VERSION: str = '5.3' +BOT_API_VERSION: str = "5.3" MAX_MESSAGE_LENGTH: int = 4096 MAX_CAPTION_LENGTH: int = 1024 ANONYMOUS_ADMIN_ID: int = 1087968824 @@ -252,38 +248,36 @@ MAX_INLINE_QUERY_RESULTS: int = 50 MAX_ANSWER_CALLBACK_QUERY_TEXT_LENGTH: int = 200 -CHAT_SENDER: str = 'sender' -CHAT_PRIVATE: str = 'private' -CHAT_GROUP: str = 'group' -CHAT_SUPERGROUP: str = 'supergroup' -CHAT_CHANNEL: str = 'channel' - -CHATACTION_FIND_LOCATION: str = 'find_location' -CHATACTION_RECORD_AUDIO: str = 'record_audio' -CHATACTION_RECORD_VOICE: str = 'record_voice' -CHATACTION_RECORD_VIDEO: str = 'record_video' -CHATACTION_RECORD_VIDEO_NOTE: str = 'record_video_note' -CHATACTION_TYPING: str = 'typing' -CHATACTION_UPLOAD_AUDIO: str = 'upload_audio' -CHATACTION_UPLOAD_VOICE: str = 'upload_voice' -CHATACTION_UPLOAD_DOCUMENT: str = 'upload_document' -CHATACTION_UPLOAD_PHOTO: str = 'upload_photo' -CHATACTION_UPLOAD_VIDEO: str = 'upload_video' -CHATACTION_UPLOAD_VIDEO_NOTE: str = 'upload_video_note' - -CHATMEMBER_ADMINISTRATOR: str = 'administrator' -CHATMEMBER_CREATOR: str = 'creator' -CHATMEMBER_KICKED: str = 'kicked' -CHATMEMBER_LEFT: str = 'left' -CHATMEMBER_MEMBER: str = 'member' -CHATMEMBER_RESTRICTED: str = 'restricted' - -DICE_DICE: str = '🎲' -DICE_DARTS: str = '🎯' -DICE_BASKETBALL: str = 'πŸ€' -DICE_FOOTBALL: str = '⚽' -DICE_SLOT_MACHINE: str = '🎰' -DICE_BOWLING: str = '🎳' +CHAT_SENDER: str = "sender" +CHAT_PRIVATE: str = "private" +CHAT_GROUP: str = "group" +CHAT_SUPERGROUP: str = "supergroup" +CHAT_CHANNEL: str = "channel" + +CHATACTION_FIND_LOCATION: str = "find_location" +CHATACTION_RECORD_VOICE: str = "record_voice" +CHATACTION_RECORD_VIDEO: str = "record_video" +CHATACTION_RECORD_VIDEO_NOTE: str = "record_video_note" +CHATACTION_TYPING: str = "typing" +CHATACTION_UPLOAD_VOICE: str = "upload_voice" +CHATACTION_UPLOAD_DOCUMENT: str = "upload_document" +CHATACTION_UPLOAD_PHOTO: str = "upload_photo" +CHATACTION_UPLOAD_VIDEO: str = "upload_video" +CHATACTION_UPLOAD_VIDEO_NOTE: str = "upload_video_note" + +CHATMEMBER_ADMINISTRATOR: str = "administrator" +CHATMEMBER_CREATOR: str = "creator" +CHATMEMBER_KICKED: str = "kicked" +CHATMEMBER_LEFT: str = "left" +CHATMEMBER_MEMBER: str = "member" +CHATMEMBER_RESTRICTED: str = "restricted" + +DICE_DICE: str = "🎲" +DICE_DARTS: str = "🎯" +DICE_BASKETBALL: str = "πŸ€" +DICE_FOOTBALL: str = "⚽" +DICE_SLOT_MACHINE: str = "🎰" +DICE_BOWLING: str = "🎳" DICE_ALL_EMOJI: List[str] = [ DICE_DICE, DICE_DARTS, @@ -293,21 +287,21 @@ DICE_BOWLING, ] -MESSAGEENTITY_MENTION: str = 'mention' -MESSAGEENTITY_HASHTAG: str = 'hashtag' -MESSAGEENTITY_CASHTAG: str = 'cashtag' -MESSAGEENTITY_PHONE_NUMBER: str = 'phone_number' -MESSAGEENTITY_BOT_COMMAND: str = 'bot_command' -MESSAGEENTITY_URL: str = 'url' -MESSAGEENTITY_EMAIL: str = 'email' -MESSAGEENTITY_BOLD: str = 'bold' -MESSAGEENTITY_ITALIC: str = 'italic' -MESSAGEENTITY_CODE: str = 'code' -MESSAGEENTITY_PRE: str = 'pre' -MESSAGEENTITY_TEXT_LINK: str = 'text_link' -MESSAGEENTITY_TEXT_MENTION: str = 'text_mention' -MESSAGEENTITY_UNDERLINE: str = 'underline' -MESSAGEENTITY_STRIKETHROUGH: str = 'strikethrough' +MESSAGEENTITY_MENTION: str = "mention" +MESSAGEENTITY_HASHTAG: str = "hashtag" +MESSAGEENTITY_CASHTAG: str = "cashtag" +MESSAGEENTITY_PHONE_NUMBER: str = "phone_number" +MESSAGEENTITY_BOT_COMMAND: str = "bot_command" +MESSAGEENTITY_URL: str = "url" +MESSAGEENTITY_EMAIL: str = "email" +MESSAGEENTITY_BOLD: str = "bold" +MESSAGEENTITY_ITALIC: str = "italic" +MESSAGEENTITY_CODE: str = "code" +MESSAGEENTITY_PRE: str = "pre" +MESSAGEENTITY_TEXT_LINK: str = "text_link" +MESSAGEENTITY_TEXT_MENTION: str = "text_mention" +MESSAGEENTITY_UNDERLINE: str = "underline" +MESSAGEENTITY_STRIKETHROUGH: str = "strikethrough" MESSAGEENTITY_ALL_TYPES: List[str] = [ MESSAGEENTITY_MENTION, MESSAGEENTITY_HASHTAG, @@ -326,33 +320,33 @@ MESSAGEENTITY_STRIKETHROUGH, ] -PARSEMODE_MARKDOWN: str = 'Markdown' -PARSEMODE_MARKDOWN_V2: str = 'MarkdownV2' -PARSEMODE_HTML: str = 'HTML' +PARSEMODE_MARKDOWN: str = "Markdown" +PARSEMODE_MARKDOWN_V2: str = "MarkdownV2" +PARSEMODE_HTML: str = "HTML" -POLL_REGULAR: str = 'regular' -POLL_QUIZ: str = 'quiz' +POLL_REGULAR: str = "regular" +POLL_QUIZ: str = "quiz" MAX_POLL_QUESTION_LENGTH: int = 300 MAX_POLL_OPTION_LENGTH: int = 100 -STICKER_FOREHEAD: str = 'forehead' -STICKER_EYES: str = 'eyes' -STICKER_MOUTH: str = 'mouth' -STICKER_CHIN: str = 'chin' - -UPDATE_MESSAGE = 'message' -UPDATE_EDITED_MESSAGE = 'edited_message' -UPDATE_CHANNEL_POST = 'channel_post' -UPDATE_EDITED_CHANNEL_POST = 'edited_channel_post' -UPDATE_INLINE_QUERY = 'inline_query' -UPDATE_CHOSEN_INLINE_RESULT = 'chosen_inline_result' -UPDATE_CALLBACK_QUERY = 'callback_query' -UPDATE_SHIPPING_QUERY = 'shipping_query' -UPDATE_PRE_CHECKOUT_QUERY = 'pre_checkout_query' -UPDATE_POLL = 'poll' -UPDATE_POLL_ANSWER = 'poll_answer' -UPDATE_MY_CHAT_MEMBER = 'my_chat_member' -UPDATE_CHAT_MEMBER = 'chat_member' +STICKER_FOREHEAD: str = "forehead" +STICKER_EYES: str = "eyes" +STICKER_MOUTH: str = "mouth" +STICKER_CHIN: str = "chin" + +UPDATE_MESSAGE = "message" +UPDATE_EDITED_MESSAGE = "edited_message" +UPDATE_CHANNEL_POST = "channel_post" +UPDATE_EDITED_CHANNEL_POST = "edited_channel_post" +UPDATE_INLINE_QUERY = "inline_query" +UPDATE_CHOSEN_INLINE_RESULT = "chosen_inline_result" +UPDATE_CALLBACK_QUERY = "callback_query" +UPDATE_SHIPPING_QUERY = "shipping_query" +UPDATE_PRE_CHECKOUT_QUERY = "pre_checkout_query" +UPDATE_POLL = "poll" +UPDATE_POLL_ANSWER = "poll_answer" +UPDATE_MY_CHAT_MEMBER = "my_chat_member" +UPDATE_CHAT_MEMBER = "chat_member" UPDATE_ALL_TYPES = [ UPDATE_MESSAGE, UPDATE_EDITED_MESSAGE, @@ -369,10 +363,10 @@ UPDATE_CHAT_MEMBER, ] -BOT_COMMAND_SCOPE_DEFAULT = 'default' -BOT_COMMAND_SCOPE_ALL_PRIVATE_CHATS = 'all_private_chats' -BOT_COMMAND_SCOPE_ALL_GROUP_CHATS = 'all_group_chats' -BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS = 'all_chat_administrators' -BOT_COMMAND_SCOPE_CHAT = 'chat' -BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS = 'chat_administrators' -BOT_COMMAND_SCOPE_CHAT_MEMBER = 'chat_member' +BOT_COMMAND_SCOPE_DEFAULT = "default" +BOT_COMMAND_SCOPE_ALL_PRIVATE_CHATS = "all_private_chats" +BOT_COMMAND_SCOPE_ALL_GROUP_CHATS = "all_group_chats" +BOT_COMMAND_SCOPE_ALL_CHAT_ADMINISTRATORS = "all_chat_administrators" +BOT_COMMAND_SCOPE_CHAT = "chat" +BOT_COMMAND_SCOPE_CHAT_ADMINISTRATORS = "chat_administrators" +BOT_COMMAND_SCOPE_CHAT_MEMBER = "chat_member" diff --git a/telegram/dice.py b/telegram/dice.py index 3406ceedad8..117433db9ed 100644 --- a/telegram/dice.py +++ b/telegram/dice.py @@ -64,7 +64,7 @@ class Dice(TelegramObject): """ - __slots__ = ('emoji', 'value', '_id_attrs') + __slots__ = ("emoji", "value") def __init__(self, value: int, emoji: str, **_kwargs: Any): self.value = value diff --git a/telegram/error.py b/telegram/error.py index 5e597cd2b77..77202f85f8d 100644 --- a/telegram/error.py +++ b/telegram/error.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. # pylint: disable=C0115 """This module contains an object that represents Telegram errors.""" -from typing import Tuple +from typing import Tuple, Union def _lstrip_str(in_s: str, lstr: str) -> str: @@ -41,22 +41,21 @@ def _lstrip_str(in_s: str, lstr: str) -> str: class TelegramError(Exception): """Base class for Telegram errors.""" - # Apparently the base class Exception already has __dict__ in it, so its not included here - __slots__ = ('message',) + __slots__ = ("message",) def __init__(self, message: str): super().__init__() - msg = _lstrip_str(message, 'Error: ') - msg = _lstrip_str(msg, '[Error]: ') - msg = _lstrip_str(msg, 'Bad Request: ') + msg = _lstrip_str(message, "Error: ") + msg = _lstrip_str(msg, "[Error]: ") + msg = _lstrip_str(msg, "Bad Request: ") if msg != message: # api_error - capitalize the msg... msg = msg.capitalize() self.message = msg def __str__(self) -> str: - return '%s' % self.message + return "%s" % self.message def __reduce__(self) -> Tuple[type, Tuple[str]]: return self.__class__, (self.message,) @@ -74,7 +73,7 @@ class InvalidToken(TelegramError): __slots__ = () def __init__(self) -> None: - super().__init__('Invalid token') + super().__init__("Invalid token") def __reduce__(self) -> Tuple[type, Tuple]: # type: ignore[override] return self.__class__, () @@ -98,7 +97,7 @@ class TimedOut(NetworkError): __slots__ = () def __init__(self) -> None: - super().__init__('Timed out') + super().__init__("Timed out") def __reduce__(self) -> Tuple[type, Tuple]: # type: ignore[override] return self.__class__, () @@ -113,10 +112,10 @@ class ChatMigrated(TelegramError): """ - __slots__ = ('new_chat_id',) + __slots__ = ("new_chat_id",) def __init__(self, new_chat_id: int): - super().__init__(f'Group migrated to supergroup. New chat id: {new_chat_id}') + super().__init__(f"Group migrated to supergroup. New chat id: {new_chat_id}") self.new_chat_id = new_chat_id def __reduce__(self) -> Tuple[type, Tuple[int]]: # type: ignore[override] @@ -132,10 +131,10 @@ class RetryAfter(TelegramError): """ - __slots__ = ('retry_after',) + __slots__ = ("retry_after",) def __init__(self, retry_after: int): - super().__init__(f'Flood control exceeded. Retry in {float(retry_after)} seconds') + super().__init__(f"Flood control exceeded. Retry in {float(retry_after)} seconds") self.retry_after = float(retry_after) def __reduce__(self) -> Tuple[type, Tuple[float]]: # type: ignore[override] @@ -149,3 +148,16 @@ class Conflict(TelegramError): def __reduce__(self) -> Tuple[type, Tuple[str]]: return self.__class__, (self.message,) + + +class PassportDecryptionError(TelegramError): + """Something went wrong with decryption.""" + + __slots__ = ("_msg",) + + def __init__(self, message: Union[str, Exception]): + super().__init__(f"PassportDecryptionError: {message}") + self._msg = str(message) + + def __reduce__(self) -> Tuple[type, Tuple[str]]: + return self.__class__, (self._msg,) diff --git a/telegram/ext/__init__.py b/telegram/ext/__init__.py index 731ad2c9e49..5bcaafeec61 100644 --- a/telegram/ext/__init__.py +++ b/telegram/ext/__init__.py @@ -16,28 +16,16 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0413 """Extensions over the Telegram Bot API to facilitate bot making""" from .extbot import ExtBot -from .basepersistence import BasePersistence +from .basepersistence import BasePersistence, PersistenceInput from .picklepersistence import PicklePersistence from .dictpersistence import DictPersistence from .handler import Handler from .callbackcontext import CallbackContext from .contexttypes import ContextTypes -from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async - -# https://bugs.python.org/issue41451, fixed on 3.7+, doesn't actually remove slots -# try-except is just here in case the __init__ is called twice (like in the tests) -# this block is also the reason for the pylint-ignore at the top of the file -try: - del Dispatcher.__slots__ -except AttributeError as exc: - if str(exc) == '__slots__': - pass - else: - raise exc +from .dispatcher import Dispatcher, DispatcherHandlerStop from .jobqueue import JobQueue, Job from .updater import Updater @@ -47,15 +35,12 @@ from .filters import BaseFilter, MessageFilter, UpdateFilter, Filters from .messagehandler import MessageHandler from .commandhandler import CommandHandler, PrefixHandler -from .regexhandler import RegexHandler from .stringcommandhandler import StringCommandHandler from .stringregexhandler import StringRegexHandler from .typehandler import TypeHandler from .conversationhandler import ConversationHandler from .precheckoutqueryhandler import PreCheckoutQueryHandler from .shippingqueryhandler import ShippingQueryHandler -from .messagequeue import MessageQueue -from .messagequeue import DelayQueue from .pollanswerhandler import PollAnswerHandler from .pollhandler import PollHandler from .chatmemberhandler import ChatMemberHandler @@ -63,42 +48,39 @@ from .callbackdatacache import CallbackDataCache, InvalidCallbackData __all__ = ( - 'BaseFilter', - 'BasePersistence', - 'CallbackContext', - 'CallbackDataCache', - 'CallbackQueryHandler', - 'ChatMemberHandler', - 'ChosenInlineResultHandler', - 'CommandHandler', - 'ContextTypes', - 'ConversationHandler', - 'Defaults', - 'DelayQueue', - 'DictPersistence', - 'Dispatcher', - 'DispatcherHandlerStop', - 'ExtBot', - 'Filters', - 'Handler', - 'InlineQueryHandler', - 'InvalidCallbackData', - 'Job', - 'JobQueue', - 'MessageFilter', - 'MessageHandler', - 'MessageQueue', - 'PicklePersistence', - 'PollAnswerHandler', - 'PollHandler', - 'PreCheckoutQueryHandler', - 'PrefixHandler', - 'RegexHandler', - 'ShippingQueryHandler', - 'StringCommandHandler', - 'StringRegexHandler', - 'TypeHandler', - 'UpdateFilter', - 'Updater', - 'run_async', + "BaseFilter", + "BasePersistence", + "CallbackContext", + "CallbackDataCache", + "CallbackQueryHandler", + "ChatMemberHandler", + "ChosenInlineResultHandler", + "CommandHandler", + "ContextTypes", + "ConversationHandler", + "Defaults", + "DictPersistence", + "Dispatcher", + "DispatcherHandlerStop", + "ExtBot", + "Filters", + "Handler", + "InlineQueryHandler", + "InvalidCallbackData", + "Job", + "JobQueue", + "MessageFilter", + "MessageHandler", + "PersistenceInput", + "PicklePersistence", + "PollAnswerHandler", + "PollHandler", + "PreCheckoutQueryHandler", + "PrefixHandler", + "ShippingQueryHandler", + "StringCommandHandler", + "StringRegexHandler", + "TypeHandler", + "UpdateFilter", + "Updater", ) diff --git a/telegram/ext/basepersistence.py b/telegram/ext/basepersistence.py index 974b97f8f8c..6a541149e3b 100644 --- a/telegram/ext/basepersistence.py +++ b/telegram/ext/basepersistence.py @@ -18,12 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the BasePersistence class.""" import warnings -from sys import version_info as py_ver from abc import ABC, abstractmethod from copy import copy -from typing import Dict, Optional, Tuple, cast, ClassVar, Generic, DefaultDict - -from telegram.utils.deprecate import set_new_attribute_deprecated +from typing import Dict, Optional, Tuple, cast, ClassVar, Generic, DefaultDict, NamedTuple from telegram import Bot import telegram.ext.extbot @@ -31,6 +28,33 @@ from telegram.ext.utils.types import UD, CD, BD, ConversationDict, CDCData +class PersistenceInput(NamedTuple): + """Convenience wrapper to group boolean input for :class:`BasePersistence`. + + Args: + bot_data (:obj:`bool`, optional): Whether the setting should be applied for ``bot_data``. + Defaults to :obj:`True`. + chat_data (:obj:`bool`, optional): Whether the setting should be applied for ``chat_data``. + Defaults to :obj:`True`. + user_data (:obj:`bool`, optional): Whether the setting should be applied for ``user_data``. + Defaults to :obj:`True`. + callback_data (:obj:`bool`, optional): Whether the setting should be applied for + ``callback_data``. Defaults to :obj:`True`. + + Attributes: + bot_data (:obj:`bool`): Whether the setting should be applied for ``bot_data``. + chat_data (:obj:`bool`): Whether the setting should be applied for ``chat_data``. + user_data (:obj:`bool`): Whether the setting should be applied for ``user_data``. + callback_data (:obj:`bool`): Whether the setting should be applied for ``callback_data``. + + """ + + bot_data: bool = True + chat_data: bool = True + user_data: bool = True + callback_data: bool = True + + class BasePersistence(Generic[UD, CD, BD], ABC): """Interface class for adding persistence to your bot. Subclass this object for different implementations of a persistent bot. @@ -53,7 +77,7 @@ class BasePersistence(Generic[UD, CD, BD], ABC): * :meth:`flush` If you don't actually need one of those methods, a simple ``pass`` is enough. For example, if - ``store_bot_data=False``, you don't need :meth:`get_bot_data`, :meth:`update_bot_data` or + you don't store ``bot_data``, you don't need :meth:`get_bot_data`, :meth:`update_bot_data` or :meth:`refresh_bot_data`. Warning: @@ -68,53 +92,28 @@ class BasePersistence(Generic[UD, CD, BD], ABC): of the :meth:`update/get_*` methods, i.e. you don't need to worry about it while implementing a custom persistence subclass. - Args: - store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this - persistence class. Default is :obj:`True`. - store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this - persistence class. Default is :obj:`True` . - store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this - persistence class. Default is :obj:`True`. - store_callback_data (:obj:`bool`, optional): Whether callback_data should be saved by this - persistence class. Default is :obj:`False`. + .. versionchanged:: 14.0 + The parameters and attributes ``store_*_data`` were replaced by :attr:`store_data`. - .. versionadded:: 13.6 + Args: + store_data (:class:`PersistenceInput`, optional): Specifies which kinds of data will be + saved by this persistence instance. By default, all available kinds of data will be + saved. Attributes: - store_user_data (:obj:`bool`): Optional, Whether user_data should be saved by this - persistence class. - store_chat_data (:obj:`bool`): Optional. Whether chat_data should be saved by this - persistence class. - store_bot_data (:obj:`bool`): Optional. Whether bot_data should be saved by this - persistence class. - store_callback_data (:obj:`bool`): Optional. Whether callback_data should be saved by this - persistence class. - - .. versionadded:: 13.6 + store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this + persistence instance. """ - # Apparently Py 3.7 and below have '__dict__' in ABC - if py_ver < (3, 7): - __slots__ = ( - 'store_user_data', - 'store_chat_data', - 'store_bot_data', - 'store_callback_data', - 'bot', - ) - else: - __slots__ = ( - 'store_user_data', # type: ignore[assignment] - 'store_chat_data', - 'store_bot_data', - 'store_callback_data', - 'bot', - '__dict__', - ) + __slots__ = ( + "bot", + "store_data", + "__dict__", # __dict__ is included because we replace methods in the __new__ + ) def __new__( cls, *args: object, **kwargs: object # pylint: disable=W0613 - ) -> 'BasePersistence': + ) -> "BasePersistence": """This overrides the get_* and update_* methods to use insert/replace_bot. That has the side effect that we always pass deepcopied data to those methods, so in Pickle/DictPersistence we don't have to worry about copying the data again. @@ -160,39 +159,24 @@ def update_callback_data_replace_bot(data: CDCData) -> None: obj_data, queue = data return update_callback_data((instance.replace_bot(obj_data), queue)) - # We want to ignore TGDeprecation warnings so we use obj.__setattr__. Adds to __dict__ - object.__setattr__(instance, 'get_user_data', get_user_data_insert_bot) - object.__setattr__(instance, 'get_chat_data', get_chat_data_insert_bot) - object.__setattr__(instance, 'get_bot_data', get_bot_data_insert_bot) - object.__setattr__(instance, 'get_callback_data', get_callback_data_insert_bot) - object.__setattr__(instance, 'update_user_data', update_user_data_replace_bot) - object.__setattr__(instance, 'update_chat_data', update_chat_data_replace_bot) - object.__setattr__(instance, 'update_bot_data', update_bot_data_replace_bot) - object.__setattr__(instance, 'update_callback_data', update_callback_data_replace_bot) + # Adds to __dict__ + setattr(instance, "get_user_data", get_user_data_insert_bot) + setattr(instance, "get_chat_data", get_chat_data_insert_bot) + setattr(instance, "get_bot_data", get_bot_data_insert_bot) + setattr(instance, "get_callback_data", get_callback_data_insert_bot) + setattr(instance, "update_user_data", update_user_data_replace_bot) + setattr(instance, "update_chat_data", update_chat_data_replace_bot) + setattr(instance, "update_bot_data", update_bot_data_replace_bot) + setattr(instance, "update_callback_data", update_callback_data_replace_bot) return instance def __init__( self, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - store_callback_data: bool = False, + store_data: PersistenceInput = None, ): - self.store_user_data = store_user_data - self.store_chat_data = store_chat_data - self.store_bot_data = store_bot_data - self.store_callback_data = store_callback_data - self.bot: Bot = None # type: ignore[assignment] + self.store_data = store_data or PersistenceInput() - def __setattr__(self, key: str, value: object) -> None: - # Allow user defined subclasses to have custom attributes. - if issubclass(self.__class__, BasePersistence) and self.__class__.__name__ not in { - 'DictPersistence', - 'PicklePersistence', - }: - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) + self.bot: Bot = None # type: ignore[assignment] def set_bot(self, bot: Bot) -> None: """Set the Bot to be used by this persistence instance. @@ -200,8 +184,8 @@ def set_bot(self, bot: Bot) -> None: Args: bot (:class:`telegram.Bot`): The bot. """ - if self.store_callback_data and not isinstance(bot, telegram.ext.extbot.ExtBot): - raise TypeError('store_callback_data can only be used with telegram.ext.ExtBot.') + if self.store_data.callback_data and not isinstance(bot, telegram.ext.extbot.ExtBot): + raise TypeError("callback_data can only be stored when using telegram.ext.ExtBot.") self.bot = bot @@ -247,8 +231,8 @@ def _replace_bot(cls, obj: object, memo: Dict[int, object]) -> object: # pylint if isinstance(obj, type): # classes usually do have a __dict__, but it's not writable warnings.warn( - 'BasePersistence.replace_bot does not handle classes. See ' - 'the docs of BasePersistence.replace_bot for more information.', + "BasePersistence.replace_bot does not handle classes. See " + "the docs of BasePersistence.replace_bot for more information.", RuntimeWarning, ) return obj @@ -258,8 +242,8 @@ def _replace_bot(cls, obj: object, memo: Dict[int, object]) -> object: # pylint memo[obj_id] = new_obj except Exception: warnings.warn( - 'BasePersistence.replace_bot does not handle objects that can not be copied. See ' - 'the docs of BasePersistence.replace_bot for more information.', + "BasePersistence.replace_bot does not handle objects that can not be copied. See " + "the docs of BasePersistence.replace_bot for more information.", RuntimeWarning, ) memo[obj_id] = obj @@ -278,7 +262,7 @@ def _replace_bot(cls, obj: object, memo: Dict[int, object]) -> object: # pylint memo[obj_id] = new_obj return new_obj try: - if hasattr(obj, '__slots__'): + if hasattr(obj, "__slots__"): for attr_name in new_obj.__slots__: setattr( new_obj, @@ -287,20 +271,20 @@ def _replace_bot(cls, obj: object, memo: Dict[int, object]) -> object: # pylint cls._replace_bot(getattr(new_obj, attr_name), memo), memo ), ) - if '__dict__' in obj.__slots__: + if "__dict__" in obj.__slots__: # In this case, we have already covered the case that obj has __dict__ # Note that obj may have a __dict__ even if it's not in __slots__! memo[obj_id] = new_obj return new_obj - if hasattr(obj, '__dict__'): + if hasattr(obj, "__dict__"): for attr_name, attr in new_obj.__dict__.items(): setattr(new_obj, attr_name, cls._replace_bot(attr, memo)) memo[obj_id] = new_obj return new_obj except Exception as exception: warnings.warn( - f'Parsing of an object failed with the following exception: {exception}. ' - f'See the docs of BasePersistence.replace_bot for more information.', + f"Parsing of an object failed with the following exception: {exception}. " + f"See the docs of BasePersistence.replace_bot for more information.", RuntimeWarning, ) @@ -350,8 +334,8 @@ def _insert_bot(self, obj: object, memo: Dict[int, object]) -> object: # pylint if isinstance(obj, type): # classes usually do have a __dict__, but it's not writable warnings.warn( - 'BasePersistence.insert_bot does not handle classes. See ' - 'the docs of BasePersistence.insert_bot for more information.', + "BasePersistence.insert_bot does not handle classes. See " + "the docs of BasePersistence.insert_bot for more information.", RuntimeWarning, ) return obj @@ -360,8 +344,8 @@ def _insert_bot(self, obj: object, memo: Dict[int, object]) -> object: # pylint new_obj = copy(obj) except Exception: warnings.warn( - 'BasePersistence.insert_bot does not handle objects that can not be copied. See ' - 'the docs of BasePersistence.insert_bot for more information.', + "BasePersistence.insert_bot does not handle objects that can not be copied. See " + "the docs of BasePersistence.insert_bot for more information.", RuntimeWarning, ) memo[obj_id] = obj @@ -380,7 +364,7 @@ def _insert_bot(self, obj: object, memo: Dict[int, object]) -> object: # pylint memo[obj_id] = new_obj return new_obj try: - if hasattr(obj, '__slots__'): + if hasattr(obj, "__slots__"): for attr_name in obj.__slots__: setattr( new_obj, @@ -389,20 +373,20 @@ def _insert_bot(self, obj: object, memo: Dict[int, object]) -> object: # pylint self._insert_bot(getattr(new_obj, attr_name), memo), memo ), ) - if '__dict__' in obj.__slots__: + if "__dict__" in obj.__slots__: # In this case, we have already covered the case that obj has __dict__ # Note that obj may have a __dict__ even if it's not in __slots__! memo[obj_id] = new_obj return new_obj - if hasattr(obj, '__dict__'): + if hasattr(obj, "__dict__"): for attr_name, attr in new_obj.__dict__.items(): setattr(new_obj, attr_name, self._insert_bot(attr, memo)) memo[obj_id] = new_obj return new_obj except Exception as exception: warnings.warn( - f'Parsing of an object failed with the following exception: {exception}. ' - f'See the docs of BasePersistence.insert_bot for more information.', + f"Parsing of an object failed with the following exception: {exception}. " + f"See the docs of BasePersistence.insert_bot for more information.", RuntimeWarning, ) @@ -439,17 +423,20 @@ def get_bot_data(self) -> BD: :class:`telegram.ext.utils.types.BD`: The restored bot data. """ + @abstractmethod def get_callback_data(self) -> Optional[CDCData]: """Will be called by :class:`telegram.ext.Dispatcher` upon creation with a persistence object. If callback data was stored, it should be returned. .. versionadded:: 13.6 + .. versionchanged:: 14.0 + Changed this method into an ``@abstractmethod``. + Returns: Optional[:class:`telegram.ext.utils.types.CDCData`]: The restored meta data or :obj:`None`, if no data was stored. """ - raise NotImplementedError @abstractmethod def get_conversations(self, name: str) -> ConversationDict: @@ -510,6 +497,7 @@ def update_bot_data(self, data: BD) -> None: :attr:`telegram.ext.Dispatcher.bot_data`. """ + @abstractmethod def refresh_user_data(self, user_id: int, user_data: UD) -> None: """Will be called by the :class:`telegram.ext.Dispatcher` before passing the :attr:`user_data` to a callback. Can be used to update data stored in :attr:`user_data` @@ -517,11 +505,15 @@ def refresh_user_data(self, user_id: int, user_data: UD) -> None: .. versionadded:: 13.6 + .. versionchanged:: 14.0 + Changed this method into an ``@abstractmethod``. + Args: user_id (:obj:`int`): The user ID this :attr:`user_data` is associated with. user_data (:class:`telegram.ext.utils.types.UD`): The ``user_data`` of a single user. """ + @abstractmethod def refresh_chat_data(self, chat_id: int, chat_data: CD) -> None: """Will be called by the :class:`telegram.ext.Dispatcher` before passing the :attr:`chat_data` to a callback. Can be used to update data stored in :attr:`chat_data` @@ -529,11 +521,15 @@ def refresh_chat_data(self, chat_id: int, chat_data: CD) -> None: .. versionadded:: 13.6 + .. versionchanged:: 14.0 + Changed this method into an ``@abstractmethod``. + Args: chat_id (:obj:`int`): The chat ID this :attr:`chat_data` is associated with. chat_data (:class:`telegram.ext.utils.types.CD`): The ``chat_data`` of a single chat. """ + @abstractmethod def refresh_bot_data(self, bot_data: BD) -> None: """Will be called by the :class:`telegram.ext.Dispatcher` before passing the :attr:`bot_data` to a callback. Can be used to update data stored in :attr:`bot_data` @@ -541,26 +537,36 @@ def refresh_bot_data(self, bot_data: BD) -> None: .. versionadded:: 13.6 + .. versionchanged:: 14.0 + Changed this method into an ``@abstractmethod``. + Args: bot_data (:class:`telegram.ext.utils.types.BD`): The ``bot_data``. """ + @abstractmethod def update_callback_data(self, data: CDCData) -> None: """Will be called by the :class:`telegram.ext.Dispatcher` after a handler has handled an update. .. versionadded:: 13.6 + .. versionchanged:: 14.0 + Changed this method into an ``@abstractmethod``. + Args: data (:class:`telegram.ext.utils.types.CDCData`): The relevant data to restore :class:`telegram.ext.CallbackDataCache`. """ - raise NotImplementedError + @abstractmethod def flush(self) -> None: """Will be called by :class:`telegram.ext.Updater` upon receiving a stop signal. Gives the persistence a chance to finish up saving or close a database connection gracefully. + + .. versionchanged:: 14.0 + Changed this method into an ``@abstractmethod``. """ - REPLACED_BOT: ClassVar[str] = 'bot_instance_replaced_by_ptb_persistence' + REPLACED_BOT: ClassVar[str] = "bot_instance_replaced_by_ptb_persistence" """:obj:`str`: Placeholder for :class:`telegram.Bot` instances replaced in saved data.""" diff --git a/telegram/ext/callbackcontext.py b/telegram/ext/callbackcontext.py index 5c5e9bedfe2..7cea0dd9aed 100644 --- a/telegram/ext/callbackcontext.py +++ b/telegram/ext/callbackcontext.py @@ -30,7 +30,6 @@ Union, Generic, Type, - TypeVar, ) from telegram import Update, CallbackQuery @@ -40,8 +39,7 @@ if TYPE_CHECKING: from telegram import Bot from telegram.ext import Dispatcher, Job, JobQueue - -CC = TypeVar('CC', bound='CallbackContext') + from telegram.ext.utils.types import CCT class CallbackContext(Generic[UD, CD, BD]): @@ -93,39 +91,35 @@ class CallbackContext(Generic[UD, CD, BD]): """ __slots__ = ( - '_dispatcher', - '_chat_id_and_data', - '_user_id_and_data', - 'args', - 'matches', - 'error', - 'job', - 'async_args', - 'async_kwargs', - '__dict__', + "_dispatcher", + "_chat_id_and_data", + "_user_id_and_data", + "args", + "matches", + "error", + "job", + "async_args", + "async_kwargs", + "__dict__", ) - def __init__(self, dispatcher: 'Dispatcher'): + def __init__(self: "CCT", dispatcher: "Dispatcher[CCT, UD, CD, BD]"): """ Args: dispatcher (:class:`telegram.ext.Dispatcher`): """ - if not dispatcher.use_context: - raise ValueError( - 'CallbackContext should not be used with a non context aware ' 'dispatcher!' - ) self._dispatcher = dispatcher self._chat_id_and_data: Optional[Tuple[int, CD]] = None self._user_id_and_data: Optional[Tuple[int, UD]] = None self.args: Optional[List[str]] = None self.matches: Optional[List[Match]] = None self.error: Optional[Exception] = None - self.job: Optional['Job'] = None + self.job: Optional["Job"] = None self.async_args: Optional[Union[List, Tuple]] = None self.async_kwargs: Optional[Dict[str, object]] = None @property - def dispatcher(self) -> 'Dispatcher': + def dispatcher(self) -> "Dispatcher[CCT, UD, CD, BD]": """:class:`telegram.ext.Dispatcher`: The dispatcher associated with this context.""" return self._dispatcher @@ -188,11 +182,17 @@ def refresh_data(self) -> None: .. versionadded:: 13.6 """ if self.dispatcher.persistence: - if self.dispatcher.persistence.store_bot_data: + if self.dispatcher.persistence.store_data.bot_data: self.dispatcher.persistence.refresh_bot_data(self.bot_data) - if self.dispatcher.persistence.store_chat_data and self._chat_id_and_data is not None: + if ( + self.dispatcher.persistence.store_data.chat_data + and self._chat_id_and_data is not None + ): self.dispatcher.persistence.refresh_chat_data(*self._chat_id_and_data) - if self.dispatcher.persistence.store_user_data and self._user_id_and_data is not None: + if ( + self.dispatcher.persistence.store_data.user_data + and self._user_id_and_data is not None + ): self.dispatcher.persistence.refresh_user_data(*self._user_id_and_data) def drop_callback_data(self, callback_query: CallbackQuery) -> None: @@ -217,21 +217,21 @@ def drop_callback_data(self, callback_query: CallbackQuery) -> None: if isinstance(self.bot, ExtBot): if not self.bot.arbitrary_callback_data: raise RuntimeError( - 'This telegram.ext.ExtBot instance does not use arbitrary callback data.' + "This telegram.ext.ExtBot instance does not use arbitrary callback data." ) self.bot.callback_data_cache.drop_data(callback_query) else: - raise RuntimeError('telegram.Bot does not allow for arbitrary callback data.') + raise RuntimeError("telegram.Bot does not allow for arbitrary callback data.") @classmethod def from_error( - cls: Type[CC], + cls: Type["CCT"], update: object, error: Exception, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher[CCT, UD, CD, BD]", async_args: Union[List, Tuple] = None, async_kwargs: Dict[str, object] = None, - ) -> CC: + ) -> "CCT": """ Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the error handlers. @@ -261,7 +261,9 @@ def from_error( return self @classmethod - def from_update(cls: Type[CC], update: object, dispatcher: 'Dispatcher') -> CC: + def from_update( + cls: Type["CCT"], update: object, dispatcher: "Dispatcher[CCT, UD, CD, BD]" + ) -> "CCT": """ Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the handlers. @@ -276,7 +278,7 @@ def from_update(cls: Type[CC], update: object, dispatcher: 'Dispatcher') -> CC: Returns: :class:`telegram.ext.CallbackContext` """ - self = cls(dispatcher) + self = cls(dispatcher) # type: ignore[arg-type] if update is not None and isinstance(update, Update): chat = update.effective_chat @@ -295,7 +297,7 @@ def from_update(cls: Type[CC], update: object, dispatcher: 'Dispatcher') -> CC: return self @classmethod - def from_job(cls: Type[CC], job: 'Job', dispatcher: 'Dispatcher') -> CC: + def from_job(cls: Type["CCT"], job: "Job", dispatcher: "Dispatcher[CCT, UD, CD, BD]") -> "CCT": """ Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to a job callback. @@ -310,7 +312,7 @@ def from_job(cls: Type[CC], job: 'Job', dispatcher: 'Dispatcher') -> CC: Returns: :class:`telegram.ext.CallbackContext` """ - self = cls(dispatcher) + self = cls(dispatcher) # type: ignore[arg-type] self.job = job return self @@ -324,12 +326,12 @@ def update(self, data: Dict[str, object]) -> None: setattr(self, key, value) @property - def bot(self) -> 'Bot': + def bot(self) -> "Bot": """:class:`telegram.Bot`: The bot associated with this context.""" return self._dispatcher.bot @property - def job_queue(self) -> Optional['JobQueue']: + def job_queue(self) -> Optional["JobQueue"]: """ :class:`telegram.ext.JobQueue`: The ``JobQueue`` used by the :class:`telegram.ext.Dispatcher` and (usually) the :class:`telegram.ext.Updater` diff --git a/telegram/ext/callbackdatacache.py b/telegram/ext/callbackdatacache.py index ac60e47be55..78d54eaea71 100644 --- a/telegram/ext/callbackdatacache.py +++ b/telegram/ext/callbackdatacache.py @@ -75,12 +75,12 @@ class InvalidCallbackData(TelegramError): be found. """ - __slots__ = ('callback_data',) + __slots__ = ("callback_data",) def __init__(self, callback_data: str = None) -> None: super().__init__( - 'The object belonging to this callback_data was deleted or the callback_data was ' - 'manipulated.' + "The object belonging to this callback_data was deleted or the callback_data was " + "manipulated." ) self.callback_data = callback_data @@ -89,7 +89,7 @@ def __reduce__(self) -> Tuple[type, Tuple[Optional[str]]]: # type: ignore[overr class _KeyboardData: - __slots__ = ('keyboard_uuid', 'button_data', 'access_time') + __slots__ = ("keyboard_uuid", "button_data", "access_time") def __init__( self, keyboard_uuid: str, access_time: float = None, button_data: Dict[str, object] = None @@ -135,11 +135,11 @@ class CallbackDataCache: """ - __slots__ = ('bot', 'maxsize', '_keyboard_data', '_callback_queries', '__lock', 'logger') + __slots__ = ("bot", "maxsize", "_keyboard_data", "_callback_queries", "__lock", "logger") def __init__( self, - bot: 'ExtBot', + bot: "ExtBot", maxsize: int = 1024, persistent_data: CDCData = None, ): @@ -223,7 +223,7 @@ def __put_button(callback_data: object, keyboard_data: _KeyboardData) -> str: """ uuid = uuid4().hex keyboard_data.button_data[uuid] = callback_data - return f'{keyboard_data.keyboard_uuid}{uuid}' + return f"{keyboard_data.keyboard_uuid}{uuid}" def __get_keyboard_uuid_and_button_data( self, callback_data: str @@ -382,7 +382,7 @@ def drop_data(self, callback_query: CallbackQuery) -> None: keyboard_uuid = self._callback_queries.pop(callback_query.id) self.__drop_keyboard(keyboard_uuid) except KeyError as exc: - raise KeyError('CallbackQuery was not found in cache.') from exc + raise KeyError("CallbackQuery was not found in cache.") from exc def __drop_keyboard(self, keyboard_uuid: str) -> None: try: diff --git a/telegram/ext/callbackqueryhandler.py b/telegram/ext/callbackqueryhandler.py index beea75fe7dd..59c386b1d49 100644 --- a/telegram/ext/callbackqueryhandler.py +++ b/telegram/ext/callbackqueryhandler.py @@ -22,7 +22,6 @@ from typing import ( TYPE_CHECKING, Callable, - Dict, Match, Optional, Pattern, @@ -40,7 +39,7 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') +RT = TypeVar("RT") class CallbackQueryHandler(Handler[Update, CCT]): @@ -49,13 +48,6 @@ class CallbackQueryHandler(Handler[Update, CCT]): Read the documentation of the ``re`` module for more information. Note: - * :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same - user or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. * If your bot allows arbitrary objects as ``callback_data``, it may happen that the original ``callback_data`` for the incoming :class:`telegram.CallbackQuery`` can not be found. This is the case when either a malicious client tempered with the @@ -72,22 +64,10 @@ class CallbackQueryHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. pattern (:obj:`str` | `Pattern` | :obj:`callable` | :obj:`type`, optional): Pattern to test :attr:`telegram.CallbackQuery.data` against. If a string or a regex pattern is passed, :meth:`re.match` is used on :attr:`telegram.CallbackQuery.data` to @@ -106,66 +86,30 @@ class CallbackQueryHandler(Handler[Update, CCT]): .. versionchanged:: 13.6 Added support for arbitrary callback data. - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. pattern (`Pattern` | :obj:`callable` | :obj:`type`): Optional. Regex pattern, callback or type to test :attr:`telegram.CallbackQuery.data` against. .. versionchanged:: 13.6 Added support for arbitrary callback data. - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('pattern', 'pass_groups', 'pass_groupdict') + __slots__ = ("pattern",) def __init__( self, callback: Callable[[Update, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, pattern: Union[str, Pattern, type, Callable[[object], Optional[bool]]] = None, - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) @@ -173,8 +117,6 @@ def __init__( pattern = re.compile(pattern) self.pattern = pattern - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict def check_update(self, update: object) -> Optional[Union[bool, object]]: """Determines whether an update should be passed to this handlers :attr:`callback`. @@ -202,30 +144,11 @@ def check_update(self, update: object) -> Optional[Union[bool, object]]: return True return None - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - 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 and not callable(self.pattern): - check_result = cast(Match, check_result) - if self.pass_groups: - optional_args['groups'] = check_result.groups() - if self.pass_groupdict: - optional_args['groupdict'] = check_result.groupdict() - return optional_args - def collect_additional_context( self, context: CCT, update: Update, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Union[bool, Match], ) -> None: """Add the result of ``re.match(pattern, update.callback_query.data)`` to diff --git a/telegram/ext/chatmemberhandler.py b/telegram/ext/chatmemberhandler.py index 9499cfd2472..0dbdf0a9b60 100644 --- a/telegram/ext/chatmemberhandler.py +++ b/telegram/ext/chatmemberhandler.py @@ -24,7 +24,7 @@ from .handler import Handler from .utils.types import CCT -RT = TypeVar('RT') +RT = TypeVar("RT") class ChatMemberHandler(Handler[Update, CCT]): @@ -32,15 +32,6 @@ class ChatMemberHandler(Handler[Update, CCT]): .. versionadded:: 13.4 - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -48,9 +39,7 @@ class ChatMemberHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. @@ -58,22 +47,6 @@ class ChatMemberHandler(Handler[Update, CCT]): :attr:`CHAT_MEMBER` or :attr:`ANY_CHAT_MEMBER` to specify if this handler should handle only updates with :attr:`telegram.Update.my_chat_member`, :attr:`telegram.Update.chat_member` or both. Defaults to :attr:`MY_CHAT_MEMBER`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. @@ -82,19 +55,11 @@ class ChatMemberHandler(Handler[Update, CCT]): chat_member_types (:obj:`int`, optional): Specifies if this handler should handle only updates with :attr:`telegram.Update.my_chat_member`, :attr:`telegram.Update.chat_member` or both. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('chat_member_types',) + __slots__ = ("chat_member_types",) MY_CHAT_MEMBER: ClassVar[int] = -1 """:obj:`int`: Used as a constant to handle only :attr:`telegram.Update.my_chat_member`.""" CHAT_MEMBER: ClassVar[int] = 0 @@ -107,18 +72,10 @@ def __init__( self, callback: Callable[[Update, CCT], RT], chat_member_types: int = MY_CHAT_MEMBER, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) diff --git a/telegram/ext/choseninlineresulthandler.py b/telegram/ext/choseninlineresulthandler.py index ec3528945d9..c2fb4f9490f 100644 --- a/telegram/ext/choseninlineresulthandler.py +++ b/telegram/ext/choseninlineresulthandler.py @@ -26,7 +26,7 @@ from .handler import Handler from .utils.types import CCT -RT = TypeVar('RT') +RT = TypeVar("RT") if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher @@ -35,15 +35,6 @@ class ChosenInlineResultHandler(Handler[Update, CCT]): """Handler class to handle Telegram updates that contain a chosen inline result. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -51,28 +42,10 @@ class ChosenInlineResultHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. pattern (:obj:`str` | `Pattern`, optional): Regex pattern. If not :obj:`None`, ``re.match`` @@ -84,14 +57,6 @@ class ChosenInlineResultHandler(Handler[Update, CCT]): Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. pattern (`Pattern`): Optional. Regex pattern to test :attr:`telegram.ChosenInlineResult.result_id` against. @@ -100,24 +65,16 @@ class ChosenInlineResultHandler(Handler[Update, CCT]): """ - __slots__ = ('pattern',) + __slots__ = ("pattern",) def __init__( self, - callback: Callable[[Update, 'CallbackContext'], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, + callback: Callable[[Update, "CallbackContext"], RT], run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, pattern: Union[str, Pattern] = None, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) @@ -147,9 +104,9 @@ def check_update(self, update: object) -> Optional[Union[bool, object]]: def collect_additional_context( self, - context: 'CallbackContext', + context: "CallbackContext", update: Update, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Union[bool, Match], ) -> None: """This function adds the matched regex pattern result to diff --git a/telegram/ext/commandhandler.py b/telegram/ext/commandhandler.py index 1f0a32118a9..64e2082d800 100644 --- a/telegram/ext/commandhandler.py +++ b/telegram/ext/commandhandler.py @@ -18,12 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the CommandHandler and PrefixHandler classes.""" import re -import warnings from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple, TypeVar, Union from telegram import MessageEntity, Update from telegram.ext import BaseFilter, Filters -from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.types import SLT from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE @@ -33,7 +31,7 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') +RT = TypeVar("RT") class CommandHandler(Handler[Update, CCT]): @@ -49,13 +47,6 @@ class CommandHandler(Handler[Update, CCT]): Note: * :class:`CommandHandler` does *not* handle (edited) channel posts. - * :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a :obj:`dict` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same - user or in the same chat, it will be the same :obj:`dict`. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom @@ -67,9 +58,7 @@ class CommandHandler(Handler[Update, CCT]): Limitations are the same as described here https://core.telegram.org/bots#commands callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. @@ -77,31 +66,6 @@ class CommandHandler(Handler[Update, CCT]): :class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in :class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise operators (& for and, | for or, ~ for not). - allow_edited (:obj:`bool`, optional): Determines whether the handler should also accept - edited messages. Default is :obj:`False`. - DEPRECATED: Edited is allowed by default. To change this behavior use - ``~Filters.update.edited_message``. - pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the - arguments passed to the command as a keyword argument called ``args``. It will contain - a list of strings, which is the text following the command split on single or - consecutive whitespace characters. Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. @@ -115,42 +79,20 @@ class CommandHandler(Handler[Update, CCT]): callback (:obj:`callable`): The callback function for this handler. filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these Filters. - allow_edited (:obj:`bool`): Determines whether the handler should also accept - edited messages. - pass_args (:obj:`bool`): Determines whether the handler should be passed - ``args``. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('command', 'filters', 'pass_args') + __slots__ = ("command", "filters") def __init__( self, command: SLT[str], callback: Callable[[Update, CCT], RT], filters: BaseFilter = None, - allow_edited: bool = None, - pass_args: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) @@ -159,24 +101,14 @@ def __init__( else: self.command = [x.lower() for x in command] for comm in self.command: - if not re.match(r'^[\da-z_]{1,32}$', comm): - raise ValueError('Command is not a valid bot command') + if not re.match(r"^[\da-z_]{1,32}$", comm): + raise ValueError("Command is not a valid bot command") if filters: self.filters = Filters.update.messages & filters else: self.filters = Filters.update.messages - if allow_edited is not None: - warnings.warn( - 'allow_edited is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if not allow_edited: - self.filters &= ~Filters.update.edited_message - self.pass_args = pass_args - def check_update( self, update: object ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]: @@ -201,7 +133,7 @@ def check_update( ): command = message.text[1 : message.entities[0].length] args = message.text.split()[1:] - command_parts = command.split('@') + command_parts = command.split("@") command_parts.append(message.bot.username) if not ( @@ -216,25 +148,11 @@ def check_update( return False return None - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - 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] - return optional_args - def collect_additional_context( self, context: CCT, update: Update, - dispatcher: 'Dispatcher', + 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 @@ -282,13 +200,6 @@ class PrefixHandler(CommandHandler): Note: * :class:`PrefixHandler` does *not* handle (edited) channel posts. - * :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a :obj:`dict` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same - user or in the same chat, it will be the same :obj:`dict`. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom @@ -301,9 +212,7 @@ class PrefixHandler(CommandHandler): The command or list of commands this handler should listen for. callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. @@ -311,27 +220,6 @@ class PrefixHandler(CommandHandler): :class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in :class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise operators (& for and, | for or, ~ for not). - pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the - arguments passed to the command as a keyword argument called ``args``. It will contain - a list of strings, which is the text following the command split on single or - consecutive whitespace characters. Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. @@ -339,22 +227,12 @@ class PrefixHandler(CommandHandler): callback (:obj:`callable`): The callback function for this handler. filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these Filters. - pass_args (:obj:`bool`): Determines whether the handler should be passed - ``args``. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ # 'prefix' is a class property, & 'command' is included in the superclass, so they're left out. - __slots__ = ('_prefix', '_command', '_commands') + __slots__ = ("_prefix", "_command", "_commands") def __init__( self, @@ -362,11 +240,6 @@ def __init__( command: SLT[str], callback: Callable[[Update, CCT], RT], filters: BaseFilter = None, - pass_args: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): @@ -375,15 +248,9 @@ def __init__( self._commands: List[str] = [] super().__init__( - 'nocommand', + "nocommand", callback, filters=filters, - allow_edited=None, - pass_args=pass_args, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) diff --git a/telegram/ext/contexttypes.py b/telegram/ext/contexttypes.py index badf7331a7a..8a2e138933b 100644 --- a/telegram/ext/contexttypes.py +++ b/telegram/ext/contexttypes.py @@ -48,59 +48,59 @@ class ContextTypes(Generic[CCT, UD, CD, BD]): """ - __slots__ = ('_context', '_bot_data', '_chat_data', '_user_data') + __slots__ = ("_context", "_bot_data", "_chat_data", "_user_data") # overload signatures generated with https://git.io/JtJPj @overload def __init__( - self: 'ContextTypes[CallbackContext[Dict, Dict, Dict], Dict, Dict, Dict]', + self: "ContextTypes[CallbackContext[Dict, Dict, Dict], Dict, Dict, Dict]", ): ... @overload - def __init__(self: 'ContextTypes[CCT, Dict, Dict, Dict]', context: Type[CCT]): + def __init__(self: "ContextTypes[CCT, Dict, Dict, Dict]", context: Type[CCT]): ... @overload def __init__( - self: 'ContextTypes[CallbackContext[UD, Dict, Dict], UD, Dict, Dict]', user_data: Type[UD] + self: "ContextTypes[CallbackContext[UD, Dict, Dict], UD, Dict, Dict]", user_data: Type[UD] ): ... @overload def __init__( - self: 'ContextTypes[CallbackContext[Dict, CD, Dict], Dict, CD, Dict]', chat_data: Type[CD] + self: "ContextTypes[CallbackContext[Dict, CD, Dict], Dict, CD, Dict]", chat_data: Type[CD] ): ... @overload def __init__( - self: 'ContextTypes[CallbackContext[Dict, Dict, BD], Dict, Dict, BD]', bot_data: Type[BD] + self: "ContextTypes[CallbackContext[Dict, Dict, BD], Dict, Dict, BD]", bot_data: Type[BD] ): ... @overload def __init__( - self: 'ContextTypes[CCT, UD, Dict, Dict]', context: Type[CCT], user_data: Type[UD] + self: "ContextTypes[CCT, UD, Dict, Dict]", context: Type[CCT], user_data: Type[UD] ): ... @overload def __init__( - self: 'ContextTypes[CCT, Dict, CD, Dict]', context: Type[CCT], chat_data: Type[CD] + self: "ContextTypes[CCT, Dict, CD, Dict]", context: Type[CCT], chat_data: Type[CD] ): ... @overload def __init__( - self: 'ContextTypes[CCT, Dict, Dict, BD]', context: Type[CCT], bot_data: Type[BD] + self: "ContextTypes[CCT, Dict, Dict, BD]", context: Type[CCT], bot_data: Type[BD] ): ... @overload def __init__( - self: 'ContextTypes[CallbackContext[UD, CD, Dict], UD, CD, Dict]', + self: "ContextTypes[CallbackContext[UD, CD, Dict], UD, CD, Dict]", user_data: Type[UD], chat_data: Type[CD], ): @@ -108,7 +108,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CallbackContext[UD, Dict, BD], UD, Dict, BD]', + self: "ContextTypes[CallbackContext[UD, Dict, BD], UD, Dict, BD]", user_data: Type[UD], bot_data: Type[BD], ): @@ -116,7 +116,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CallbackContext[Dict, CD, BD], Dict, CD, BD]', + self: "ContextTypes[CallbackContext[Dict, CD, BD], Dict, CD, BD]", chat_data: Type[CD], bot_data: Type[BD], ): @@ -124,7 +124,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CCT, UD, CD, Dict]', + self: "ContextTypes[CCT, UD, CD, Dict]", context: Type[CCT], user_data: Type[UD], chat_data: Type[CD], @@ -133,7 +133,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CCT, UD, Dict, BD]', + self: "ContextTypes[CCT, UD, Dict, BD]", context: Type[CCT], user_data: Type[UD], bot_data: Type[BD], @@ -142,7 +142,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CCT, Dict, CD, BD]', + self: "ContextTypes[CCT, Dict, CD, BD]", context: Type[CCT], chat_data: Type[CD], bot_data: Type[BD], @@ -151,7 +151,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CallbackContext[UD, CD, BD], UD, CD, BD]', + self: "ContextTypes[CallbackContext[UD, CD, BD], UD, CD, BD]", user_data: Type[UD], chat_data: Type[CD], bot_data: Type[BD], @@ -160,7 +160,7 @@ def __init__( @overload def __init__( - self: 'ContextTypes[CCT, UD, CD, BD]', + self: "ContextTypes[CCT, UD, CD, BD]", context: Type[CCT], user_data: Type[UD], chat_data: Type[CD], @@ -176,7 +176,7 @@ def __init__( # type: ignore[no-untyped-def] user_data=dict, ): if not issubclass(context, CallbackContext): - raise ValueError('context must be a subclass of CallbackContext.') + raise ValueError("context must be a subclass of CallbackContext.") # We make all those only accessible via properties because we don't currently support # changing this at runtime, so overriding the attributes doesn't make sense diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index ba621fdeaa5..01e12dd45f4 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -46,15 +46,14 @@ class _ConversationTimeoutContext: - # '__dict__' is not included since this a private class - __slots__ = ('conversation_key', 'update', 'dispatcher', 'callback_context') + __slots__ = ("conversation_key", "update", "dispatcher", "callback_context") def __init__( self, conversation_key: Tuple[int, ...], update: Update, - dispatcher: 'Dispatcher', - callback_context: Optional[CallbackContext], + dispatcher: "Dispatcher", + callback_context: CallbackContext, ): self.conversation_key = conversation_key self.update = update @@ -187,23 +186,23 @@ class ConversationHandler(Handler[Update, CCT]): """ __slots__ = ( - '_entry_points', - '_states', - '_fallbacks', - '_allow_reentry', - '_per_user', - '_per_chat', - '_per_message', - '_conversation_timeout', - '_name', - 'persistent', - '_persistence', - '_map_to_parent', - 'timeout_jobs', - '_timeout_jobs_lock', - '_conversations', - '_conversations_lock', - 'logger', + "_entry_points", + "_states", + "_fallbacks", + "_allow_reentry", + "_per_user", + "_per_chat", + "_per_message", + "_conversation_timeout", + "_name", + "persistent", + "_persistence", + "_map_to_parent", + "timeout_jobs", + "_timeout_jobs_lock", + "_conversations", + "_conversations_lock", + "logger", ) END: ClassVar[int] = -1 @@ -249,7 +248,7 @@ def __init__( Set by dispatcher""" self._map_to_parent = map_to_parent - self.timeout_jobs: Dict[Tuple[int, ...], 'Job'] = {} + self.timeout_jobs: Dict[Tuple[int, ...], "Job"] = {} self._timeout_jobs_lock = Lock() self._conversations: ConversationDict = {} self._conversations_lock = Lock() @@ -322,7 +321,7 @@ def entry_points(self) -> List[Handler]: @entry_points.setter def entry_points(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to entry_points after initialization.') + raise ValueError("You can not assign a new value to entry_points after initialization.") @property def states(self) -> Dict[object, List[Handler]]: @@ -334,7 +333,7 @@ def states(self) -> Dict[object, List[Handler]]: @states.setter def states(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to states after initialization.') + raise ValueError("You can not assign a new value to states after initialization.") @property def fallbacks(self) -> List[Handler]: @@ -346,7 +345,7 @@ def fallbacks(self) -> List[Handler]: @fallbacks.setter def fallbacks(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to fallbacks after initialization.') + raise ValueError("You can not assign a new value to fallbacks after initialization.") @property def allow_reentry(self) -> bool: @@ -355,7 +354,7 @@ def allow_reentry(self) -> bool: @allow_reentry.setter def allow_reentry(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to allow_reentry after initialization.') + raise ValueError("You can not assign a new value to allow_reentry after initialization.") @property def per_user(self) -> bool: @@ -364,7 +363,7 @@ def per_user(self) -> bool: @per_user.setter def per_user(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to per_user after initialization.') + raise ValueError("You can not assign a new value to per_user after initialization.") @property def per_chat(self) -> bool: @@ -373,7 +372,7 @@ def per_chat(self) -> bool: @per_chat.setter def per_chat(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to per_chat after initialization.') + raise ValueError("You can not assign a new value to per_chat after initialization.") @property def per_message(self) -> bool: @@ -382,7 +381,7 @@ def per_message(self) -> bool: @per_message.setter def per_message(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to per_message after initialization.') + raise ValueError("You can not assign a new value to per_message after initialization.") @property def conversation_timeout( @@ -397,7 +396,7 @@ def 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 @@ -407,7 +406,7 @@ def name(self) -> Optional[str]: @name.setter def name(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to name after initialization.') + raise ValueError("You can not assign a new value to name after initialization.") @property def map_to_parent(self) -> Optional[Dict[object, object]]: @@ -419,7 +418,7 @@ def map_to_parent(self) -> Optional[Dict[object, object]]: @map_to_parent.setter def map_to_parent(self, value: object) -> NoReturn: - raise ValueError('You can not assign a new value to map_to_parent after initialization.') + raise ValueError("You can not assign a new value to map_to_parent after initialization.") @property def persistence(self) -> Optional[BasePersistence]: @@ -485,9 +484,9 @@ def _resolve_promise(self, state: Tuple) -> object: def _schedule_job( self, new_state: object, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", update: Update, - context: Optional[CallbackContext], + context: CallbackContext, conversation_key: Tuple[int, ...], ) -> None: if new_state != self.END: @@ -537,7 +536,7 @@ def check_update(self, update: object) -> CheckUpdateType: # pylint: disable=R0 # Resolve promises if isinstance(state, tuple) and len(state) == 2 and isinstance(state[1], Promise): - self.logger.debug('waiting for promise...') + self.logger.debug("waiting for promise...") # check if promise is finished or not if state[1].done.wait(0): @@ -555,7 +554,7 @@ def check_update(self, update: object) -> CheckUpdateType: # pylint: disable=R0 return key, hdlr, check return None - self.logger.debug('selecting conversation %s with state %s', str(key), str(state)) + self.logger.debug("selecting conversation %s with state %s", str(key), str(state)) handler = None @@ -597,9 +596,9 @@ def check_update(self, update: object) -> CheckUpdateType: # pylint: disable=R0 def handle_update( # type: ignore[override] self, update: Update, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: CheckUpdateType, - context: CallbackContext = None, + context: CallbackContext, ) -> Optional[object]: """Send the update to the callback for the current state and Handler @@ -608,11 +607,10 @@ def handle_update( # type: ignore[override] handler, and the handler's check result. update (:class:`telegram.Update`): Incoming telegram update. dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update. - context (:class:`telegram.ext.CallbackContext`, optional): The context as provided by + context (:class:`telegram.ext.CallbackContext`): The context as provided by the dispatcher. """ - update = cast(Update, update) # for mypy conversation_key, handler, check_result = check_result # type: ignore[assignment,misc] raise_dp_handler_stop = False @@ -691,15 +689,11 @@ def _update_state(self, new_state: object, key: Tuple[int, ...]) -> None: if self.persistent and self.persistence and self.name: self.persistence.update_conversation(self.name, key, new_state) - def _trigger_timeout(self, context: CallbackContext, job: 'Job' = None) -> None: - self.logger.debug('conversation timeout was triggered!') + def _trigger_timeout(self, context: CallbackContext) -> None: + self.logger.debug("conversation timeout was triggered!") - # Backward compatibility with bots that do not use CallbackContext - if isinstance(context, CallbackContext): - job = context.job - ctxt = cast(_ConversationTimeoutContext, job.context) # type: ignore[union-attr] - else: - ctxt = cast(_ConversationTimeoutContext, job.context) + job = cast("Job", context.job) + ctxt = cast(_ConversationTimeoutContext, job.context) callback_context = ctxt.callback_context @@ -718,8 +712,8 @@ def _trigger_timeout(self, context: CallbackContext, job: 'Job' = None) -> None: handler.handle_update(ctxt.update, ctxt.dispatcher, check, callback_context) except DispatcherHandlerStop: self.logger.warning( - 'DispatcherHandlerStop in TIMEOUT state of ' - 'ConversationHandler has no effect. Ignoring.' + "DispatcherHandlerStop in TIMEOUT state of " + "ConversationHandler has no effect. Ignoring." ) self._update_state(self.END, ctxt.conversation_key) diff --git a/telegram/ext/defaults.py b/telegram/ext/defaults.py index 8546f717536..de5b95af9d3 100644 --- a/telegram/ext/defaults.py +++ b/telegram/ext/defaults.py @@ -22,7 +22,6 @@ import pytz -from telegram.utils.deprecate import set_new_attribute_deprecated from telegram.utils.helpers import DEFAULT_NONE from telegram.utils.types import ODVInput @@ -58,16 +57,15 @@ class Defaults: """ __slots__ = ( - '_timeout', - '_tzinfo', - '_disable_web_page_preview', - '_run_async', - '_quote', - '_disable_notification', - '_allow_sending_without_reply', - '_parse_mode', - '_api_defaults', - '__dict__', + "_timeout", + "_tzinfo", + "_disable_web_page_preview", + "_run_async", + "_quote", + "_disable_notification", + "_allow_sending_without_reply", + "_parse_mode", + "_api_defaults", ) def __init__( @@ -95,21 +93,18 @@ def __init__( # Gather all defaults that actually have a default value self._api_defaults = {} for kwarg in ( - 'parse_mode', - 'explanation_parse_mode', - 'disable_notification', - 'disable_web_page_preview', - 'allow_sending_without_reply', + "parse_mode", + "explanation_parse_mode", + "disable_notification", + "disable_web_page_preview", + "allow_sending_without_reply", ): value = getattr(self, kwarg) if value not in [None, DEFAULT_NONE]: self._api_defaults[kwarg] = value # Special casing, as None is a valid default value if self._timeout != DEFAULT_NONE: - self._api_defaults['timeout'] = self._timeout - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) + self._api_defaults["timeout"] = self._timeout @property def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003 diff --git a/telegram/ext/dictpersistence.py b/telegram/ext/dictpersistence.py index 72c767d74fa..891350405d5 100644 --- a/telegram/ext/dictpersistence.py +++ b/telegram/ext/dictpersistence.py @@ -26,7 +26,7 @@ decode_user_chat_data_from_json, encode_conversations_to_json, ) -from telegram.ext import BasePersistence +from telegram.ext import BasePersistence, PersistenceInput from telegram.ext.utils.types import ConversationDict, CDCData try: @@ -53,17 +53,13 @@ class DictPersistence(BasePersistence): :meth:`telegram.ext.BasePersistence.replace_bot` and :meth:`telegram.ext.BasePersistence.insert_bot`. - Args: - store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this - persistence class. Default is :obj:`True`. - store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this - persistence class. Default is :obj:`True`. - store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this - persistence class. Default is :obj:`True`. - store_callback_data (:obj:`bool`, optional): Whether callback_data should be saved by this - persistence class. Default is :obj:`False`. + .. versionchanged:: 14.0 + The parameters and attributes ``store_*_data`` were replaced by :attr:`store_data`. - .. versionadded:: 13.6 + Args: + store_data (:class:`PersistenceInput`, optional): Specifies which kinds of data will be + saved by this persistence instance. By default, all available kinds of data will be + saved. user_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct user_data on creating this persistence. Default is ``""``. chat_data_json (:obj:`str`, optional): JSON string that will be used to reconstruct @@ -78,49 +74,33 @@ class DictPersistence(BasePersistence): conversation on creating this persistence. Default is ``""``. Attributes: - store_user_data (:obj:`bool`): Whether user_data should be saved by this - persistence class. - store_chat_data (:obj:`bool`): Whether chat_data should be saved by this - persistence class. - store_bot_data (:obj:`bool`): Whether bot_data should be saved by this - persistence class. - store_callback_data (:obj:`bool`): Whether callback_data be saved by this - persistence class. - - .. versionadded:: 13.6 + store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this + persistence instance. """ __slots__ = ( - '_user_data', - '_chat_data', - '_bot_data', - '_callback_data', - '_conversations', - '_user_data_json', - '_chat_data_json', - '_bot_data_json', - '_callback_data_json', - '_conversations_json', + "_user_data", + "_chat_data", + "_bot_data", + "_callback_data", + "_conversations", + "_user_data_json", + "_chat_data_json", + "_bot_data_json", + "_callback_data_json", + "_conversations_json", ) def __init__( self, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, - user_data_json: str = '', - chat_data_json: str = '', - bot_data_json: str = '', - conversations_json: str = '', - store_callback_data: bool = False, - callback_data_json: str = '', + store_data: PersistenceInput = None, + user_data_json: str = "", + chat_data_json: str = "", + bot_data_json: str = "", + conversations_json: str = "", + callback_data_json: str = "", ): - super().__init__( - store_user_data=store_user_data, - store_chat_data=store_chat_data, - store_bot_data=store_bot_data, - store_callback_data=store_callback_data, - ) + super().__init__(store_data=store_data) self._user_data = None self._chat_data = None self._bot_data = None @@ -402,3 +382,10 @@ def refresh_bot_data(self, bot_data: Dict) -> None: .. versionadded:: 13.6 .. seealso:: :meth:`telegram.ext.BasePersistence.refresh_bot_data` """ + + def flush(self) -> None: + """Does nothing. + + .. versionadded:: 14.0 + .. seealso:: :meth:`telegram.ext.BasePersistence.flush` + """ diff --git a/telegram/ext/dispatcher.py b/telegram/ext/dispatcher.py index 3322acfe5a0..0bf0468d2da 100644 --- a/telegram/ext/dispatcher.py +++ b/telegram/ext/dispatcher.py @@ -22,7 +22,6 @@ import warnings import weakref from collections import defaultdict -from functools import wraps from queue import Empty, Queue from threading import BoundedSemaphore, Event, Lock, Thread, current_thread from time import sleep @@ -44,11 +43,9 @@ from telegram import TelegramError, Update from telegram.ext import BasePersistence, ContextTypes -from telegram.ext.callbackcontext import CallbackContext from telegram.ext.handler import Handler import telegram.ext.extbot from telegram.ext.callbackdatacache import CallbackDataCache -from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated from telegram.ext.utils.promise import Promise from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE from telegram.ext.utils.types import CCT, UD, CD, BD @@ -56,44 +53,11 @@ if TYPE_CHECKING: from telegram import Bot from telegram.ext import JobQueue + from telegram.ext.callbackcontext import CallbackContext DEFAULT_GROUP: int = 0 -UT = TypeVar('UT') - - -def run_async( - func: Callable[[Update, CallbackContext], object] -) -> Callable[[Update, CallbackContext], object]: - """ - Function decorator that will run the function in a new thread. - - Will run :attr:`telegram.ext.Dispatcher.run_async`. - - Using this decorator is only possible when only a single Dispatcher exist in the system. - - Note: - DEPRECATED. Use :attr:`telegram.ext.Dispatcher.run_async` directly instead or the - :attr:`Handler.run_async` parameter. - - Warning: - If you're using ``@run_async`` you cannot rely on adding custom attributes to - :class:`telegram.ext.CallbackContext`. See its docs for more info. - """ - - @wraps(func) - def async_func(*args: object, **kwargs: object) -> object: - warnings.warn( - 'The @run_async decorator is deprecated. Use the `run_async` parameter of ' - 'your Handler or `Dispatcher.run_async` instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return Dispatcher.get_instance()._run_async( # pylint: disable=W0212 - func, *args, update=None, error_handling=False, **kwargs - ) - - return async_func +UT = TypeVar("UT") class DispatcherHandlerStop(Exception): @@ -116,7 +80,7 @@ def callback(update, context): state (:obj:`object`, optional): The next state of the conversation. """ - __slots__ = ('state',) + __slots__ = ("state",) def __init__(self, state: object = None) -> None: super().__init__() @@ -135,9 +99,6 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): ``@run_async`` decorator and :meth:`run_async`. Defaults to 4. persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to store data that should be persistent over restarts. - use_context (:obj:`bool`, optional): If set to :obj:`True` uses the context based callback - API (ignored if `dispatcher` argument is used). Defaults to :obj:`True`. - **New users**: set this to :obj:`True`. context_types (:class:`telegram.ext.ContextTypes`, optional): Pass an instance of :class:`telegram.ext.ContextTypes` to customize the types used in the ``context`` interface. If not passed, the defaults documented in @@ -166,27 +127,26 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): # Allowing '__weakref__' creation here since we need it for the singleton __slots__ = ( - 'workers', - 'persistence', - 'use_context', - 'update_queue', - 'job_queue', - 'user_data', - 'chat_data', - 'bot_data', - '_update_persistence_lock', - 'handlers', - 'groups', - 'error_handlers', - 'running', - '__stop_event', - '__exception_event', - '__async_queue', - '__async_threads', - 'bot', - '__dict__', - '__weakref__', - 'context_types', + "workers", + "persistence", + "update_queue", + "job_queue", + "user_data", + "chat_data", + "bot_data", + "_update_persistence_lock", + "handlers", + "groups", + "error_handlers", + "running", + "__stop_event", + "__exception_event", + "__async_queue", + "__async_threads", + "bot", + "__dict__", + "__weakref__", + "context_types", ) __singleton_lock = Lock() @@ -196,59 +156,48 @@ class Dispatcher(Generic[CCT, UD, CD, BD]): @overload def __init__( - self: 'Dispatcher[CallbackContext[Dict, Dict, Dict], Dict, Dict, Dict]', - bot: 'Bot', + self: "Dispatcher[CallbackContext[Dict, Dict, Dict], Dict, Dict, Dict]", + bot: "Bot", update_queue: Queue, workers: int = 4, exception_event: Event = None, - job_queue: 'JobQueue' = None, + job_queue: "JobQueue" = None, persistence: BasePersistence = None, - use_context: bool = True, ): ... @overload def __init__( - self: 'Dispatcher[CCT, UD, CD, BD]', - bot: 'Bot', + self: "Dispatcher[CCT, UD, CD, BD]", + bot: "Bot", update_queue: Queue, workers: int = 4, exception_event: Event = None, - job_queue: 'JobQueue' = None, + job_queue: "JobQueue" = None, persistence: BasePersistence = None, - use_context: bool = True, context_types: ContextTypes[CCT, UD, CD, BD] = None, ): ... def __init__( self, - bot: 'Bot', + bot: "Bot", update_queue: Queue, workers: int = 4, exception_event: Event = None, - job_queue: 'JobQueue' = None, + job_queue: "JobQueue" = None, persistence: BasePersistence = None, - use_context: bool = True, context_types: ContextTypes[CCT, UD, CD, BD] = None, ): self.bot = bot self.update_queue = update_queue self.job_queue = job_queue self.workers = workers - self.use_context = use_context self.context_types = cast(ContextTypes[CCT, UD, CD, BD], context_types or ContextTypes()) - if not use_context: - warnings.warn( - 'Old Handler API is deprecated - see https://git.io/fxJuV for details', - TelegramDeprecationWarning, - stacklevel=3, - ) - if self.workers < 1: warnings.warn( - 'Asynchronous callbacks can not be processed without at least one worker thread.' + "Asynchronous callbacks can not be processed without at least one worker thread." ) self.user_data: DefaultDict[int, UD] = defaultdict(self.context_types.user_data) @@ -261,26 +210,26 @@ def __init__( raise TypeError("persistence must be based on telegram.ext.BasePersistence") self.persistence = persistence self.persistence.set_bot(self.bot) - if self.persistence.store_user_data: + if self.persistence.store_data.user_data: self.user_data = self.persistence.get_user_data() if not isinstance(self.user_data, defaultdict): raise ValueError("user_data must be of type defaultdict") - if self.persistence.store_chat_data: + if self.persistence.store_data.chat_data: self.chat_data = self.persistence.get_chat_data() if not isinstance(self.chat_data, defaultdict): raise ValueError("chat_data must be of type defaultdict") - if self.persistence.store_bot_data: + if self.persistence.store_data.bot_data: self.bot_data = self.persistence.get_bot_data() if not isinstance(self.bot_data, self.context_types.bot_data): raise ValueError( f"bot_data must be of type {self.context_types.bot_data.__name__}" ) - if self.persistence.store_callback_data: + if self.persistence.store_data.callback_data: self.bot = cast(telegram.ext.extbot.ExtBot, self.bot) persistent_data = self.persistence.get_callback_data() if persistent_data is not None: if not isinstance(persistent_data, tuple) and len(persistent_data) != 2: - raise ValueError('callback_data must be a 2-tuple') + raise ValueError("callback_data must be a 2-tuple") self.bot.callback_data_cache = CallbackDataCache( self.bot, self.bot.callback_data_cache.maxsize, @@ -312,36 +261,25 @@ def __init__( else: self._set_singleton(None) - def __setattr__(self, key: str, value: object) -> None: - # Mangled names don't automatically apply in __setattr__ (see - # https://docs.python.org/3/tutorial/classes.html#private-variables), so we have to make - # it mangled so they don't raise TelegramDeprecationWarning unnecessarily - if key.startswith('__'): - key = f"_{self.__class__.__name__}{key}" - if issubclass(self.__class__, Dispatcher) and self.__class__ is not Dispatcher: - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - @property def exception_event(self) -> Event: # skipcq: PY-D0003 return self.__exception_event def _init_async_threads(self, base_name: str, workers: int) -> None: - base_name = f'{base_name}_' if base_name else '' + base_name = f"{base_name}_" if base_name else "" for i in range(workers): - thread = Thread(target=self._pooled, name=f'Bot:{self.bot.id}:worker:{base_name}{i}') + thread = Thread(target=self._pooled, name=f"Bot:{self.bot.id}:worker:{base_name}{i}") self.__async_threads.add(thread) thread.start() @classmethod - def _set_singleton(cls, val: Optional['Dispatcher']) -> None: - cls.logger.debug('Setting singleton dispatcher as %s', val) + def _set_singleton(cls, val: Optional["Dispatcher"]) -> None: + cls.logger.debug("Setting singleton dispatcher as %s", val) cls.__singleton = weakref.ref(val) if val else None @classmethod - def get_instance(cls) -> 'Dispatcher': + def get_instance(cls) -> "Dispatcher": """Get the singleton instance of this class. Returns: @@ -353,7 +291,7 @@ def get_instance(cls) -> 'Dispatcher': """ if cls.__singleton is not None: return cls.__singleton() # type: ignore[return-value] # pylint: disable=not-callable - raise RuntimeError(f'{cls.__name__} not initialized or multiple instances exist') + raise RuntimeError(f"{cls.__name__} not initialized or multiple instances exist") def _pooled(self) -> None: thr_name = current_thread().getName() @@ -375,21 +313,14 @@ def _pooled(self) -> None: if isinstance(promise.exception, DispatcherHandlerStop): self.logger.warning( - 'DispatcherHandlerStop is not supported with async functions; func: %s', + "DispatcherHandlerStop is not supported with async functions; func: %s", promise.pooled_function.__name__, ) continue # Avoid infinite recursion of error handlers. if promise.pooled_function in self.error_handlers: - self.logger.error('An uncaught error was raised while handling the error.') - continue - - # Don't perform error handling for a `Promise` with deactivated error handling. This - # should happen only via the deprecated `@run_async` decorator or `Promises` created - # within error handlers - if not promise.error_handling: - self.logger.error('A promise with deactivated error handling raised an error.') + self.logger.error("An uncaught error was raised while handling the error.") continue # If we arrive here, an exception happened in the promise and was neither @@ -397,7 +328,7 @@ def _pooled(self) -> None: try: self.dispatch_error(promise.update, promise.exception, promise=promise) except Exception: - self.logger.exception('An uncaught error was raised while handling the error.') + self.logger.exception("An uncaught error was raised while handling the error.") def run_async( self, func: Callable[..., object], *args: object, update: object = None, **kwargs: object @@ -425,18 +356,7 @@ def run_async( Promise """ - return self._run_async(func, *args, update=update, error_handling=True, **kwargs) - - def _run_async( - self, - func: Callable[..., object], - *args: object, - update: object = None, - error_handling: bool = True, - **kwargs: object, - ) -> Promise: - # TODO: Remove error_handling parameter once we drop the @run_async decorator - promise = Promise(func, args, kwargs, update=update, error_handling=error_handling) + promise = Promise(func, args, kwargs, update=update) self.__async_queue.put(promise) return promise @@ -451,19 +371,19 @@ def start(self, ready: Event = None) -> None: """ if self.running: - self.logger.warning('already running') + self.logger.warning("already running") if ready is not None: ready.set() return if self.__exception_event.is_set(): - msg = 'reusing dispatcher after exception event is forbidden' + msg = "reusing dispatcher after exception event is forbidden" self.logger.error(msg) raise TelegramError(msg) self._init_async_threads(str(uuid4()), self.workers) self.running = True - self.logger.debug('Dispatcher started') + self.logger.debug("Dispatcher started") if ready is not None: ready.set() @@ -474,19 +394,19 @@ def start(self, ready: Event = None) -> None: update = self.update_queue.get(True, 1) except Empty: if self.__stop_event.is_set(): - self.logger.debug('orderly stopping') + self.logger.debug("orderly stopping") break if self.__exception_event.is_set(): - self.logger.critical('stopping due to exception in another thread') + self.logger.critical("stopping due to exception in another thread") break continue - self.logger.debug('Processing Update: %s', update) + self.logger.debug("Processing Update: %s", update) self.process_update(update) self.update_queue.task_done() self.running = False - self.logger.debug('Dispatcher thread stopped') + self.logger.debug("Dispatcher thread stopped") def stop(self) -> None: """Stops the thread.""" @@ -506,10 +426,10 @@ def stop(self) -> None: self.__async_queue.put(None) for i, thr in enumerate(threads): - self.logger.debug('Waiting for async thread %s/%s to end', i + 1, total) + self.logger.debug("Waiting for async thread %s/%s to end", i + 1, total) thr.join() self.__async_threads.remove(thr) - self.logger.debug('async thread %s/%s has ended', i + 1, total) + self.logger.debug("async thread %s/%s has ended", i + 1, total) @property def has_running_threads(self) -> bool: # skipcq: PY-D0003 @@ -535,7 +455,7 @@ def process_update(self, update: object) -> None: try: self.dispatch_error(None, update) except Exception: - self.logger.exception('An uncaught error was raised while handling the error.') + self.logger.exception("An uncaught error was raised while handling the error.") return context = None @@ -547,7 +467,7 @@ def process_update(self, update: object) -> None: for handler in self.handlers[group]: check = handler.check_update(update) if check is not None and check is not False: - if not context and self.use_context: + if not context: context = self.context_types.context.from_update(update, self) context.refresh_data() handled = True @@ -557,7 +477,7 @@ def process_update(self, update: object) -> None: # Stop processing with any other handler. except DispatcherHandlerStop: - self.logger.debug('Stopping further handlers due to DispatcherHandlerStop') + self.logger.debug("Stopping further handlers due to DispatcherHandlerStop") self.update_persistence(update=update) break @@ -566,11 +486,11 @@ def process_update(self, update: object) -> None: try: self.dispatch_error(update, exc) except DispatcherHandlerStop: - self.logger.debug('Error handler stopped further handlers') + self.logger.debug("Error handler stopped further handlers") break # Errors should not stop the thread. except Exception: - self.logger.exception('An uncaught error was raised while handling the error.') + self.logger.exception("An uncaught error was raised while handling the error.") # Update persistence, if handled handled_only_async = all(sync_modes) @@ -611,9 +531,9 @@ def add_handler(self, handler: Handler[UT, CCT], group: int = DEFAULT_GROUP) -> from .conversationhandler import ConversationHandler # pylint: disable=C0415 if not isinstance(handler, Handler): - raise TypeError(f'handler is not an instance of {Handler.__name__}') + raise TypeError(f"handler is not an instance of {Handler.__name__}") if not isinstance(group, int): - raise TypeError('group is not int') + raise TypeError("group is not int") # For some reason MyPy infers the type of handler is here, # so for now we just ignore all the errors if ( @@ -679,7 +599,7 @@ def __update_persistence(self, update: object = None) -> None: else: user_ids = [] - if self.persistence.store_callback_data: + if self.persistence.store_data.callback_data: self.bot = cast(telegram.ext.extbot.ExtBot, self.bot) try: self.persistence.update_callback_data( @@ -690,12 +610,12 @@ def __update_persistence(self, update: object = None) -> None: self.dispatch_error(update, exc) except Exception: message = ( - 'Saving callback data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' + "Saving callback data raised an error and an " + "uncaught error was raised while handling " + "the error with an error_handler" ) self.logger.exception(message) - if self.persistence.store_bot_data: + if self.persistence.store_data.bot_data: try: self.persistence.update_bot_data(self.bot_data) except Exception as exc: @@ -703,12 +623,12 @@ def __update_persistence(self, update: object = None) -> None: self.dispatch_error(update, exc) except Exception: message = ( - 'Saving bot data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' + "Saving bot data raised an error and an " + "uncaught error was raised while handling " + "the error with an error_handler" ) self.logger.exception(message) - if self.persistence.store_chat_data: + if self.persistence.store_data.chat_data: for chat_id in chat_ids: try: self.persistence.update_chat_data(chat_id, self.chat_data[chat_id]) @@ -717,12 +637,12 @@ def __update_persistence(self, update: object = None) -> None: self.dispatch_error(update, exc) except Exception: message = ( - 'Saving chat data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' + "Saving chat data raised an error and an " + "uncaught error was raised while handling " + "the error with an error_handler" ) self.logger.exception(message) - if self.persistence.store_user_data: + if self.persistence.store_data.user_data: for user_id in user_ids: try: self.persistence.update_user_data(user_id, self.user_data[user_id]) @@ -731,9 +651,9 @@ def __update_persistence(self, update: object = None) -> None: self.dispatch_error(update, exc) except Exception: message = ( - 'Saving user data raised an error and an ' - 'uncaught error was raised while handling ' - 'the error with an error_handler' + "Saving user data raised an error and an " + "uncaught error was raised while handling " + "the error with an error_handler" ) self.logger.exception(message) @@ -754,19 +674,15 @@ def add_error_handler( Args: callback (:obj:`callable`): The callback function for this error handler. Will be - called when an error is raised. Callback signature for context based API: - - ``def callback(update: object, context: CallbackContext)`` + called when an error is raised. + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The error that happened will be present in context.error. run_async (:obj:`bool`, optional): Whether this handlers callback should be run asynchronously using :meth:`run_async`. Defaults to :obj:`False`. - - Note: - See https://git.io/fxJuV for more info about switching to context based API. """ if callback in self.error_handlers: - self.logger.debug('The callback is already registered as an error handler. Ignoring.') + self.logger.debug("The callback is already registered as an error handler. Ignoring.") return if run_async is DEFAULT_FALSE and self.bot.defaults and self.bot.defaults.run_async: @@ -800,21 +716,15 @@ def dispatch_error( if self.error_handlers: for callback, run_async in self.error_handlers.items(): # pylint: disable=W0621 - if self.use_context: - context = self.context_types.context.from_error( - update, error, self, async_args=async_args, async_kwargs=async_kwargs - ) - if run_async: - self.run_async(callback, update, context, update=update) - else: - callback(update, context) + context = self.context_types.context.from_error( + update, error, self, async_args=async_args, async_kwargs=async_kwargs + ) + if run_async: + self.run_async(callback, update, context, update=update) else: - if run_async: - self.run_async(callback, self.bot, update, error, update=update) - else: - callback(self.bot, update, error) + callback(update, context) else: self.logger.exception( - 'No error handlers are registered, logging exception.', exc_info=error + "No error handlers are registered, logging exception.", exc_info=error ) diff --git a/telegram/ext/extbot.py b/telegram/ext/extbot.py index 842b8e4e11d..1d7f17615de 100644 --- a/telegram/ext/extbot.py +++ b/telegram/ext/extbot.py @@ -42,7 +42,7 @@ from telegram.utils.request import Request from .defaults import Defaults -HandledTypes = TypeVar('HandledTypes', bound=Union[Message, CallbackQuery, Chat]) +HandledTypes = TypeVar("HandledTypes", bound=Union[Message, CallbackQuery, Chat]) class ExtBot(telegram.bot.Bot): @@ -73,25 +73,17 @@ class ExtBot(telegram.bot.Bot): """ - __slots__ = ('arbitrary_callback_data', 'callback_data_cache') - - # The ext_bot argument is a little hack to get warnings handled correctly. - # It's not very clean, but the warnings will be dropped at some point anyway. - def __setattr__(self, key: str, value: object, ext_bot: bool = True) -> None: - if issubclass(self.__class__, ExtBot) and self.__class__ is not ExtBot: - object.__setattr__(self, key, value) - return - super().__setattr__(key, value, ext_bot=ext_bot) # type: ignore[call-arg] + __slots__ = ("arbitrary_callback_data", "callback_data_cache") def __init__( self, token: str, base_url: str = None, base_file_url: str = None, - request: 'Request' = None, + request: "Request" = None, private_key: bytes = None, private_key_password: bytes = None, - defaults: 'Defaults' = None, + defaults: "Defaults" = None, arbitrary_callback_data: Union[bool, int] = False, ): super().__init__( @@ -236,11 +228,11 @@ def get_updates( def _effective_inline_results( # pylint: disable=R0201 self, results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] ], next_offset: str = None, current_offset: str = None, - ) -> Tuple[Sequence['InlineQueryResult'], Optional[str]]: + ) -> Tuple[Sequence["InlineQueryResult"], Optional[str]]: """ This method is called by Bot.answer_inline_query to build the actual results list. Overriding this to call self._replace_keyboard suffices @@ -256,14 +248,14 @@ def _effective_inline_results( # pylint: disable=R0201 for result in effective_results: # All currently existingInlineQueryResults have a reply_markup, but future ones # might not have. Better be save than sorry - if not hasattr(result, 'reply_markup'): + if not hasattr(result, "reply_markup"): results.append(result) else: # We build a new result in case the user wants to use the same object in # different places new_result = copy(result) markup = self._replace_keyboard(result.reply_markup) # type: ignore[attr-defined] - new_result.reply_markup = markup + new_result.reply_markup = markup # type: ignore[attr-defined] results.append(new_result) return results, next_offset @@ -292,7 +284,7 @@ def copy_message( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 72a4b30f22a..301802242a4 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -20,10 +20,8 @@ """This module contains the Filters for use with the MessageHandler class.""" import re -import warnings from abc import ABC, abstractmethod -from sys import version_info as py_ver from threading import Lock from typing import ( Dict, @@ -42,16 +40,15 @@ from telegram import Chat, Message, MessageEntity, Update, User __all__ = [ - 'Filters', - 'BaseFilter', - 'MessageFilter', - 'UpdateFilter', - 'InvertedFilter', - 'MergedFilter', - 'XORFilter', + "Filters", + "BaseFilter", + "MessageFilter", + "UpdateFilter", + "InvertedFilter", + "MergedFilter", + "XORFilter", ] -from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated from telegram.utils.types import SLT DataDict = Dict[str, list] @@ -113,12 +110,10 @@ class variable. (depends on the handler). """ - if py_ver < (3, 7): - __slots__ = ('_name', '_data_filter') - else: - __slots__ = ('_name', '_data_filter', '__dict__') # type: ignore[assignment] + __slots__ = ("_name", "_data_filter") - def __new__(cls, *args: object, **kwargs: object) -> 'BaseFilter': # pylint: disable=W0613 + def __new__(cls, *args: object, **kwargs: object) -> "BaseFilter": # pylint: disable=W0613 + # We do this here instead of in a __init__ so filter don't have to call __init__ or super() instance = super().__new__(cls) instance._name = None instance._data_filter = False @@ -129,30 +124,18 @@ def __new__(cls, *args: object, **kwargs: object) -> 'BaseFilter': # pylint: di def __call__(self, update: Update) -> Optional[Union[bool, DataDict]]: ... - def __and__(self, other: 'BaseFilter') -> 'BaseFilter': + def __and__(self, other: "BaseFilter") -> "BaseFilter": return MergedFilter(self, and_filter=other) - def __or__(self, other: 'BaseFilter') -> 'BaseFilter': + def __or__(self, other: "BaseFilter") -> "BaseFilter": return MergedFilter(self, or_filter=other) - def __xor__(self, other: 'BaseFilter') -> 'BaseFilter': + def __xor__(self, other: "BaseFilter") -> "BaseFilter": return XORFilter(self, other) - def __invert__(self) -> 'BaseFilter': + def __invert__(self) -> "BaseFilter": return InvertedFilter(self) - def __setattr__(self, key: str, value: object) -> None: - # Allow setting custom attributes w/o warning for user defined custom filters. - # To differentiate between a custom and a PTB filter, we use this hacky but - # simple way of checking the module name where the class is defined from. - if ( - issubclass(self.__class__, (UpdateFilter, MessageFilter)) - and self.__class__.__module__ != __name__ - ): # __name__ is telegram.ext.filters - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - @property def data_filter(self) -> bool: return self._data_filter @@ -253,7 +236,7 @@ class InvertedFilter(UpdateFilter): """ - __slots__ = ('f',) + __slots__ = ("f",) def __init__(self, f: BaseFilter): self.f = f @@ -267,7 +250,7 @@ def name(self) -> str: @name.setter def name(self, name: str) -> NoReturn: - raise RuntimeError('Cannot set name for InvertedFilter') + raise RuntimeError("Cannot set name for InvertedFilter") class MergedFilter(UpdateFilter): @@ -280,7 +263,7 @@ class MergedFilter(UpdateFilter): """ - __slots__ = ('base_filter', 'and_filter', 'or_filter') + __slots__ = ("base_filter", "and_filter", "or_filter") def __init__( self, base_filter: BaseFilter, and_filter: BaseFilter = None, or_filter: BaseFilter = None @@ -353,7 +336,7 @@ def name(self) -> str: @name.setter def name(self, name: str) -> NoReturn: - raise RuntimeError('Cannot set name for MergedFilter') + raise RuntimeError("Cannot set name for MergedFilter") class XORFilter(UpdateFilter): @@ -366,7 +349,7 @@ class XORFilter(UpdateFilter): """ - __slots__ = ('base_filter', 'xor_filter', 'merged_filter') + __slots__ = ("base_filter", "xor_filter", "merged_filter") def __init__(self, base_filter: BaseFilter, xor_filter: BaseFilter): self.base_filter = base_filter @@ -378,22 +361,22 @@ def filter(self, update: Update) -> Optional[Union[bool, DataDict]]: @property def name(self) -> str: - return f'<{self.base_filter} xor {self.xor_filter}>' + return f"<{self.base_filter} xor {self.xor_filter}>" @name.setter def name(self, name: str) -> NoReturn: - raise RuntimeError('Cannot set name for XORFilter') + raise RuntimeError("Cannot set name for XORFilter") class _DiceEmoji(MessageFilter): - __slots__ = ('emoji',) + __slots__ = ("emoji",) def __init__(self, emoji: str = None, name: str = None): - self.name = f'Filters.dice.{name}' if name else 'Filters.dice' + self.name = f"Filters.dice.{name}" if name else "Filters.dice" self.emoji = emoji class _DiceValues(MessageFilter): - __slots__ = ('values', 'emoji') + __slots__ = ("values", "emoji") def __init__( self, @@ -403,7 +386,7 @@ def __init__( ): self.values = [values] if isinstance(values, int) else values self.emoji = emoji - self.name = f'{name}({values})' + self.name = f"{name}({values})" def filter(self, message: Message) -> bool: if message.dice and message.dice.value in self.values: @@ -414,7 +397,7 @@ def filter(self, message: Message) -> bool: def __call__( # type: ignore[override] self, update: Union[Update, List[int], Tuple[int]] - ) -> Union[bool, '_DiceValues']: + ) -> Union[bool, "_DiceValues"]: if isinstance(update, Update): return self.filter(update.effective_message) return self._DiceValues(update, self.name, emoji=self.emoji) @@ -437,14 +420,11 @@ class Filters: """ - __slots__ = ('__dict__',) - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) + __slots__ = () class _All(MessageFilter): __slots__ = () - name = 'Filters.all' + name = "Filters.all" def filter(self, message: Message) -> bool: return True @@ -454,14 +434,14 @@ def filter(self, message: Message) -> bool: class _Text(MessageFilter): __slots__ = () - name = 'Filters.text' + name = "Filters.text" class _TextStrings(MessageFilter): - __slots__ = ('strings',) + __slots__ = ("strings",) def __init__(self, strings: Union[List[str], Tuple[str]]): self.strings = strings - self.name = f'Filters.text({strings})' + self.name = f"Filters.text({strings})" def filter(self, message: Message) -> bool: if message.text: @@ -470,7 +450,7 @@ def filter(self, message: Message) -> bool: def __call__( # type: ignore[override] self, update: Union[Update, List[str], Tuple[str]] - ) -> Union[bool, '_TextStrings']: + ) -> Union[bool, "_TextStrings"]: if isinstance(update, Update): return self.filter(update.effective_message) return self._TextStrings(update) @@ -508,14 +488,14 @@ def filter(self, message: Message) -> bool: class _Caption(MessageFilter): __slots__ = () - name = 'Filters.caption' + name = "Filters.caption" class _CaptionStrings(MessageFilter): - __slots__ = ('strings',) + __slots__ = ("strings",) def __init__(self, strings: Union[List[str], Tuple[str]]): self.strings = strings - self.name = f'Filters.caption({strings})' + self.name = f"Filters.caption({strings})" def filter(self, message: Message) -> bool: if message.caption: @@ -524,7 +504,7 @@ def filter(self, message: Message) -> bool: def __call__( # type: ignore[override] self, update: Union[Update, List[str], Tuple[str]] - ) -> Union[bool, '_CaptionStrings']: + ) -> Union[bool, "_CaptionStrings"]: if isinstance(update, Update): return self.filter(update.effective_message) return self._CaptionStrings(update) @@ -546,14 +526,14 @@ def filter(self, message: Message) -> bool: class _Command(MessageFilter): __slots__ = () - name = 'Filters.command' + name = "Filters.command" class _CommandOnlyStart(MessageFilter): - __slots__ = ('only_start',) + __slots__ = ("only_start",) def __init__(self, only_start: bool): self.only_start = only_start - self.name = f'Filters.command({only_start})' + self.name = f"Filters.command({only_start})" def filter(self, message: Message) -> bool: return bool( @@ -563,7 +543,7 @@ def filter(self, message: Message) -> bool: def __call__( # type: ignore[override] self, update: Union[bool, Update] - ) -> Union[bool, '_CommandOnlyStart']: + ) -> Union[bool, "_CommandOnlyStart"]: if isinstance(update, Update): return self.filter(update.effective_message) return self._CommandOnlyStart(update) @@ -623,7 +603,7 @@ class regex(MessageFilter): pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. """ - __slots__ = ('pattern',) + __slots__ = ("pattern",) data_filter = True def __init__(self, pattern: Union[str, Pattern]): @@ -631,14 +611,14 @@ def __init__(self, pattern: Union[str, Pattern]): pattern = re.compile(pattern) pattern = cast(Pattern, pattern) self.pattern: Pattern = pattern - self.name = f'Filters.regex({self.pattern})' + self.name = f"Filters.regex({self.pattern})" def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]: """""" # remove method from docs if message.text: match = self.pattern.search(message.text) if match: - return {'matches': [match]} + return {"matches": [match]} return {} class caption_regex(MessageFilter): @@ -659,7 +639,7 @@ class caption_regex(MessageFilter): pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. """ - __slots__ = ('pattern',) + __slots__ = ("pattern",) data_filter = True def __init__(self, pattern: Union[str, Pattern]): @@ -667,19 +647,19 @@ def __init__(self, pattern: Union[str, Pattern]): pattern = re.compile(pattern) pattern = cast(Pattern, pattern) self.pattern: Pattern = pattern - self.name = f'Filters.caption_regex({self.pattern})' + self.name = f"Filters.caption_regex({self.pattern})" def filter(self, message: Message) -> Optional[Dict[str, List[Match]]]: """""" # remove method from docs if message.caption: match = self.pattern.search(message.caption) if match: - return {'matches': [match]} + return {"matches": [match]} return {} class _Reply(MessageFilter): __slots__ = () - name = 'Filters.reply' + name = "Filters.reply" def filter(self, message: Message) -> bool: return bool(message.reply_to_message) @@ -689,7 +669,7 @@ def filter(self, message: Message) -> bool: class _Audio(MessageFilter): __slots__ = () - name = 'Filters.audio' + name = "Filters.audio" def filter(self, message: Message) -> bool: return bool(message.audio) @@ -699,7 +679,7 @@ def filter(self, message: Message) -> bool: class _Document(MessageFilter): __slots__ = () - name = 'Filters.document' + name = "Filters.document" class category(MessageFilter): """Filters documents by their category in the mime-type attribute. @@ -715,7 +695,7 @@ class category(MessageFilter): of audio sent as file, for example 'audio/mpeg' or 'audio/x-wav'. """ - __slots__ = ('_category',) + __slots__ = ("_category",) def __init__(self, category: Optional[str]): """Initialize the category you want to filter @@ -732,11 +712,11 @@ def filter(self, message: Message) -> bool: return message.document.mime_type.startswith(self._category) return False - application = category('application/') - audio = category('audio/') - image = category('image/') - video = category('video/') - text = category('text/') + application = category("application/") + audio = category("audio/") + image = category("image/") + video = category("video/") + text = category("text/") class mime_type(MessageFilter): """This Filter filters documents by their mime-type attribute @@ -751,7 +731,7 @@ class mime_type(MessageFilter): ``Filters.document.mime_type('audio/mpeg')`` filters all audio in mp3 format. """ - __slots__ = ('mimetype',) + __slots__ = ("mimetype",) def __init__(self, mimetype: Optional[str]): self.mimetype = mimetype @@ -763,21 +743,21 @@ def filter(self, message: Message) -> bool: return message.document.mime_type == self.mimetype return False - apk = mime_type('application/vnd.android.package-archive') - doc = mime_type('application/msword') - docx = mime_type('application/vnd.openxmlformats-officedocument.wordprocessingml.document') - exe = mime_type('application/x-ms-dos-executable') - gif = mime_type('video/mp4') - jpg = mime_type('image/jpeg') - mp3 = mime_type('audio/mpeg') - pdf = mime_type('application/pdf') - py = mime_type('text/x-python') - svg = mime_type('image/svg+xml') - txt = mime_type('text/plain') - targz = mime_type('application/x-compressed-tar') - wav = mime_type('audio/x-wav') - xml = mime_type('application/xml') - zip = mime_type('application/zip') + apk = mime_type("application/vnd.android.package-archive") + doc = mime_type("application/msword") + docx = mime_type("application/vnd.openxmlformats-officedocument.wordprocessingml.document") + exe = mime_type("application/x-ms-dos-executable") + gif = mime_type("video/mp4") + jpg = mime_type("image/jpeg") + mp3 = mime_type("audio/mpeg") + pdf = mime_type("application/pdf") + py = mime_type("text/x-python") + svg = mime_type("image/svg+xml") + txt = mime_type("text/plain") + targz = mime_type("application/x-compressed-tar") + wav = mime_type("audio/x-wav") + xml = mime_type("application/xml") + zip = mime_type("application/zip") class file_extension(MessageFilter): """This filter filters documents by their file ending/extension. @@ -805,7 +785,7 @@ class file_extension(MessageFilter): filters files without a dot in the filename. """ - __slots__ = ('_file_extension', 'is_case_sensitive') + __slots__ = ("_file_extension", "is_case_sensitive") def __init__(self, file_extension: Optional[str], case_sensitive: bool = False): """Initialize the extension you want to filter. @@ -926,7 +906,7 @@ def filter(self, message: Message) -> bool: class _Animation(MessageFilter): __slots__ = () - name = 'Filters.animation' + name = "Filters.animation" def filter(self, message: Message) -> bool: return bool(message.animation) @@ -936,7 +916,7 @@ def filter(self, message: Message) -> bool: class _Photo(MessageFilter): __slots__ = () - name = 'Filters.photo' + name = "Filters.photo" def filter(self, message: Message) -> bool: return bool(message.photo) @@ -946,7 +926,7 @@ def filter(self, message: Message) -> bool: class _Sticker(MessageFilter): __slots__ = () - name = 'Filters.sticker' + name = "Filters.sticker" def filter(self, message: Message) -> bool: return bool(message.sticker) @@ -956,7 +936,7 @@ def filter(self, message: Message) -> bool: class _Video(MessageFilter): __slots__ = () - name = 'Filters.video' + name = "Filters.video" def filter(self, message: Message) -> bool: return bool(message.video) @@ -966,7 +946,7 @@ def filter(self, message: Message) -> bool: class _Voice(MessageFilter): __slots__ = () - name = 'Filters.voice' + name = "Filters.voice" def filter(self, message: Message) -> bool: return bool(message.voice) @@ -976,7 +956,7 @@ def filter(self, message: Message) -> bool: class _VideoNote(MessageFilter): __slots__ = () - name = 'Filters.video_note' + name = "Filters.video_note" def filter(self, message: Message) -> bool: return bool(message.video_note) @@ -986,7 +966,7 @@ def filter(self, message: Message) -> bool: class _Contact(MessageFilter): __slots__ = () - name = 'Filters.contact' + name = "Filters.contact" def filter(self, message: Message) -> bool: return bool(message.contact) @@ -996,7 +976,7 @@ def filter(self, message: Message) -> bool: class _Location(MessageFilter): __slots__ = () - name = 'Filters.location' + name = "Filters.location" def filter(self, message: Message) -> bool: return bool(message.location) @@ -1006,7 +986,7 @@ def filter(self, message: Message) -> bool: class _Venue(MessageFilter): __slots__ = () - name = 'Filters.venue' + name = "Filters.venue" def filter(self, message: Message) -> bool: return bool(message.venue) @@ -1027,7 +1007,7 @@ class _StatusUpdate(UpdateFilter): class _NewChatMembers(MessageFilter): __slots__ = () - name = 'Filters.status_update.new_chat_members' + name = "Filters.status_update.new_chat_members" def filter(self, message: Message) -> bool: return bool(message.new_chat_members) @@ -1037,7 +1017,7 @@ def filter(self, message: Message) -> bool: class _LeftChatMember(MessageFilter): __slots__ = () - name = 'Filters.status_update.left_chat_member' + name = "Filters.status_update.left_chat_member" def filter(self, message: Message) -> bool: return bool(message.left_chat_member) @@ -1047,7 +1027,7 @@ def filter(self, message: Message) -> bool: class _NewChatTitle(MessageFilter): __slots__ = () - name = 'Filters.status_update.new_chat_title' + name = "Filters.status_update.new_chat_title" def filter(self, message: Message) -> bool: return bool(message.new_chat_title) @@ -1057,7 +1037,7 @@ def filter(self, message: Message) -> bool: class _NewChatPhoto(MessageFilter): __slots__ = () - name = 'Filters.status_update.new_chat_photo' + name = "Filters.status_update.new_chat_photo" def filter(self, message: Message) -> bool: return bool(message.new_chat_photo) @@ -1067,7 +1047,7 @@ def filter(self, message: Message) -> bool: class _DeleteChatPhoto(MessageFilter): __slots__ = () - name = 'Filters.status_update.delete_chat_photo' + name = "Filters.status_update.delete_chat_photo" def filter(self, message: Message) -> bool: return bool(message.delete_chat_photo) @@ -1077,7 +1057,7 @@ def filter(self, message: Message) -> bool: class _ChatCreated(MessageFilter): __slots__ = () - name = 'Filters.status_update.chat_created' + name = "Filters.status_update.chat_created" def filter(self, message: Message) -> bool: return bool( @@ -1093,7 +1073,7 @@ def filter(self, message: Message) -> bool: class _MessageAutoDeleteTimerChanged(MessageFilter): __slots__ = () - name = 'MessageAutoDeleteTimerChanged' + name = "MessageAutoDeleteTimerChanged" def filter(self, message: Message) -> bool: return bool(message.message_auto_delete_timer_changed) @@ -1103,7 +1083,7 @@ def filter(self, message: Message) -> bool: class _Migrate(MessageFilter): __slots__ = () - name = 'Filters.status_update.migrate' + name = "Filters.status_update.migrate" def filter(self, message: Message) -> bool: return bool(message.migrate_from_chat_id or message.migrate_to_chat_id) @@ -1114,7 +1094,7 @@ def filter(self, message: Message) -> bool: class _PinnedMessage(MessageFilter): __slots__ = () - name = 'Filters.status_update.pinned_message' + name = "Filters.status_update.pinned_message" def filter(self, message: Message) -> bool: return bool(message.pinned_message) @@ -1124,7 +1104,7 @@ def filter(self, message: Message) -> bool: class _ConnectedWebsite(MessageFilter): __slots__ = () - name = 'Filters.status_update.connected_website' + name = "Filters.status_update.connected_website" def filter(self, message: Message) -> bool: return bool(message.connected_website) @@ -1134,7 +1114,7 @@ def filter(self, message: Message) -> bool: class _ProximityAlertTriggered(MessageFilter): __slots__ = () - name = 'Filters.status_update.proximity_alert_triggered' + name = "Filters.status_update.proximity_alert_triggered" def filter(self, message: Message) -> bool: return bool(message.proximity_alert_triggered) @@ -1144,7 +1124,7 @@ def filter(self, message: Message) -> bool: class _VoiceChatScheduled(MessageFilter): __slots__ = () - name = 'Filters.status_update.voice_chat_scheduled' + name = "Filters.status_update.voice_chat_scheduled" def filter(self, message: Message) -> bool: return bool(message.voice_chat_scheduled) @@ -1154,7 +1134,7 @@ def filter(self, message: Message) -> bool: class _VoiceChatStarted(MessageFilter): __slots__ = () - name = 'Filters.status_update.voice_chat_started' + name = "Filters.status_update.voice_chat_started" def filter(self, message: Message) -> bool: return bool(message.voice_chat_started) @@ -1164,7 +1144,7 @@ def filter(self, message: Message) -> bool: class _VoiceChatEnded(MessageFilter): __slots__ = () - name = 'Filters.status_update.voice_chat_ended' + name = "Filters.status_update.voice_chat_ended" def filter(self, message: Message) -> bool: return bool(message.voice_chat_ended) @@ -1174,7 +1154,7 @@ def filter(self, message: Message) -> bool: class _VoiceChatParticipantsInvited(MessageFilter): __slots__ = () - name = 'Filters.status_update.voice_chat_participants_invited' + name = "Filters.status_update.voice_chat_participants_invited" def filter(self, message: Message) -> bool: return bool(message.voice_chat_participants_invited) @@ -1182,7 +1162,7 @@ def filter(self, message: Message) -> bool: voice_chat_participants_invited = _VoiceChatParticipantsInvited() """Messages that contain :attr:`telegram.Message.voice_chat_participants_invited`.""" - name = 'Filters.status_update' + name = "Filters.status_update" def filter(self, message: Update) -> bool: return bool( @@ -1259,7 +1239,7 @@ def filter(self, message: Update) -> bool: class _Forwarded(MessageFilter): __slots__ = () - name = 'Filters.forwarded' + name = "Filters.forwarded" def filter(self, message: Message) -> bool: return bool(message.forward_date) @@ -1269,7 +1249,7 @@ def filter(self, message: Message) -> bool: class _Game(MessageFilter): __slots__ = () - name = 'Filters.game' + name = "Filters.game" def filter(self, message: Message) -> bool: return bool(message.game) @@ -1291,11 +1271,11 @@ class entity(MessageFilter): """ - __slots__ = ('entity_type',) + __slots__ = ("entity_type",) def __init__(self, entity_type: str): self.entity_type = entity_type - self.name = f'Filters.entity({self.entity_type})' + self.name = f"Filters.entity({self.entity_type})" def filter(self, message: Message) -> bool: """""" # remove method from docs @@ -1315,65 +1295,23 @@ class caption_entity(MessageFilter): """ - __slots__ = ('entity_type',) + __slots__ = ("entity_type",) def __init__(self, entity_type: str): self.entity_type = entity_type - self.name = f'Filters.caption_entity({self.entity_type})' + self.name = f"Filters.caption_entity({self.entity_type})" def filter(self, message: Message) -> bool: """""" # remove method from docs return any(entity.type == self.entity_type for entity in message.caption_entities) - class _Private(MessageFilter): - __slots__ = () - name = 'Filters.private' - - def filter(self, message: Message) -> bool: - warnings.warn( - 'Filters.private is deprecated. Use Filters.chat_type.private instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return message.chat.type == Chat.PRIVATE - - private = _Private() - """ - Messages sent in a private chat. - - Note: - DEPRECATED. Use - :attr:`telegram.ext.Filters.chat_type.private` instead. - """ - - class _Group(MessageFilter): - __slots__ = () - name = 'Filters.group' - - def filter(self, message: Message) -> bool: - warnings.warn( - 'Filters.group is deprecated. Use Filters.chat_type.groups instead.', - TelegramDeprecationWarning, - stacklevel=2, - ) - return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] - - group = _Group() - """ - Messages sent in a group or a supergroup chat. - - Note: - DEPRECATED. Use - :attr:`telegram.ext.Filters.chat_type.groups` instead. - """ - class _ChatType(MessageFilter): __slots__ = () - name = 'Filters.chat_type' + name = "Filters.chat_type" class _Channel(MessageFilter): __slots__ = () - name = 'Filters.chat_type.channel' + name = "Filters.chat_type.channel" def filter(self, message: Message) -> bool: return message.chat.type == Chat.CHANNEL @@ -1382,7 +1320,7 @@ def filter(self, message: Message) -> bool: class _Group(MessageFilter): __slots__ = () - name = 'Filters.chat_type.group' + name = "Filters.chat_type.group" def filter(self, message: Message) -> bool: return message.chat.type == Chat.GROUP @@ -1391,7 +1329,7 @@ def filter(self, message: Message) -> bool: class _SuperGroup(MessageFilter): __slots__ = () - name = 'Filters.chat_type.supergroup' + name = "Filters.chat_type.supergroup" def filter(self, message: Message) -> bool: return message.chat.type == Chat.SUPERGROUP @@ -1400,7 +1338,7 @@ def filter(self, message: Message) -> bool: class _Groups(MessageFilter): __slots__ = () - name = 'Filters.chat_type.groups' + name = "Filters.chat_type.groups" def filter(self, message: Message) -> bool: return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP] @@ -1409,7 +1347,7 @@ def filter(self, message: Message) -> bool: class _Private(MessageFilter): __slots__ = () - name = 'Filters.chat_type.private' + name = "Filters.chat_type.private" def filter(self, message: Message) -> bool: return message.chat.type == Chat.PRIVATE @@ -1437,12 +1375,12 @@ def filter(self, message: Message) -> bool: class _ChatUserBaseFilter(MessageFilter, ABC): __slots__ = ( - 'chat_id_name', - 'username_name', - 'allow_empty', - '__lock', - '_chat_ids', - '_usernames', + "chat_id_name", + "username_name", + "allow_empty", + "__lock", + "_chat_ids", + "_usernames", ) def __init__( @@ -1451,8 +1389,8 @@ def __init__( username: SLT[str] = None, allow_empty: bool = False, ): - self.chat_id_name = 'chat_id' - self.username_name = 'username' + self.chat_id_name = "chat_id" + self.username_name = "username" self.allow_empty = allow_empty self.__lock = Lock() @@ -1479,8 +1417,8 @@ def _parse_username(username: SLT[str]) -> Set[str]: if username is None: return set() if isinstance(username, str): - return {username[1:] if username.startswith('@') else username} - return {chat[1:] if chat.startswith('@') else chat for chat in username} + return {username[1:] if username.startswith("@") else username} + return {chat[1:] if chat.startswith("@") else chat for chat in username} def _set_chat_ids(self, chat_id: SLT[int]) -> None: with self.__lock: @@ -1576,13 +1514,13 @@ def filter(self, message: Message) -> bool: @property def name(self) -> str: return ( - f'Filters.{self.__class__.__name__}(' + f"Filters.{self.__class__.__name__}(" f'{", ".join(str(s) for s in (self.usernames or self.chat_ids))})' ) @name.setter def name(self, name: str) -> NoReturn: - raise RuntimeError(f'Cannot set name for Filters.{self.__class__.__name__}') + raise RuntimeError(f"Cannot set name for Filters.{self.__class__.__name__}") class user(_ChatUserBaseFilter): # pylint: disable=W0235 @@ -1630,7 +1568,7 @@ def __init__( allow_empty: bool = False, ): super().__init__(chat_id=user_id, username=username, allow_empty=allow_empty) - self.chat_id_name = 'user_id' + self.chat_id_name = "user_id" def get_chat_or_user(self, message: Message) -> Optional[User]: return message.from_user @@ -1731,7 +1669,7 @@ def __init__( allow_empty: bool = False, ): super().__init__(chat_id=bot_id, username=username, allow_empty=allow_empty) - self.chat_id_name = 'bot_id' + self.chat_id_name = "bot_id" def get_chat_or_user(self, message: Message) -> Optional[User]: return message.via_bot @@ -2091,7 +2029,7 @@ def filter(self, message: Message) -> bool: class _Invoice(MessageFilter): __slots__ = () - name = 'Filters.invoice' + name = "Filters.invoice" def filter(self, message: Message) -> bool: return bool(message.invoice) @@ -2101,7 +2039,7 @@ def filter(self, message: Message) -> bool: class _SuccessfulPayment(MessageFilter): __slots__ = () - name = 'Filters.successful_payment' + name = "Filters.successful_payment" def filter(self, message: Message) -> bool: return bool(message.successful_payment) @@ -2111,7 +2049,7 @@ def filter(self, message: Message) -> bool: class _PassportData(MessageFilter): __slots__ = () - name = 'Filters.passport_data' + name = "Filters.passport_data" def filter(self, message: Message) -> bool: return bool(message.passport_data) @@ -2121,7 +2059,7 @@ def filter(self, message: Message) -> bool: class _Poll(MessageFilter): __slots__ = () - name = 'Filters.poll' + name = "Filters.poll" def filter(self, message: Message) -> bool: return bool(message.poll) @@ -2131,12 +2069,12 @@ def filter(self, message: Message) -> bool: class _Dice(_DiceEmoji): __slots__ = () - dice = _DiceEmoji('🎲', 'dice') - darts = _DiceEmoji('🎯', 'darts') - basketball = _DiceEmoji('πŸ€', 'basketball') - football = _DiceEmoji('⚽') - slot_machine = _DiceEmoji('🎰') - bowling = _DiceEmoji('🎳', 'bowling') + dice = _DiceEmoji("🎲", "dice") + darts = _DiceEmoji("🎯", "darts") + basketball = _DiceEmoji("πŸ€", "basketball") + football = _DiceEmoji("⚽") + slot_machine = _DiceEmoji("🎰") + bowling = _DiceEmoji("🎳", "bowling") dice = _Dice() """Dice Messages. If an integer or a list of integers is passed, it filters messages to only @@ -2199,7 +2137,7 @@ class language(MessageFilter): """ - __slots__ = ('lang',) + __slots__ = ("lang",) def __init__(self, lang: SLT[str]): if isinstance(lang, str): @@ -2208,7 +2146,7 @@ def __init__(self, lang: SLT[str]): else: lang = cast(List[str], lang) self.lang = lang - self.name = f'Filters.language({self.lang})' + self.name = f"Filters.language({self.lang})" def filter(self, message: Message) -> bool: """""" # remove method from docs @@ -2220,7 +2158,7 @@ def filter(self, message: Message) -> bool: class _Attachment(MessageFilter): __slots__ = () - name = 'Filters.attachment' + name = "Filters.attachment" def filter(self, message: Message) -> bool: return bool(message.effective_attachment) @@ -2233,11 +2171,11 @@ def filter(self, message: Message) -> bool: class _UpdateType(UpdateFilter): __slots__ = () - name = 'Filters.update' + name = "Filters.update" class _Message(UpdateFilter): __slots__ = () - name = 'Filters.update.message' + name = "Filters.update.message" def filter(self, update: Update) -> bool: return update.message is not None @@ -2246,7 +2184,7 @@ def filter(self, update: Update) -> bool: class _EditedMessage(UpdateFilter): __slots__ = () - name = 'Filters.update.edited_message' + name = "Filters.update.edited_message" def filter(self, update: Update) -> bool: return update.edited_message is not None @@ -2255,7 +2193,7 @@ def filter(self, update: Update) -> bool: class _Messages(UpdateFilter): __slots__ = () - name = 'Filters.update.messages' + name = "Filters.update.messages" def filter(self, update: Update) -> bool: return update.message is not None or update.edited_message is not None @@ -2264,7 +2202,7 @@ def filter(self, update: Update) -> bool: class _ChannelPost(UpdateFilter): __slots__ = () - name = 'Filters.update.channel_post' + name = "Filters.update.channel_post" def filter(self, update: Update) -> bool: return update.channel_post is not None @@ -2273,7 +2211,7 @@ def filter(self, update: Update) -> bool: class _EditedChannelPost(UpdateFilter): __slots__ = () - name = 'Filters.update.edited_channel_post' + name = "Filters.update.edited_channel_post" def filter(self, update: Update) -> bool: return update.edited_channel_post is not None @@ -2282,7 +2220,7 @@ def filter(self, update: Update) -> bool: class _ChannelPosts(UpdateFilter): __slots__ = () - name = 'Filters.update.channel_posts' + name = "Filters.update.channel_posts" def filter(self, update: Update) -> bool: return update.channel_post is not None or update.edited_channel_post is not None diff --git a/telegram/ext/handler.py b/telegram/ext/handler.py index befaf413979..318c9da8cef 100644 --- a/telegram/ext/handler.py +++ b/telegram/ext/handler.py @@ -18,12 +18,8 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the base class for handlers as used by the Dispatcher.""" from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, Union, Generic -from sys import version_info as py_ver +from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, Generic -from telegram.utils.deprecate import set_new_attribute_deprecated - -from telegram import Update from telegram.ext.utils.promise import Promise from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE from telegram.ext.utils.types import CCT @@ -31,22 +27,13 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') -UT = TypeVar('UT') +RT = TypeVar("RT") +UT = TypeVar("UT") class Handler(Generic[UT, CCT], ABC): """The base class for all update handlers. Create custom handlers by inheriting from it. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -54,93 +41,32 @@ class Handler(Generic[UT, CCT], ABC): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - # Apparently Py 3.7 and below have '__dict__' in ABC - if py_ver < (3, 7): - __slots__ = ( - 'callback', - 'pass_update_queue', - 'pass_job_queue', - 'pass_user_data', - 'pass_chat_data', - 'run_async', - ) - else: - __slots__ = ( - 'callback', # type: ignore[assignment] - 'pass_update_queue', - 'pass_job_queue', - 'pass_user_data', - 'pass_chat_data', - 'run_async', - '__dict__', - ) + __slots__ = ( + "callback", + "run_async", + ) def __init__( self, callback: Callable[[UT, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): self.callback = callback - self.pass_update_queue = pass_update_queue - self.pass_job_queue = pass_job_queue - self.pass_user_data = pass_user_data - self.pass_chat_data = pass_chat_data self.run_async = run_async - def __setattr__(self, key: str, value: object) -> None: - # See comment on BaseFilter to know why this was done. - if key.startswith('__'): - key = f"_{self.__class__.__name__}{key}" - if issubclass(self.__class__, Handler) and not self.__class__.__module__.startswith( - 'telegram.ext.' - ): - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - @abstractmethod def check_update(self, update: object) -> Optional[Union[bool, object]]: """ @@ -164,9 +90,9 @@ def check_update(self, update: object) -> Optional[Union[bool, object]]: def handle_update( self, update: UT, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: object, - context: CCT = None, + context: CCT, ) -> Union[RT, Promise]: """ This method is called if it was determined that an update should indeed @@ -179,7 +105,7 @@ def handle_update( update (:obj:`str` | :class:`telegram.Update`): The update to be handled. dispatcher (:class:`telegram.ext.Dispatcher`): The calling dispatcher. check_result (:obj:`obj`): The result from :attr:`check_update`. - context (:class:`telegram.ext.CallbackContext`, optional): The context as provided by + context (:class:`telegram.ext.CallbackContext`): The context as provided by the dispatcher. """ @@ -191,24 +117,16 @@ def handle_update( ): run_async = True - if context: - self.collect_additional_context(context, update, dispatcher, check_result) - if run_async: - return dispatcher.run_async(self.callback, update, context, update=update) - return self.callback(update, context) - - optional_args = self.collect_optional_args(dispatcher, update, check_result) + self.collect_additional_context(context, update, dispatcher, check_result) if run_async: - return dispatcher.run_async( - self.callback, dispatcher.bot, update, update=update, **optional_args - ) - return self.callback(dispatcher.bot, update, **optional_args) # type: ignore + return dispatcher.run_async(self.callback, update, context, update=update) + return self.callback(update, context) def collect_additional_context( self, context: CCT, update: UT, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Any, ) -> None: """Prepares additional arguments for the context. Override if needed. @@ -220,41 +138,3 @@ def collect_additional_context( check_result: The result (return value) from :attr:`check_update`. """ - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - update: UT = None, - check_result: Any = None, # pylint: disable=W0613 - ) -> Dict[str, object]: - """ - Prepares the optional arguments. If the handler has additional optional args, - it should subclass this method, but remember to call this super method. - - DEPRECATED: This method is being replaced by new context based callbacks. Please see - https://git.io/fxJuV for more info. - - Args: - dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher. - update (:class:`telegram.Update`): The update to gather chat/user id from. - check_result: The result from check_update - - """ - optional_args: Dict[str, object] = {} - - if self.pass_update_queue: - optional_args['update_queue'] = dispatcher.update_queue - if self.pass_job_queue: - optional_args['job_queue'] = dispatcher.job_queue - if self.pass_user_data and isinstance(update, Update): - user = update.effective_user - optional_args['user_data'] = dispatcher.user_data[ - user.id if user else None # type: ignore[index] - ] - if self.pass_chat_data and isinstance(update, Update): - chat = update.effective_chat - optional_args['chat_data'] = dispatcher.chat_data[ - chat.id if chat else None # type: ignore[index] - ] - - return optional_args diff --git a/telegram/ext/inlinequeryhandler.py b/telegram/ext/inlinequeryhandler.py index 11103e71ff6..7b4f2e6e8ed 100644 --- a/telegram/ext/inlinequeryhandler.py +++ b/telegram/ext/inlinequeryhandler.py @@ -21,7 +21,6 @@ from typing import ( TYPE_CHECKING, Callable, - Dict, Match, Optional, Pattern, @@ -40,7 +39,7 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') +RT = TypeVar("RT") class InlineQueryHandler(Handler[Update, CCT]): @@ -48,15 +47,6 @@ class InlineQueryHandler(Handler[Update, CCT]): Handler class to handle Telegram inline queries. Optionally based on a regex. Read the documentation of the ``re`` module for more information. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: * When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -67,22 +57,10 @@ class InlineQueryHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. pattern (:obj:`str` | :obj:`Pattern`, optional): Regex pattern. If not :obj:`None`, ``re.match`` is used on :attr:`telegram.InlineQuery.query` to determine if an update should be handled by this handler. @@ -90,67 +68,31 @@ class InlineQueryHandler(Handler[Update, CCT]): handle inline queries with the appropriate :attr:`telegram.InlineQuery.chat_type`. .. versionadded:: 13.5 - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test :attr:`telegram.InlineQuery.query` against. chat_types (List[:obj:`str`], optional): List of allowed chat types. .. versionadded:: 13.5 - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('pattern', 'chat_types', 'pass_groups', 'pass_groupdict') + __slots__ = ("pattern", "chat_types") def __init__( self, callback: Callable[[Update, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, pattern: Union[str, Pattern] = None, - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, chat_types: List[str] = None, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) @@ -159,8 +101,6 @@ def __init__( self.pattern = pattern self.chat_types = chat_types - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict def check_update(self, update: object) -> Optional[Union[bool, Match]]: """ @@ -187,30 +127,11 @@ def check_update(self, update: object) -> Optional[Union[bool, Match]]: return True return None - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - 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) - if self.pass_groups: - optional_args['groups'] = check_result.groups() - if self.pass_groupdict: - optional_args['groupdict'] = check_result.groupdict() - return optional_args - def collect_additional_context( self, context: CCT, update: Update, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Optional[Union[bool, Match]], ) -> None: """Add the result of ``re.match(pattern, update.inline_query.query)`` to diff --git a/telegram/ext/jobqueue.py b/telegram/ext/jobqueue.py index da2dea4f210..f3d5529a65e 100644 --- a/telegram/ext/jobqueue.py +++ b/telegram/ext/jobqueue.py @@ -25,16 +25,12 @@ import pytz from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_EXECUTED, JobEvent from apscheduler.schedulers.background import BackgroundScheduler -from apscheduler.triggers.combining import OrTrigger -from apscheduler.triggers.cron import CronTrigger from apscheduler.job import Job as APSJob from telegram.ext.callbackcontext import CallbackContext from telegram.utils.types import JSONDict -from telegram.utils.deprecate import set_new_attribute_deprecated if TYPE_CHECKING: - from telegram import Bot from telegram.ext import Dispatcher import apscheduler.job # noqa: F401 @@ -50,10 +46,10 @@ class JobQueue: """ - __slots__ = ('_dispatcher', 'logger', 'scheduler', '__dict__') + __slots__ = ("_dispatcher", "logger", "scheduler") def __init__(self) -> None: - self._dispatcher: 'Dispatcher' = None # type: ignore[assignment] + self._dispatcher: "Dispatcher" = None # type: ignore[assignment] self.logger = logging.getLogger(self.__class__.__name__) self.scheduler = BackgroundScheduler(timezone=pytz.utc) self.scheduler.add_listener( @@ -62,18 +58,13 @@ def __init__(self) -> None: # Dispatch errors and don't log them in the APS logger def aps_log_filter(record): # type: ignore - return 'raised an exception' not in record.msg + return "raised an exception" not in record.msg - logging.getLogger('apscheduler.executors.default').addFilter(aps_log_filter) + logging.getLogger("apscheduler.executors.default").addFilter(aps_log_filter) self.scheduler.add_listener(self._dispatch_error, EVENT_JOB_ERROR) - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - def _build_args(self, job: 'Job') -> List[Union[CallbackContext, 'Bot', 'Job']]: - if self._dispatcher.use_context: - return [self._dispatcher.context_types.context.from_job(job, self._dispatcher)] - return [self._dispatcher.bot, job] + def _build_args(self, job: "Job") -> List[CallbackContext]: + return [self._dispatcher.context_types.context.from_job(job, self._dispatcher)] def _tz_now(self) -> datetime.datetime: return datetime.datetime.now(self.scheduler.timezone) @@ -87,9 +78,9 @@ def _dispatch_error(self, event: JobEvent) -> None: # Errors should not stop the thread. except Exception: self.logger.exception( - 'An error was raised while processing the job and an ' - 'uncaught error was raised while handling the error ' - 'with an error_handler.' + "An error was raised while processing the job and an " + "uncaught error was raised while handling the error " + "with an error_handler." ) @overload @@ -127,7 +118,7 @@ def _parse_time_input( # isinstance(time, datetime.datetime): return time - def set_dispatcher(self, dispatcher: 'Dispatcher') -> None: + def set_dispatcher(self, dispatcher: "Dispatcher") -> None: """Set the dispatcher to be used by this JobQueue. Use this instead of passing a :class:`telegram.Bot` to the JobQueue, which is deprecated. @@ -141,22 +132,17 @@ def set_dispatcher(self, dispatcher: 'Dispatcher') -> None: def run_once( self, - callback: Callable[['CallbackContext'], None], + callback: Callable[["CallbackContext"], None], when: Union[float, datetime.timedelta, datetime.datetime, datetime.time], context: object = None, name: str = None, job_kwargs: JSONDict = None, - ) -> 'Job': + ) -> "Job": """Creates a new ``Job`` that runs once and adds it to the queue. Args: callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. + job. Callback signature: ``def callback(update: Update, context: CallbackContext)`` when (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta` | \ :obj:`datetime.datetime` | :obj:`datetime.time`): Time in or at which the job should run. This parameter will be interpreted @@ -196,7 +182,7 @@ def run_once( j = self.scheduler.add_job( callback, name=name, - trigger='date', + trigger="date", run_date=date_time, args=self._build_args(job), timezone=date_time.tzinfo or self.scheduler.timezone, @@ -208,14 +194,14 @@ def run_once( def run_repeating( self, - callback: Callable[['CallbackContext'], None], + callback: Callable[["CallbackContext"], None], interval: Union[float, datetime.timedelta], first: Union[float, datetime.timedelta, datetime.datetime, datetime.time] = None, last: Union[float, datetime.timedelta, datetime.datetime, datetime.time] = None, context: object = None, name: str = None, job_kwargs: JSONDict = None, - ) -> 'Job': + ) -> "Job": """Creates a new ``Job`` that runs at specified intervals and adds it to the queue. Note: @@ -226,12 +212,7 @@ def run_repeating( Args: callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. + job. Callback signature: ``def callback(update: Update, context: CallbackContext)`` interval (:obj:`int` | :obj:`float` | :obj:`datetime.timedelta`): The interval in which the job will run. If it is an :obj:`int` or a :obj:`float`, it will be interpreted as seconds. @@ -292,7 +273,7 @@ def run_repeating( j = self.scheduler.add_job( callback, - trigger='interval', + trigger="interval", args=self._build_args(job), start_date=dt_first, end_date=dt_last, @@ -306,34 +287,32 @@ def run_repeating( def run_monthly( self, - callback: Callable[['CallbackContext'], None], + callback: Callable[["CallbackContext"], None], when: datetime.time, day: int, context: object = None, name: str = None, - day_is_strict: bool = True, job_kwargs: JSONDict = None, - ) -> 'Job': + ) -> "Job": """Creates a new ``Job`` that runs on a monthly basis and adds it to the queue. + .. versionchanged:: 14.0 + The ``day_is_strict`` argument was removed. Instead one can now pass -1 to the ``day`` + parameter to have the job run on the last day of the month. + Args: callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. + job. Callback signature: ``def callback(update: Update, context: CallbackContext)`` when (:obj:`datetime.time`): Time of day at which the job should run. If the timezone (``when.tzinfo``) is :obj:`None`, the default timezone of the bot will be used. day (:obj:`int`): Defines the day of the month whereby the job would run. It should - be within the range of 1 and 31, inclusive. + be within the range of 1 and 31, inclusive. If a month has fewer days than this + number, the job will not run in this month. Passing -1 leads to the job running on + the last day of the month. context (:obj:`object`, optional): Additional data needed for the callback function. Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. name (:obj:`str`, optional): The name of the new job. Defaults to ``callback.__name__``. - day_is_strict (:obj:`bool`, optional): If :obj:`False` and day > month.days, will pick - the last day in the month. Defaults to :obj:`True`. job_kwargs (:obj:`dict`, optional): Arbitrary keyword arguments to pass to the ``scheduler.add_job()``. @@ -348,56 +327,30 @@ def run_monthly( name = name or callback.__name__ job = Job(callback, context, name, self) - if day_is_strict: - j = self.scheduler.add_job( - callback, - trigger='cron', - args=self._build_args(job), - name=name, - day=day, - hour=when.hour, - minute=when.minute, - second=when.second, - timezone=when.tzinfo or self.scheduler.timezone, - **job_kwargs, - ) - else: - trigger = OrTrigger( - [ - CronTrigger( - day=day, - hour=when.hour, - minute=when.minute, - second=when.second, - timezone=when.tzinfo, - **job_kwargs, - ), - CronTrigger( - day='last', - hour=when.hour, - minute=when.minute, - second=when.second, - timezone=when.tzinfo or self.scheduler.timezone, - **job_kwargs, - ), - ] - ) - j = self.scheduler.add_job( - callback, trigger=trigger, args=self._build_args(job), name=name, **job_kwargs - ) - + j = self.scheduler.add_job( + callback, + trigger="cron", + args=self._build_args(job), + name=name, + day="last" if day == -1 else day, + hour=when.hour, + minute=when.minute, + second=when.second, + timezone=when.tzinfo or self.scheduler.timezone, + **job_kwargs, + ) job.job = j return job def run_daily( self, - callback: Callable[['CallbackContext'], None], + callback: Callable[["CallbackContext"], None], time: datetime.time, days: Tuple[int, ...] = tuple(range(7)), context: object = None, name: str = None, job_kwargs: JSONDict = None, - ) -> 'Job': + ) -> "Job": """Creates a new ``Job`` that runs on a daily basis and adds it to the queue. Note: @@ -408,12 +361,7 @@ def run_daily( Args: callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. + job. Callback signature: ``def callback(update: Update, context: CallbackContext)`` time (:obj:`datetime.time`): Time of day at which the job should run. If the timezone (``time.tzinfo``) is :obj:`None`, the default timezone of the bot will be used. days (Tuple[:obj:`int`], optional): Defines on which days of the week the job should @@ -440,8 +388,8 @@ def run_daily( callback, name=name, args=self._build_args(job), - trigger='cron', - day_of_week=','.join([str(d) for d in days]), + trigger="cron", + day_of_week=",".join([str(d) for d in days]), hour=time.hour, minute=time.minute, second=time.second, @@ -454,21 +402,16 @@ def run_daily( def run_custom( self, - callback: Callable[['CallbackContext'], None], + callback: Callable[["CallbackContext"], None], job_kwargs: JSONDict, context: object = None, name: str = None, - ) -> 'Job': + ) -> "Job": """Creates a new customly defined ``Job``. Args: callback (:obj:`callable`): The callback function that should be executed by the new - job. Callback signature for context based API: - - ``def callback(CallbackContext)`` - - ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. + job. Callback signature: ``def callback(update: Update, context: CallbackContext)`` job_kwargs (:obj:`dict`): Arbitrary keyword arguments. Used as arguments for ``scheduler.add_job``. context (:obj:`object`, optional): Additional data needed for the callback function. @@ -499,14 +442,14 @@ def stop(self) -> None: if self.scheduler.running: self.scheduler.shutdown() - def jobs(self) -> Tuple['Job', ...]: + def jobs(self) -> Tuple["Job", ...]: """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', ...]: + 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``. """ @@ -531,12 +474,7 @@ class Job: Args: callback (:obj:`callable`): The callback function that should be executed by the new job. - Callback signature for context based API: - - ``def callback(CallbackContext)`` - - a ``context.job`` is the :class:`telegram.ext.Job` instance. It can be used to access - its ``job.context`` or change it to a repeating job. + Callback signature: ``def callback(update: Update, context: CallbackContext)`` context (:obj:`object`, optional): Additional data needed for the callback function. Can be accessed through ``job.context`` in the callback. Defaults to :obj:`None`. name (:obj:`str`, optional): The name of the new job. Defaults to ``callback.__name__``. @@ -553,19 +491,18 @@ class Job: """ __slots__ = ( - 'callback', - 'context', - 'name', - 'job_queue', - '_removed', - '_enabled', - 'job', - '__dict__', + "callback", + "context", + "name", + "job_queue", + "_removed", + "_enabled", + "job", ) def __init__( self, - callback: Callable[['CallbackContext'], None], + callback: Callable[["CallbackContext"], None], context: object = None, name: str = None, job_queue: JobQueue = None, @@ -582,25 +519,19 @@ def __init__( self.job = cast(APSJob, job) # skipcq: PTC-W0052 - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - - def run(self, dispatcher: 'Dispatcher') -> None: + def run(self, dispatcher: "Dispatcher") -> None: """Executes the callback function independently of the jobs schedule.""" try: - if dispatcher.use_context: - self.callback(dispatcher.context_types.context.from_job(self, dispatcher)) - else: - self.callback(dispatcher.bot, self) # type: ignore[arg-type,call-arg] + self.callback(dispatcher.context_types.context.from_job(self, dispatcher)) except Exception as exc: try: dispatcher.dispatch_error(None, exc) # Errors should not stop the thread. except Exception: dispatcher.logger.exception( - 'An error was raised while processing the job and an ' - 'uncaught error was raised while handling the error ' - 'with an error_handler.' + "An error was raised while processing the job and an " + "uncaught error was raised while handling the error " + "with an error_handler." ) def schedule_removal(self) -> None: @@ -639,7 +570,7 @@ def next_t(self) -> Optional[datetime.datetime]: 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 c3f0c015cd1..363be361ac3 100644 --- a/telegram/ext/messagehandler.py +++ b/telegram/ext/messagehandler.py @@ -16,14 +16,11 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# TODO: Remove allow_edited """This module contains the MessageHandler class.""" -import warnings from typing import TYPE_CHECKING, Callable, Dict, Optional, TypeVar, Union from telegram import Update from telegram.ext import BaseFilter, Filters -from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE from .handler import Handler @@ -32,21 +29,12 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') +RT = TypeVar("RT") class MessageHandler(Handler[Update, CCT]): """Handler class to handle telegram messages. They might contain text, media or status updates. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -62,37 +50,10 @@ class MessageHandler(Handler[Update, CCT]): argument. callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - message_updates (:obj:`bool`, optional): Should "normal" message updates be handled? - Default is :obj:`None`. - DEPRECATED: Please switch to filters for update filtering. - channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled? - Default is :obj:`None`. - DEPRECATED: Please switch to filters for update filtering. - edited_updates (:obj:`bool`, optional): Should "edited" message updates be handled? Default - is :obj:`None`. - DEPRECATED: Please switch to filters for update filtering. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. @@ -103,84 +64,27 @@ class MessageHandler(Handler[Update, CCT]): filters (:obj:`Filter`): Only allow updates with these Filters. See :mod:`telegram.ext.filters` for a full list of all available filters. callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - message_updates (:obj:`bool`): Should "normal" message updates be handled? - Default is :obj:`None`. - channel_post_updates (:obj:`bool`): Should channel posts updates be handled? - Default is :obj:`None`. - edited_updates (:obj:`bool`): Should "edited" message updates be handled? - Default is :obj:`None`. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('filters',) + __slots__ = ("filters",) def __init__( self, filters: BaseFilter, callback: Callable[[Update, CCT], RT], - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - message_updates: bool = None, - channel_post_updates: bool = None, - edited_updates: bool = None, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, run_async=run_async, ) - if message_updates is False and channel_post_updates is False and edited_updates is False: - raise ValueError( - 'message_updates, channel_post_updates and edited_updates are all False' - ) if filters is not None: self.filters = Filters.update & filters else: self.filters = Filters.update - if message_updates is not None: - warnings.warn( - 'message_updates is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if message_updates is False: - self.filters &= ~Filters.update.message - - if channel_post_updates is not None: - warnings.warn( - 'channel_post_updates is deprecated. See https://git.io/fxJuV ' 'for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if channel_post_updates is False: - self.filters &= ~Filters.update.channel_post - - if edited_updates is not None: - warnings.warn( - 'edited_updates is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - if edited_updates is False: - self.filters &= ~( - Filters.update.edited_message | Filters.update.edited_channel_post - ) def check_update(self, update: object) -> Optional[Union[bool, Dict[str, list]]]: """Determines whether an update should be passed to this handlers :attr:`callback`. @@ -192,7 +96,7 @@ def check_update(self, update: object) -> Optional[Union[bool, Dict[str, list]]] :obj:`bool` """ - if isinstance(update, Update) and update.effective_message: + if isinstance(update, Update): return self.filters(update) return None @@ -200,7 +104,7 @@ def collect_additional_context( self, context: CCT, update: Update, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Optional[Union[bool, Dict[str, object]]], ) -> None: """Adds possible output of data filters to the :class:`CallbackContext`.""" diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py deleted file mode 100644 index ece0bc38908..00000000000 --- a/telegram/ext/messagequeue.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/env python -# -# Module author: -# Tymofii A. Khodniev (thodnev) -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/] -"""A throughput-limiting message processor for Telegram bots.""" -import functools -import queue as q -import threading -import time -import warnings -from typing import TYPE_CHECKING, Callable, List, NoReturn - -from telegram.ext.utils.promise import Promise -from telegram.utils.deprecate import TelegramDeprecationWarning - -if TYPE_CHECKING: - from telegram import Bot - -# We need to count < 1s intervals, so the most accurate timer is needed -curtime = time.perf_counter - - -class DelayQueueError(RuntimeError): - """Indicates processing errors.""" - - __slots__ = () - - -class DelayQueue(threading.Thread): - """ - Processes callbacks from queue with specified throughput limits. Creates a separate thread to - process callbacks with delays. - - .. deprecated:: 13.3 - :class:`telegram.ext.DelayQueue` in its current form is deprecated and will be reinvented - in a future release. See `this thread `_ for a list of known bugs. - - Args: - queue (:obj:`Queue`, optional): Used to pass callbacks to thread. Creates ``Queue`` - implicitly if not provided. - burst_limit (:obj:`int`, optional): Number of maximum callbacks to process per time-window - defined by :attr:`time_limit_ms`. Defaults to 30. - time_limit_ms (:obj:`int`, optional): Defines width of time-window used when each - processing limit is calculated. Defaults to 1000. - exc_route (:obj:`callable`, optional): A callable, accepting 1 positional argument; used to - route exceptions from processor thread to main thread; is called on `Exception` - subclass exceptions. If not provided, exceptions are routed through dummy handler, - which re-raises them. - autostart (:obj:`bool`, optional): If :obj:`True`, processor is started immediately after - object's creation; if :obj:`False`, should be started manually by `start` method. - Defaults to :obj:`True`. - name (:obj:`str`, optional): Thread's name. Defaults to ``'DelayQueue-N'``, where N is - sequential number of object created. - - Attributes: - burst_limit (:obj:`int`): Number of maximum callbacks to process per time-window. - time_limit (:obj:`int`): Defines width of time-window used when each processing limit is - calculated. - exc_route (:obj:`callable`): A callable, accepting 1 positional argument; used to route - exceptions from processor thread to main thread; - name (:obj:`str`): Thread's name. - - """ - - _instcnt = 0 # instance counter - - def __init__( - self, - queue: q.Queue = None, - burst_limit: int = 30, - time_limit_ms: int = 1000, - exc_route: Callable[[Exception], None] = None, - autostart: bool = True, - name: str = None, - ): - warnings.warn( - 'DelayQueue in its current form is deprecated and will be reinvented in a future ' - 'release. See https://git.io/JtDbF for a list of known bugs.', - category=TelegramDeprecationWarning, - ) - - self._queue = queue if queue is not None else q.Queue() - self.burst_limit = burst_limit - self.time_limit = time_limit_ms / 1000 - self.exc_route = exc_route if exc_route is not None else self._default_exception_handler - self.__exit_req = False # flag to gently exit thread - self.__class__._instcnt += 1 - if name is None: - name = f'{self.__class__.__name__}-{self.__class__._instcnt}' - super().__init__(name=name) - self.daemon = False - if autostart: # immediately start processing - super().start() - - def run(self) -> None: - """ - Do not use the method except for unthreaded testing purposes, the method normally is - automatically called by autostart argument. - - """ - times: List[float] = [] # used to store each callable processing time - while True: - item = self._queue.get() - if self.__exit_req: - return # shutdown thread - # delay routine - now = time.perf_counter() - t_delta = now - self.time_limit # calculate early to improve perf. - if times and t_delta > times[-1]: - # if last call was before the limit time-window - # used to impr. perf. in long-interval calls case - times = [now] - else: - # collect last in current limit time-window - times = [t for t in times if t >= t_delta] - times.append(now) - if len(times) >= self.burst_limit: # if throughput limit was hit - time.sleep(times[1] - t_delta) - # finally process one - try: - func, args, kwargs = item - func(*args, **kwargs) - except Exception as exc: # re-route any exceptions - self.exc_route(exc) # to prevent thread exit - - def stop(self, timeout: float = None) -> None: - """Used to gently stop processor and shutdown its thread. - - Args: - timeout (:obj:`float`): Indicates maximum time to wait for processor to stop and its - thread to exit. If timeout exceeds and processor has not stopped, method silently - returns. :attr:`is_alive` could be used afterwards to check the actual status. - ``timeout`` set to :obj:`None`, blocks until processor is shut down. - Defaults to :obj:`None`. - - """ - self.__exit_req = True # gently request - self._queue.put(None) # put something to unfreeze if frozen - super().join(timeout=timeout) - - @staticmethod - def _default_exception_handler(exc: Exception) -> NoReturn: - """ - Dummy exception handler which re-raises exception in thread. Could be possibly overwritten - by subclasses. - - """ - raise exc - - def __call__(self, func: Callable, *args: object, **kwargs: object) -> None: - """Used to process callbacks in throughput-limiting thread through queue. - - Args: - func (:obj:`callable`): The actual function (or any callable) that is processed through - queue. - *args (:obj:`list`): Variable-length `func` arguments. - **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)) - - -# The most straightforward way to implement this is to use 2 sequential delay -# queues, like on classic delay chain schematics in electronics. -# So, message path is: -# msg --> group delay if group msg, else no delay --> normal msg delay --> out -# This way OS threading scheduler cares of timings accuracy. -# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org) -class MessageQueue: - """ - Implements callback processing with proper delays to avoid hitting Telegram's message limits. - Contains two ``DelayQueue``, for group and for all messages, interconnected in delay chain. - Callables are processed through *group* ``DelayQueue``, then through *all* ``DelayQueue`` for - group-type messages. For non-group messages, only the *all* ``DelayQueue`` is used. - - .. deprecated:: 13.3 - :class:`telegram.ext.MessageQueue` in its current form is deprecated and will be reinvented - in a future release. See `this thread `_ for a list of known bugs. - - Args: - all_burst_limit (:obj:`int`, optional): Number of maximum *all-type* callbacks to process - per time-window defined by :attr:`all_time_limit_ms`. Defaults to 30. - all_time_limit_ms (:obj:`int`, optional): Defines width of *all-type* time-window used when - each processing limit is calculated. Defaults to 1000 ms. - group_burst_limit (:obj:`int`, optional): Number of maximum *group-type* callbacks to - process per time-window defined by :attr:`group_time_limit_ms`. Defaults to 20. - group_time_limit_ms (:obj:`int`, optional): Defines width of *group-type* time-window used - when each processing limit is calculated. Defaults to 60000 ms. - exc_route (:obj:`callable`, optional): A callable, accepting one positional argument; used - to route exceptions from processor threads to main thread; is called on ``Exception`` - subclass exceptions. If not provided, exceptions are routed through dummy handler, - which re-raises them. - autostart (:obj:`bool`, optional): If :obj:`True`, processors are started immediately after - object's creation; if :obj:`False`, should be started manually by :attr:`start` method. - Defaults to :obj:`True`. - - """ - - def __init__( - self, - all_burst_limit: int = 30, - all_time_limit_ms: int = 1000, - group_burst_limit: int = 20, - group_time_limit_ms: int = 60000, - exc_route: Callable[[Exception], None] = None, - autostart: bool = True, - ): - warnings.warn( - 'MessageQueue in its current form is deprecated and will be reinvented in a future ' - 'release. See https://git.io/JtDbF for a list of known bugs.', - category=TelegramDeprecationWarning, - ) - - # create according delay queues, use composition - self._all_delayq = DelayQueue( - burst_limit=all_burst_limit, - time_limit_ms=all_time_limit_ms, - exc_route=exc_route, - autostart=autostart, - ) - self._group_delayq = DelayQueue( - burst_limit=group_burst_limit, - time_limit_ms=group_time_limit_ms, - exc_route=exc_route, - autostart=autostart, - ) - - def start(self) -> None: - """Method is used to manually start the ``MessageQueue`` processing.""" - self._all_delayq.start() - 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) - - stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docstring if any - - def __call__(self, promise: Callable, is_group_msg: bool = False) -> Callable: - """ - Processes callables in throughput-limiting queues to avoid hitting limits (specified with - :attr:`burst_limit` and :attr:`time_limit`. - - Args: - promise (:obj:`callable`): Mainly the ``telegram.utils.promise.Promise`` (see Notes for - other callables), that is processed in delay queues. - is_group_msg (:obj:`bool`, optional): Defines whether ``promise`` would be processed in - group*+*all* ``DelayQueue``s (if set to :obj:`True`), or only through *all* - ``DelayQueue`` (if set to :obj:`False`), resulting in needed delays to avoid - hitting specified limits. Defaults to :obj:`False`. - - Note: - Method is designed to accept ``telegram.utils.promise.Promise`` as ``promise`` - argument, but other callables could be used too. For example, lambdas or simple - functions could be used to wrap original func to be called with needed args. In that - case, be sure that either wrapper func does not raise outside exceptions or the proper - :attr:`exc_route` handler is provided. - - Returns: - :obj:`callable`: Used as ``promise`` argument. - - """ - if not is_group_msg: # ignore middle group delay - self._all_delayq(promise) - else: # use middle group delay - self._group_delayq(self._all_delayq, promise) - return promise - - -def queuedmessage(method: Callable) -> Callable: - """A decorator to be used with :attr:`telegram.Bot` send* methods. - - Note: - As it probably wouldn't be a good idea to make this decorator a property, it has been coded - as decorator function, so it implies that first positional argument to wrapped MUST be - self. - - The next object attributes are used by decorator: - - Attributes: - self._is_messages_queued_default (:obj:`bool`): Value to provide class-defaults to - ``queued`` kwarg if not provided during wrapped method call. - self._msg_queue (:class:`telegram.ext.messagequeue.MessageQueue`): The actual - ``MessageQueue`` used to delay outbound messages according to specified time-limits. - - Wrapped method starts accepting the next kwargs: - - Args: - queued (:obj:`bool`, optional): If set to :obj:`True`, the ``MessageQueue`` is used to - process output messages. Defaults to `self._is_queued_out`. - isgroup (:obj:`bool`, optional): If set to :obj:`True`, the message is meant to be - group-type(as there's no obvious way to determine its type in other way at the moment). - Group-type messages could have additional processing delay according to limits set - in `self._out_queue`. Defaults to :obj:`False`. - - Returns: - ``telegram.utils.promise.Promise``: In case call is queued or original method's return - value if it's not. - - """ - - @functools.wraps(method) - def wrapped(self: 'Bot', *args: object, **kwargs: object) -> object: - # pylint: disable=W0212 - queued = kwargs.pop( - 'queued', self._is_messages_queued_default # type: ignore[attr-defined] - ) - isgroup = kwargs.pop('isgroup', False) - if queued: - prom = Promise(method, (self,) + args, kwargs) - return self._msg_queue(prom, isgroup) # type: ignore[attr-defined] - return method(self, *args, **kwargs) - - return wrapped diff --git a/telegram/ext/picklepersistence.py b/telegram/ext/picklepersistence.py index cf0059ad1ba..327fc597407 100644 --- a/telegram/ext/picklepersistence.py +++ b/telegram/ext/picklepersistence.py @@ -29,7 +29,7 @@ DefaultDict, ) -from telegram.ext import BasePersistence +from telegram.ext import BasePersistence, PersistenceInput from .utils.types import UD, CD, BD, ConversationDict, CDCData from .contexttypes import ContextTypes @@ -46,19 +46,15 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): :meth:`telegram.ext.BasePersistence.replace_bot` and :meth:`telegram.ext.BasePersistence.insert_bot`. + .. versionchanged:: 14.0 + The parameters and attributes ``store_*_data`` were replaced by :attr:`store_data`. + Args: filename (:obj:`str`): The filename for storing the pickle files. When :attr:`single_file` is :obj:`False` this will be used as a prefix. - store_user_data (:obj:`bool`, optional): Whether user_data should be saved by this - persistence class. Default is :obj:`True`. - store_chat_data (:obj:`bool`, optional): Whether chat_data should be saved by this - persistence class. Default is :obj:`True`. - store_bot_data (:obj:`bool`, optional): Whether bot_data should be saved by this - persistence class. Default is :obj:`True`. - store_callback_data (:obj:`bool`, optional): Whether callback_data should be saved by this - persistence class. Default is :obj:`False`. - - .. versionadded:: 13.6 + store_data (:class:`PersistenceInput`, optional): Specifies which kinds of data will be + saved by this persistence instance. By default, all available kinds of data will be + saved. single_file (:obj:`bool`, optional): When :obj:`False` will store 5 separate files of `filename_user_data`, `filename_bot_data`, `filename_chat_data`, `filename_callback_data` and `filename_conversations`. Default is :obj:`True`. @@ -76,16 +72,8 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): Attributes: filename (:obj:`str`): The filename for storing the pickle files. When :attr:`single_file` is :obj:`False` this will be used as a prefix. - store_user_data (:obj:`bool`): Optional. Whether user_data should be saved by this - persistence class. - store_chat_data (:obj:`bool`): Optional. Whether chat_data should be saved by this - persistence class. - store_bot_data (:obj:`bool`): Optional. Whether bot_data should be saved by this - persistence class. - store_callback_data (:obj:`bool`): Optional. Whether callback_data be saved by this - persistence class. - - .. versionadded:: 13.6 + store_data (:class:`PersistenceInput`): Specifies which kinds of data will be saved by this + persistence instance. single_file (:obj:`bool`): Optional. When :obj:`False` will store 5 separate files of `filename_user_data`, `filename_bot_data`, `filename_chat_data`, `filename_callback_data` and `filename_conversations`. Default is :obj:`True`. @@ -100,40 +88,34 @@ class PicklePersistence(BasePersistence[UD, CD, BD]): """ __slots__ = ( - 'filename', - 'single_file', - 'on_flush', - 'user_data', - 'chat_data', - 'bot_data', - 'callback_data', - 'conversations', - 'context_types', + "filename", + "single_file", + "on_flush", + "user_data", + "chat_data", + "bot_data", + "callback_data", + "conversations", + "context_types", ) @overload def __init__( - self: 'PicklePersistence[Dict, Dict, Dict]', + self: "PicklePersistence[Dict, Dict, Dict]", filename: str, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, + store_data: PersistenceInput = None, single_file: bool = True, on_flush: bool = False, - store_callback_data: bool = False, ): ... @overload def __init__( - self: 'PicklePersistence[UD, CD, BD]', + self: "PicklePersistence[UD, CD, BD]", filename: str, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, + store_data: PersistenceInput = None, single_file: bool = True, on_flush: bool = False, - store_callback_data: bool = False, context_types: ContextTypes[Any, UD, CD, BD] = None, ): ... @@ -141,20 +123,12 @@ def __init__( def __init__( self, filename: str, - store_user_data: bool = True, - store_chat_data: bool = True, - store_bot_data: bool = True, + store_data: PersistenceInput = None, single_file: bool = True, on_flush: bool = False, - store_callback_data: bool = False, context_types: ContextTypes[Any, UD, CD, BD] = None, ): - super().__init__( - store_user_data=store_user_data, - store_chat_data=store_chat_data, - store_bot_data=store_bot_data, - store_callback_data=store_callback_data, - ) + super().__init__(store_data=store_data) self.filename = filename self.single_file = single_file self.on_flush = on_flush @@ -170,12 +144,12 @@ def _load_singlefile(self) -> None: filename = self.filename with open(self.filename, "rb") as file: data = pickle.load(file) - self.user_data = defaultdict(self.context_types.user_data, data['user_data']) - self.chat_data = defaultdict(self.context_types.chat_data, data['chat_data']) + self.user_data = defaultdict(self.context_types.user_data, data["user_data"]) + self.chat_data = defaultdict(self.context_types.chat_data, data["chat_data"]) # For backwards compatibility with files not containing bot data - self.bot_data = data.get('bot_data', self.context_types.bot_data()) - self.callback_data = data.get('callback_data', {}) - self.conversations = data['conversations'] + self.bot_data = data.get("bot_data", self.context_types.bot_data()) + self.callback_data = data.get("callback_data", {}) + self.conversations = data["conversations"] except OSError: self.conversations = {} self.user_data = defaultdict(self.context_types.user_data) @@ -202,11 +176,11 @@ def _load_file(filename: str) -> Any: def _dump_singlefile(self) -> None: with open(self.filename, "wb") as file: data = { - 'conversations': self.conversations, - 'user_data': self.user_data, - 'chat_data': self.chat_data, - 'bot_data': self.bot_data, - 'callback_data': self.callback_data, + "conversations": self.conversations, + "user_data": self.user_data, + "chat_data": self.chat_data, + "bot_data": self.bot_data, + "callback_data": self.callback_data, } pickle.dump(data, file) diff --git a/telegram/ext/pollanswerhandler.py b/telegram/ext/pollanswerhandler.py index 199bcb3ad2b..6bafc1ffe3f 100644 --- a/telegram/ext/pollanswerhandler.py +++ b/telegram/ext/pollanswerhandler.py @@ -28,15 +28,6 @@ class PollAnswerHandler(Handler[Update, CCT]): """Handler class to handle Telegram updates that contain a poll answer. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -44,41 +35,15 @@ class PollAnswerHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ diff --git a/telegram/ext/pollhandler.py b/telegram/ext/pollhandler.py index 7b67e76ffb1..d23fa1b0af5 100644 --- a/telegram/ext/pollhandler.py +++ b/telegram/ext/pollhandler.py @@ -28,15 +28,6 @@ class PollHandler(Handler[Update, CCT]): """Handler class to handle Telegram updates that contain a poll. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -44,41 +35,15 @@ class PollHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ diff --git a/telegram/ext/precheckoutqueryhandler.py b/telegram/ext/precheckoutqueryhandler.py index 3a2eee30d0a..c79e7b44c0b 100644 --- a/telegram/ext/precheckoutqueryhandler.py +++ b/telegram/ext/precheckoutqueryhandler.py @@ -28,15 +28,6 @@ class PreCheckoutQueryHandler(Handler[Update, CCT]): """Handler class to handle Telegram PreCheckout callback queries. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -44,41 +35,15 @@ class PreCheckoutQueryHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - DEPRECATED: Please switch to context based callbacks. - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ diff --git a/telegram/ext/regexhandler.py b/telegram/ext/regexhandler.py deleted file mode 100644 index 399e4df7d94..00000000000 --- a/telegram/ext/regexhandler.py +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -# TODO: Remove allow_edited -"""This module contains the RegexHandler class.""" - -import warnings -from typing import TYPE_CHECKING, Callable, Dict, Optional, Pattern, TypeVar, Union, Any - -from telegram import Update -from telegram.ext import Filters, MessageHandler -from telegram.utils.deprecate import TelegramDeprecationWarning -from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE -from telegram.ext.utils.types import CCT - -if TYPE_CHECKING: - from telegram.ext import Dispatcher - -RT = TypeVar('RT') - - -class RegexHandler(MessageHandler): - """Handler class to handle Telegram updates based on a regex. - - It uses a regular expression to check text messages. Read the documentation of the ``re`` - module for more information. The ``re.match`` function is used to determine if an update should - be handled by this handler. - - Note: - This handler is being deprecated. For the same use case use: - ``MessageHandler(Filters.regex(r'pattern'), callback)`` - - Warning: - When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom - attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. - - - Args: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - callback (:obj:`callable`): The callback function for this handler. Will be called when - :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` - - The return value of the callback is usually ignored except for the special case of - :class:`telegram.ext.ConversationHandler`. - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - message_updates (:obj:`bool`, optional): Should "normal" message updates be handled? - Default is :obj:`True`. - channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled? - Default is :obj:`True`. - edited_updates (:obj:`bool`, optional): Should "edited" message updates be handled? Default - is :obj:`False`. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - Defaults to :obj:`False`. - - Raises: - ValueError - - Attributes: - pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. - callback (:obj:`callable`): The callback function for this handler. - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. - run_async (:obj:`bool`): Determines whether the callback will run asynchronously. - - """ - - __slots__ = ('pass_groups', 'pass_groupdict') - - def __init__( - self, - pattern: Union[str, Pattern], - callback: Callable[[Update, CCT], RT], - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, - pass_user_data: bool = False, - pass_chat_data: bool = False, - allow_edited: bool = False, # pylint: disable=W0613 - message_updates: bool = True, - channel_post_updates: bool = False, - edited_updates: bool = False, - run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, - ): - warnings.warn( - 'RegexHandler is deprecated. See https://git.io/fxJuV for more info', - TelegramDeprecationWarning, - stacklevel=2, - ) - super().__init__( - Filters.regex(pattern), - callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, - pass_user_data=pass_user_data, - pass_chat_data=pass_chat_data, - message_updates=message_updates, - channel_post_updates=channel_post_updates, - edited_updates=edited_updates, - run_async=run_async, - ) - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict - - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - 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: - optional_args['groups'] = check_result['matches'][0].groups() - if self.pass_groupdict: - optional_args['groupdict'] = check_result['matches'][0].groupdict() - return optional_args diff --git a/telegram/ext/shippingqueryhandler.py b/telegram/ext/shippingqueryhandler.py index e4229ceb738..17309b2d7e3 100644 --- a/telegram/ext/shippingqueryhandler.py +++ b/telegram/ext/shippingqueryhandler.py @@ -27,15 +27,6 @@ class ShippingQueryHandler(Handler[Update, CCT]): """Handler class to handle Telegram shipping callback queries. - Note: - :attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you - can use to keep any data in will be sent to the :attr:`callback` function. Related to - either the user or the chat that the update was sent in. For each update from the same user - or in the same chat, it will be the same ``dict``. - - Note that this is DEPRECATED, and you should use context based callbacks. See - https://git.io/fxJuV for more info. - Warning: When setting ``run_async`` to :obj:`True`, you cannot rely on adding custom attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info. @@ -43,41 +34,15 @@ class ShippingQueryHandler(Handler[Update, CCT]): Args: callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_user_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``user_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_chat_data (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``chat_data`` will be passed to the callback function. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: callback (:obj:`callable`): The callback function for this handler. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. - pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to - the callback function. - pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ diff --git a/telegram/ext/stringcommandhandler.py b/telegram/ext/stringcommandhandler.py index 1d84892e444..4da7dbff6c1 100644 --- a/telegram/ext/stringcommandhandler.py +++ b/telegram/ext/stringcommandhandler.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the StringCommandHandler class.""" -from typing import TYPE_CHECKING, Callable, Dict, List, Optional, TypeVar, Union +from typing import TYPE_CHECKING, Callable, List, Optional, TypeVar, Union from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE @@ -28,7 +28,7 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') +RT = TypeVar("RT") class StringCommandHandler(Handler[str, CCT]): @@ -49,62 +49,33 @@ class StringCommandHandler(Handler[str, CCT]): command (:obj:`str`): The command this handler should listen for. callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the - arguments passed to the command as a keyword argument called ``args``. It will contain - a list of strings, which is the text following the command split on single or - consecutive whitespace characters. Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: command (:obj:`str`): The command this handler should listen for. callback (:obj:`callable`): The callback function for this handler. - pass_args (:obj:`bool`): Determines whether the handler should be passed - ``args``. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('command', 'pass_args') + __slots__ = ("command",) def __init__( self, command: str, callback: Callable[[str, CCT], RT], - pass_args: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, run_async=run_async, ) self.command = command - self.pass_args = pass_args def check_update(self, update: object) -> Optional[List[str]]: """Determines whether an update should be passed to this handlers :attr:`callback`. @@ -116,31 +87,17 @@ def check_update(self, update: object) -> Optional[List[str]]: :obj:`bool` """ - if isinstance(update, str) and update.startswith('/'): - args = update[1:].split(' ') + if isinstance(update, str) and update.startswith("/"): + args = update[1:].split(" ") if args[0] == self.command: return args[1:] return None - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - 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 - return optional_args - def collect_additional_context( self, context: CCT, update: str, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Optional[List[str]], ) -> None: """Add text after the command to :attr:`CallbackContext.args` as list, split on single diff --git a/telegram/ext/stringregexhandler.py b/telegram/ext/stringregexhandler.py index 282c48ad70e..8f2db6166c0 100644 --- a/telegram/ext/stringregexhandler.py +++ b/telegram/ext/stringregexhandler.py @@ -19,7 +19,7 @@ """This module contains the StringRegexHandler class.""" import re -from typing import TYPE_CHECKING, Callable, Dict, Match, Optional, Pattern, TypeVar, Union +from typing import TYPE_CHECKING, Callable, Match, Optional, Pattern, TypeVar, Union from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE @@ -29,7 +29,7 @@ if TYPE_CHECKING: from telegram.ext import Dispatcher -RT = TypeVar('RT') +RT = TypeVar("RT") class StringRegexHandler(Handler[str, CCT]): @@ -50,64 +50,30 @@ class StringRegexHandler(Handler[str, CCT]): pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. - pass_groups (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groups()`` as a keyword argument called ``groups``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of - ``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``. - Default is :obj:`False` - DEPRECATED: Please switch to context based callbacks. - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. Attributes: pattern (:obj:`str` | :obj:`Pattern`): The regex pattern. callback (:obj:`callable`): The callback function for this handler. - pass_groups (:obj:`bool`): Determines whether ``groups`` will be passed to the - callback function. - pass_groupdict (:obj:`bool`): Determines whether ``groupdict``. will be passed to - the callback function. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('pass_groups', 'pass_groupdict', 'pattern') + __slots__ = ("pattern",) def __init__( self, pattern: Union[str, Pattern], callback: Callable[[str, CCT], RT], - pass_groups: bool = False, - pass_groupdict: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, run_async=run_async, ) @@ -115,8 +81,6 @@ def __init__( pattern = re.compile(pattern) self.pattern = pattern - self.pass_groups = pass_groups - self.pass_groupdict = pass_groupdict def check_update(self, update: object) -> Optional[Match]: """Determines whether an update should be passed to this handlers :attr:`callback`. @@ -134,29 +98,11 @@ def check_update(self, update: object) -> Optional[Match]: return match return None - def collect_optional_args( - self, - dispatcher: 'Dispatcher', - 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: - optional_args['groups'] = check_result.groups() - if self.pass_groupdict and check_result: - optional_args['groupdict'] = check_result.groupdict() - return optional_args - def collect_additional_context( self, context: CCT, update: str, - dispatcher: 'Dispatcher', + dispatcher: "Dispatcher", check_result: Optional[Match], ) -> None: """Add the result of ``re.match(pattern, update)`` to :attr:`CallbackContext.matches` as diff --git a/telegram/ext/typehandler.py b/telegram/ext/typehandler.py index 531d10c30fa..59abc3fa154 100644 --- a/telegram/ext/typehandler.py +++ b/telegram/ext/typehandler.py @@ -24,8 +24,8 @@ from .handler import Handler from .utils.types import CCT -RT = TypeVar('RT') -UT = TypeVar('UT') +RT = TypeVar("RT") +UT = TypeVar("UT") class TypeHandler(Handler[UT, CCT]): @@ -40,24 +40,12 @@ class TypeHandler(Handler[UT, CCT]): determined by ``isinstance`` callback (:obj:`callable`): The callback function for this handler. Will be called when :attr:`check_update` has determined that an update should be processed by this handler. - Callback signature for context based API: - - ``def callback(update: Update, context: CallbackContext)`` + Callback signature: ``def callback(update: Update, context: CallbackContext)`` The return value of the callback is usually ignored except for the special case of :class:`telegram.ext.ConversationHandler`. strict (:obj:`bool`, optional): Use ``type`` instead of ``isinstance``. Default is :obj:`False` - pass_update_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``update_queue`` will be passed to the callback function. It will be the ``Queue`` - instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher` - that contains new updates which can be used to insert updates. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. - pass_job_queue (:obj:`bool`, optional): If set to :obj:`True`, a keyword argument called - ``job_queue`` will be passed to the callback function. It will be a - :class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater` - which can be used to schedule new jobs. Default is :obj:`False`. - DEPRECATED: Please switch to context based callbacks. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. Defaults to :obj:`False`. @@ -65,29 +53,21 @@ class TypeHandler(Handler[UT, CCT]): type (:obj:`type`): The ``type`` of updates this handler should process. callback (:obj:`callable`): The callback function for this handler. strict (:obj:`bool`): Use ``type`` instead of ``isinstance``. Default is :obj:`False`. - pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be - passed to the callback function. - pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to - the callback function. run_async (:obj:`bool`): Determines whether the callback will run asynchronously. """ - __slots__ = ('type', 'strict') + __slots__ = ("type", "strict") def __init__( self, type: Type[UT], # pylint: disable=W0622 callback: Callable[[UT, CCT], RT], strict: bool = False, - pass_update_queue: bool = False, - pass_job_queue: bool = False, run_async: Union[bool, DefaultValue] = DEFAULT_FALSE, ): super().__init__( callback, - pass_update_queue=pass_update_queue, - pass_job_queue=pass_job_queue, run_async=run_async, ) self.type = type # pylint: disable=E0237 diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index 37a2e7e526a..6d184d8f1ef 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -42,7 +42,7 @@ from telegram import Bot, TelegramError from telegram.error import InvalidToken, RetryAfter, TimedOut, Unauthorized from telegram.ext import Dispatcher, JobQueue, ContextTypes, ExtBot -from telegram.utils.deprecate import TelegramDeprecationWarning, set_new_attribute_deprecated +from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.helpers import get_signal_name, DEFAULT_FALSE, DefaultValue from telegram.utils.request import Request from telegram.ext.utils.types import CCT, UD, CD, BD @@ -93,9 +93,6 @@ class Updater(Generic[CCT, UD, CD, BD]): `telegram.utils.request.Request` object (ignored if `bot` or `dispatcher` argument is used). The request_kwargs are very useful for the advanced users who would like to control the default timeouts and/or control the proxy used for http communication. - use_context (:obj:`bool`, optional): If set to :obj:`True` uses the context based callback - API (ignored if `dispatcher` argument is used). Defaults to :obj:`True`. - **New users**: set this to :obj:`True`. persistence (:class:`telegram.ext.BasePersistence`, optional): The persistence class to store data that should be persistent over restarts (ignored if `dispatcher` argument is used). @@ -129,32 +126,30 @@ class Updater(Generic[CCT, UD, CD, BD]): running (:obj:`bool`): Indicates if the updater is running. persistence (:class:`telegram.ext.BasePersistence`): Optional. The persistence class to store data that should be persistent over restarts. - use_context (:obj:`bool`): Optional. :obj:`True` if using context based callbacks. """ __slots__ = ( - 'persistence', - 'dispatcher', - 'user_sig_handler', - 'bot', - 'logger', - 'update_queue', - 'job_queue', - '__exception_event', - 'last_update_id', - 'running', - '_request', - 'is_idle', - 'httpd', - '__lock', - '__threads', - '__dict__', + "persistence", + "dispatcher", + "user_sig_handler", + "bot", + "logger", + "update_queue", + "job_queue", + "__exception_event", + "last_update_id", + "running", + "_request", + "is_idle", + "httpd", + "__lock", + "__threads", ) @overload def __init__( - self: 'Updater[CallbackContext, dict, dict, dict]', + self: "Updater[CallbackContext, dict, dict, dict]", token: str = None, base_url: str = None, workers: int = 4, @@ -163,9 +158,8 @@ def __init__( private_key_password: bytes = None, user_sig_handler: Callable = None, request_kwargs: Dict[str, Any] = None, - persistence: 'BasePersistence' = None, # pylint: disable=E0601 - defaults: 'Defaults' = None, - use_context: bool = True, + persistence: "BasePersistence" = None, # pylint: disable=E0601 + defaults: "Defaults" = None, base_file_url: str = None, arbitrary_callback_data: Union[DefaultValue, bool, int, None] = DEFAULT_FALSE, ): @@ -173,7 +167,7 @@ def __init__( @overload def __init__( - self: 'Updater[CCT, UD, CD, BD]', + self: "Updater[CCT, UD, CD, BD]", token: str = None, base_url: str = None, workers: int = 4, @@ -182,9 +176,8 @@ def __init__( private_key_password: bytes = None, user_sig_handler: Callable = None, request_kwargs: Dict[str, Any] = None, - persistence: 'BasePersistence' = None, - defaults: 'Defaults' = None, - use_context: bool = True, + persistence: "BasePersistence" = None, + defaults: "Defaults" = None, base_file_url: str = None, arbitrary_callback_data: Union[DefaultValue, bool, int, None] = DEFAULT_FALSE, context_types: ContextTypes[CCT, UD, CD, BD] = None, @@ -193,7 +186,7 @@ def __init__( @overload def __init__( - self: 'Updater[CCT, UD, CD, BD]', + self: "Updater[CCT, UD, CD, BD]", user_sig_handler: Callable = None, dispatcher: Dispatcher[CCT, UD, CD, BD] = None, ): @@ -209,9 +202,8 @@ def __init__( # type: ignore[no-untyped-def,misc] private_key_password: bytes = None, user_sig_handler: Callable = None, request_kwargs: Dict[str, Any] = None, - persistence: 'BasePersistence' = None, - defaults: 'Defaults' = None, - use_context: bool = True, + persistence: "BasePersistence" = None, + defaults: "Defaults" = None, dispatcher=None, base_file_url: str = None, arbitrary_callback_data: Union[DefaultValue, bool, int, None] = DEFAULT_FALSE, @@ -220,36 +212,34 @@ def __init__( # type: ignore[no-untyped-def,misc] if defaults and bot: warnings.warn( - 'Passing defaults to an Updater has no effect when a Bot is passed ' - 'as well. Pass them to the Bot instead.', + "Passing defaults to an Updater has no effect when a Bot is passed " + "as well. Pass them to the Bot instead.", TelegramDeprecationWarning, stacklevel=2, ) if arbitrary_callback_data is not DEFAULT_FALSE and bot: warnings.warn( - 'Passing arbitrary_callback_data to an Updater has no ' - 'effect when a Bot is passed as well. Pass them to the Bot instead.', + "Passing arbitrary_callback_data to an Updater has no " + "effect when a Bot is passed as well. Pass them to the Bot instead.", stacklevel=2, ) if dispatcher is None: if (token is None) and (bot is None): - raise ValueError('`token` or `bot` must be passed') + raise ValueError("`token` or `bot` must be passed") if (token is not None) and (bot is not None): - raise ValueError('`token` and `bot` are mutually exclusive') + raise ValueError("`token` and `bot` are mutually exclusive") if (private_key is not None) and (bot is not None): - raise ValueError('`bot` and `private_key` are mutually exclusive') + raise ValueError("`bot` and `private_key` are mutually exclusive") else: if bot is not None: - raise ValueError('`dispatcher` and `bot` are mutually exclusive') + raise ValueError("`dispatcher` and `bot` are mutually exclusive") if persistence is not None: - raise ValueError('`dispatcher` and `persistence` are mutually exclusive') - if use_context != dispatcher.use_context: - raise ValueError('`dispatcher` and `use_context` are mutually exclusive') + raise ValueError("`dispatcher` and `persistence` are mutually exclusive") if context_types is not None: - raise ValueError('`dispatcher` and `context_types` are mutually exclusive') + raise ValueError("`dispatcher` and `context_types` are mutually exclusive") if workers is not None: - raise ValueError('`dispatcher` and `workers` are mutually exclusive') + raise ValueError("`dispatcher` and `workers` are mutually exclusive") self.logger = logging.getLogger(__name__) self._request = None @@ -261,7 +251,7 @@ def __init__( # type: ignore[no-untyped-def,misc] self.bot = bot if bot.request.con_pool_size < con_pool_size: self.logger.warning( - 'Connection pool of Request object is smaller than optimal value (%s)', + "Connection pool of Request object is smaller than optimal value (%s)", con_pool_size, ) else: @@ -273,8 +263,8 @@ def __init__( # type: ignore[no-untyped-def,misc] # * 1 for main thread if request_kwargs is None: request_kwargs = {} - if 'con_pool_size' not in request_kwargs: - request_kwargs['con_pool_size'] = con_pool_size + if "con_pool_size" not in request_kwargs: + request_kwargs["con_pool_size"] = con_pool_size self._request = Request(**request_kwargs) self.bot = ExtBot( token, # type: ignore[arg-type] @@ -301,7 +291,6 @@ def __init__( # type: ignore[no-untyped-def,misc] workers=workers, exception_event=self.__exception_event, persistence=persistence, - use_context=use_context, context_types=context_types, ) self.job_queue.set_dispatcher(self.dispatcher) @@ -311,7 +300,7 @@ def __init__( # type: ignore[no-untyped-def,misc] self.bot = dispatcher.bot if self.bot.request.con_pool_size < con_pool_size: self.logger.warning( - 'Connection pool of Request object is smaller than optimal value (%s)', + "Connection pool of Request object is smaller than optimal value (%s)", con_pool_size, ) self.update_queue = dispatcher.update_queue @@ -328,14 +317,6 @@ def __init__( # type: ignore[no-untyped-def,misc] self.__lock = Lock() self.__threads: List[Thread] = [] - def __setattr__(self, key: str, value: object) -> None: - if key.startswith('__'): - key = f"_{self.__class__.__name__}{key}" - if issubclass(self.__class__, Updater) and self.__class__ is not Updater: - object.__setattr__(self, key, value) - return - set_new_attribute_deprecated(self, key, value) - def _init_thread(self, target: Callable, name: str, *args: object, **kwargs: object) -> None: thr = Thread( target=self._thread_wrapper, @@ -348,20 +329,19 @@ def _init_thread(self, target: Callable, name: str, *args: object, **kwargs: obj def _thread_wrapper(self, target: Callable, *args: object, **kwargs: object) -> None: thr_name = current_thread().name - self.logger.debug('%s - started', thr_name) + self.logger.debug("%s - started", thr_name) try: target(*args, **kwargs) except Exception: self.__exception_event.set() - self.logger.exception('unhandled exception in %s', thr_name) + self.logger.exception("unhandled exception in %s", thr_name) raise - self.logger.debug('%s - ended', thr_name) + self.logger.debug("%s - ended", thr_name) def start_polling( self, poll_interval: float = 0.0, timeout: float = 10, - clean: bool = None, bootstrap_retries: int = -1, read_latency: float = 2.0, allowed_updates: List[str] = None, @@ -369,6 +349,9 @@ def start_polling( ) -> Optional[Queue]: """Starts polling updates from Telegram. + .. versionchanged:: 14.0 + Removed the ``clean`` argument in favor of ``drop_pending_updates``. + Args: poll_interval (:obj:`float`, optional): Time to wait between polling updates from Telegram in seconds. Default is ``0.0``. @@ -377,10 +360,6 @@ def start_polling( Telegram servers before actually starting to poll. Default is :obj:`False`. .. versionadded :: 13.4 - clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``. - - .. deprecated:: 13.4 - Use ``drop_pending_updates`` instead. bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the :class:`telegram.ext.Updater` will retry on failures on the Telegram server. @@ -398,19 +377,6 @@ def start_polling( :obj:`Queue`: The update queue that can be filled from the main thread. """ - if (clean is not None) and (drop_pending_updates is not None): - raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.') - - if clean is not None: - warnings.warn( - 'The argument `clean` of `start_polling` is deprecated. Please use ' - '`drop_pending_updates` instead.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean - with self.__lock: if not self.running: self.running = True @@ -432,7 +398,7 @@ def start_polling( ready=polling_ready, ) - self.logger.debug('Waiting for Dispatcher and polling to start') + self.logger.debug("Waiting for Dispatcher and polling to start") dispatcher_ready.wait() polling_ready.wait() @@ -442,16 +408,14 @@ def start_polling( def start_webhook( self, - listen: str = '127.0.0.1', + listen: str = "127.0.0.1", port: int = 80, - url_path: str = '', + url_path: str = "", cert: str = None, key: str = None, - clean: bool = None, bootstrap_retries: int = 0, webhook_url: str = None, allowed_updates: List[str] = None, - force_event_loop: bool = None, drop_pending_updates: bool = None, ip_address: str = None, max_connections: int = 40, @@ -467,6 +431,10 @@ def start_webhook( :meth:`start_webhook` now *always* calls :meth:`telegram.Bot.set_webhook`, so pass ``webhook_url`` instead of calling ``updater.bot.set_webhook(webhook_url)`` manually. + .. versionchanged:: 14.0 + Removed the ``clean`` argument in favor of ``drop_pending_updates`` and removed the + deprecated argument ``force_event_loop``. + Args: listen (:obj:`str`, optional): IP-Address to listen on. Default ``127.0.0.1``. port (:obj:`int`, optional): Port the bot should be listening on. Default ``80``. @@ -477,10 +445,6 @@ def start_webhook( Telegram servers before actually starting to poll. Default is :obj:`False`. .. versionadded :: 13.4 - clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``. - - .. deprecated:: 13.4 - Use ``drop_pending_updates`` instead. bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the :class:`telegram.ext.Updater` will retry on failures on the Telegram server. @@ -496,13 +460,6 @@ def start_webhook( .. versionadded :: 13.4 allowed_updates (List[:obj:`str`], optional): Passed to :meth:`telegram.Bot.set_webhook`. - force_event_loop (:obj:`bool`, optional): Legacy parameter formerly used for a - workaround on Windows + Python 3.8+. No longer has any effect. - - .. deprecated:: 13.6 - Since version 13.6, ``tornade>=6.1`` is required, which resolves the former - issue. - max_connections (:obj:`int`, optional): Passed to :meth:`telegram.Bot.set_webhook`. @@ -512,27 +469,6 @@ def start_webhook( :obj:`Queue`: The update queue that can be filled from the main thread. """ - if (clean is not None) and (drop_pending_updates is not None): - raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.') - - if clean is not None: - warnings.warn( - 'The argument `clean` of `start_webhook` is deprecated. Please use ' - '`drop_pending_updates` instead.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - if force_event_loop is not None: - warnings.warn( - 'The argument `force_event_loop` of `start_webhook` is deprecated and no longer ' - 'has any effect.', - category=TelegramDeprecationWarning, - stacklevel=2, - ) - - drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean - with self.__lock: if not self.running: self.running = True @@ -559,7 +495,7 @@ def start_webhook( max_connections=max_connections, ) - self.logger.debug('Waiting for Dispatcher and Webhook to start') + self.logger.debug("Waiting for Dispatcher and Webhook to start") webhook_ready.wait() dispatcher_ready.wait() @@ -582,16 +518,16 @@ def _start_polling( # updates from Telegram and inserts them in the update queue of the # Dispatcher. - self.logger.debug('Updater thread started (polling)') + self.logger.debug("Updater thread started (polling)") self._bootstrap( bootstrap_retries, drop_pending_updates=drop_pending_updates, - webhook_url='', + webhook_url="", allowed_updates=None, ) - self.logger.debug('Bootstrap done') + self.logger.debug("Bootstrap done") def polling_action_cb(): updates = self.bot.get_updates( @@ -603,7 +539,7 @@ def polling_action_cb(): if updates: if not self.running: - self.logger.debug('Updates ignored and will be pulled again on restart') + self.logger.debug("Updates ignored and will be pulled again on restart") else: for update in updates: self.update_queue.put(update) @@ -620,7 +556,7 @@ def polling_onerr_cb(exc): ready.set() self._network_loop_retry( - polling_action_cb, polling_onerr_cb, 'getting Updates', poll_interval + polling_action_cb, polling_onerr_cb, "getting Updates", poll_interval ) @no_type_check @@ -639,24 +575,24 @@ def _network_loop_retry(self, action_cb, onerr_cb, description, interval): `action_cb`. """ - self.logger.debug('Start network loop retry %s', description) + self.logger.debug("Start network loop retry %s", description) cur_interval = interval while self.running: try: if not action_cb(): break except RetryAfter as exc: - self.logger.info('%s', exc) + self.logger.info("%s", exc) cur_interval = 0.5 + exc.retry_after except TimedOut as toe: - self.logger.debug('Timed out %s: %s', description, toe) + self.logger.debug("Timed out %s: %s", description, toe) # If failure is due to timeout, we should retry asap. cur_interval = 0 except InvalidToken as pex: - self.logger.error('Invalid token; aborting') + self.logger.error("Invalid token; aborting") raise pex except TelegramError as telegram_exc: - self.logger.error('Error while %s: %s', description, telegram_exc) + self.logger.error("Error while %s: %s", description, telegram_exc) onerr_cb(telegram_exc) cur_interval = self._increase_poll_interval(cur_interval) else: @@ -692,15 +628,15 @@ def _start_webhook( ip_address=None, max_connections: int = 40, ): - self.logger.debug('Updater thread started (webhook)') + self.logger.debug("Updater thread started (webhook)") # Note that we only use the SSL certificate for the WebhookServer, if the key is also # present. This is because the WebhookServer may not actually be in charge of performing # the SSL handshake, e.g. in case a reverse proxy is used use_ssl = cert is not None and key is not None - if not url_path.startswith('/'): - url_path = f'/{url_path}' + if not url_path.startswith("/"): + url_path = f"/{url_path}" # Create Tornado app instance app = WebhookAppClass(url_path, self.bot, self.update_queue) @@ -712,7 +648,7 @@ def _start_webhook( ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(cert, key) except ssl.SSLError as exc: - raise TelegramError('Invalid SSL Certificate') from exc + raise TelegramError("Invalid SSL Certificate") from exc else: ssl_ctx = None @@ -723,7 +659,7 @@ def _start_webhook( 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 + cert_file = open(cert, "rb") if cert is not None else None self._bootstrap( max_retries=bootstrap_retries, drop_pending_updates=drop_pending_updates, @@ -740,7 +676,7 @@ def _start_webhook( @staticmethod def _gen_webhook_url(listen: str, port: int, url_path: str) -> str: - return f'https://{listen}:{port}{url_path}' + return f"https://{listen}:{port}{url_path}" @no_type_check def _bootstrap( @@ -757,16 +693,16 @@ def _bootstrap( retries = [0] def bootstrap_del_webhook(): - self.logger.debug('Deleting webhook') + self.logger.debug("Deleting webhook") if drop_pending_updates: - self.logger.debug('Dropping pending updates from Telegram server') + self.logger.debug("Dropping pending updates from Telegram server") self.bot.delete_webhook(drop_pending_updates=drop_pending_updates) return False def bootstrap_set_webhook(): - self.logger.debug('Setting webhook') + self.logger.debug("Setting webhook") if drop_pending_updates: - self.logger.debug('Dropping pending updates from Telegram server') + self.logger.debug("Dropping pending updates from Telegram server") self.bot.set_webhook( url=webhook_url, certificate=cert, @@ -781,10 +717,10 @@ def bootstrap_onerr_cb(exc): if not isinstance(exc, Unauthorized) and (max_retries < 0 or retries[0] < max_retries): retries[0] += 1 self.logger.warning( - 'Failed bootstrap phase; try=%s max_retries=%s', retries[0], max_retries + "Failed bootstrap phase; try=%s max_retries=%s", retries[0], max_retries ) else: - self.logger.error('Failed bootstrap phase after %s retries (%s)', retries[0], exc) + self.logger.error("Failed bootstrap phase after %s retries (%s)", retries[0], exc) raise exc # Dropping pending updates from TG can be efficiently done with the drop_pending_updates @@ -795,7 +731,7 @@ def bootstrap_onerr_cb(exc): self._network_loop_retry( bootstrap_del_webhook, bootstrap_onerr_cb, - 'bootstrap del webhook', + "bootstrap del webhook", bootstrap_interval, ) retries[0] = 0 @@ -806,7 +742,7 @@ def bootstrap_onerr_cb(exc): self._network_loop_retry( bootstrap_set_webhook, bootstrap_onerr_cb, - 'bootstrap set webhook', + "bootstrap set webhook", bootstrap_interval, ) @@ -815,7 +751,7 @@ def stop(self) -> None: self.job_queue.stop() with self.__lock: if self.running or self.dispatcher.has_running_threads: - self.logger.debug('Stopping Updater and Dispatcher...') + self.logger.debug("Stopping Updater and Dispatcher...") self.running = False @@ -831,24 +767,24 @@ def stop(self) -> None: def _stop_httpd(self) -> None: if self.httpd: self.logger.debug( - 'Waiting for current webhook connection to be ' - 'closed... Send a Telegram message to the bot to exit ' - 'immediately.' + "Waiting for current webhook connection to be " + "closed... Send a Telegram message to the bot to exit " + "immediately." ) self.httpd.shutdown() self.httpd = None @no_type_check def _stop_dispatcher(self) -> None: - self.logger.debug('Requesting Dispatcher to stop...') + self.logger.debug("Requesting Dispatcher to stop...") self.dispatcher.stop() @no_type_check def _join_threads(self) -> None: for thr in self.__threads: - self.logger.debug('Waiting for %s thread to end', thr.name) + self.logger.debug("Waiting for %s thread to end", thr.name) thr.join() - self.logger.debug('%s thread has ended', thr.name) + self.logger.debug("%s thread has ended", thr.name) self.__threads = [] @no_type_check @@ -856,7 +792,7 @@ def _signal_handler(self, signum, frame) -> None: self.is_idle = False if self.running: self.logger.info( - 'Received signal %s (%s), stopping...', signum, get_signal_name(signum) + "Received signal %s (%s), stopping...", signum, get_signal_name(signum) ) if self.persistence: # Update user_data, chat_data and bot_data before flushing @@ -866,7 +802,7 @@ def _signal_handler(self, signum, frame) -> None: if self.user_sig_handler: self.user_sig_handler(signum, frame) else: - self.logger.warning('Exiting immediately!') + self.logger.warning("Exiting immediately!") # pylint: disable=C0415,W0212 import os diff --git a/telegram/ext/utils/promise.py b/telegram/ext/utils/promise.py index 6b548242972..2959735e6c2 100644 --- a/telegram/ext/utils/promise.py +++ b/telegram/ext/utils/promise.py @@ -22,10 +22,9 @@ from threading import Event from typing import Callable, List, Optional, Tuple, TypeVar, Union -from telegram.utils.deprecate import set_new_attribute_deprecated from telegram.utils.types import JSONDict -RT = TypeVar('RT') +RT = TypeVar("RT") logger = logging.getLogger(__name__) @@ -34,14 +33,15 @@ class Promise: """A simple Promise implementation for use with the run_async decorator, DelayQueue etc. + .. versionchanged:: 14.0 + Removed the argument and attribute ``error_handler``. + Args: pooled_function (:obj:`callable`): The callable that will be called concurrently. args (:obj:`list` | :obj:`tuple`): Positional arguments for :attr:`pooled_function`. kwargs (:obj:`dict`): Keyword arguments for :attr:`pooled_function`. update (:class:`telegram.Update` | :obj:`object`, optional): The update this promise is associated with. - error_handling (:obj:`bool`, optional): Whether exceptions raised by :attr:`func` - may be handled by error handlers. Defaults to :obj:`True`. Attributes: pooled_function (:obj:`callable`): The callable that will be called concurrently. @@ -50,46 +50,36 @@ class Promise: done (:obj:`threading.Event`): Is set when the result is available. update (:class:`telegram.Update` | :obj:`object`): Optional. The update this promise is associated with. - error_handling (:obj:`bool`): Optional. Whether exceptions raised by :attr:`func` - may be handled by error handlers. Defaults to :obj:`True`. """ __slots__ = ( - 'pooled_function', - 'args', - 'kwargs', - 'update', - 'error_handling', - 'done', - '_done_callback', - '_result', - '_exception', - '__dict__', + "pooled_function", + "args", + "kwargs", + "update", + "done", + "_done_callback", + "_result", + "_exception", ) - # TODO: Remove error_handling parameter once we drop the @run_async decorator def __init__( self, pooled_function: Callable[..., RT], args: Union[List, Tuple], kwargs: JSONDict, update: object = None, - error_handling: bool = True, ): self.pooled_function = pooled_function self.args = args self.kwargs = kwargs self.update = update - self.error_handling = error_handling self.done = Event() self._done_callback: Optional[Callable] = None self._result: Optional[RT] = None self._exception: Optional[Exception] = None - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - def run(self) -> None: """Calls the :attr:`pooled_function` callable.""" try: diff --git a/telegram/ext/utils/types.py b/telegram/ext/utils/types.py index b7152f6e142..0e73379b26b 100644 --- a/telegram/ext/utils/types.py +++ b/telegram/ext/utils/types.py @@ -40,22 +40,22 @@ .. versionadded:: 13.6 """ -CCT = TypeVar('CCT', bound='CallbackContext') +CCT = TypeVar("CCT", bound="CallbackContext") """An instance of :class:`telegram.ext.CallbackContext` or a custom subclass. .. versionadded:: 13.6 """ -UD = TypeVar('UD') +UD = TypeVar("UD") """Type of the user data for a single user. .. versionadded:: 13.6 """ -CD = TypeVar('CD') +CD = TypeVar("CD") """Type of the chat data for a single user. .. versionadded:: 13.6 """ -BD = TypeVar('BD') +BD = TypeVar("BD") """Type of the bot data. .. versionadded:: 13.6 diff --git a/telegram/ext/utils/webhookhandler.py b/telegram/ext/utils/webhookhandler.py index ddf5e6904e9..bcf2f1415a8 100644 --- a/telegram/ext/utils/webhookhandler.py +++ b/telegram/ext/utils/webhookhandler.py @@ -31,7 +31,6 @@ from telegram import Update from telegram.ext import ExtBot -from telegram.utils.deprecate import set_new_attribute_deprecated from telegram.utils.types import JSONDict if TYPE_CHECKING: @@ -45,19 +44,18 @@ class WebhookServer: __slots__ = ( - 'http_server', - 'listen', - 'port', - 'loop', - 'logger', - 'is_running', - 'server_lock', - 'shutdown_lock', - '__dict__', + "http_server", + "listen", + "port", + "loop", + "logger", + "is_running", + "server_lock", + "shutdown_lock", ) def __init__( - self, listen: str, port: int, webhook_app: 'WebhookAppClass', ssl_ctx: SSLContext + self, listen: str, port: int, webhook_app: "WebhookAppClass", ssl_ctx: SSLContext ): self.http_server = HTTPServer(webhook_app, ssl_options=ssl_ctx) self.listen = listen @@ -68,14 +66,11 @@ def __init__( self.server_lock = Lock() self.shutdown_lock = Lock() - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - def serve_forever(self, ready: Event = None) -> None: with self.server_lock: IOLoop().make_current() self.is_running = True - self.logger.debug('Webhook Server started.') + self.logger.debug("Webhook Server started.") self.loop = IOLoop.current() self.http_server.listen(self.port, address=self.listen) @@ -83,27 +78,27 @@ def serve_forever(self, ready: Event = None) -> None: ready.set() self.loop.start() - self.logger.debug('Webhook Server stopped.') + self.logger.debug("Webhook Server stopped.") self.is_running = False def shutdown(self) -> None: with self.shutdown_lock: if not self.is_running: - self.logger.warning('Webhook Server already stopped.') + self.logger.warning("Webhook Server already stopped.") return self.loop.add_callback(self.loop.stop) # type: ignore def handle_error(self, request: object, client_address: str) -> None: # pylint: disable=W0613 """Handle an error gracefully.""" self.logger.debug( - 'Exception happened during processing of request from %s', + "Exception happened during processing of request from %s", client_address, exc_info=True, ) class WebhookAppClass(tornado.web.Application): - def __init__(self, webhook_path: str, bot: 'Bot', update_queue: Queue): + def __init__(self, webhook_path: str, bot: "Bot", update_queue: Queue): self.shared_objects = {"bot": bot, "update_queue": update_queue} handlers = [(rf"{webhook_path}/?", WebhookHandler, self.shared_objects)] # noqa tornado.web.Application.__init__(self, handlers) # type: ignore @@ -126,7 +121,7 @@ def __init__( super().__init__(application, request, **kwargs) self.logger = logging.getLogger(__name__) - def initialize(self, bot: 'Bot', update_queue: Queue) -> None: + def initialize(self, bot: "Bot", update_queue: Queue) -> None: # pylint: disable=W0201 self.bot = bot self.update_queue = update_queue @@ -135,15 +130,15 @@ def set_default_headers(self) -> None: self.set_header("Content-Type", 'application/json; charset="utf-8"') def post(self) -> None: - self.logger.debug('Webhook triggered') + self.logger.debug("Webhook triggered") self._validate_post() json_string = self.request.body.decode() data = json.loads(json_string) self.set_status(200) - self.logger.debug('Webhook received data: %s', json_string) + self.logger.debug("Webhook received data: %s", json_string) update = Update.de_json(data, self.bot) if update: - self.logger.debug('Received Update with ID %d on Webhook', update.update_id) + self.logger.debug("Received Update with ID %d on Webhook", update.update_id) # handle arbitrary callback data, if necessary if isinstance(self.bot, ExtBot): self.bot.insert_callback_data(update) @@ -151,7 +146,7 @@ def post(self) -> None: def _validate_post(self) -> None: ct_header = self.request.headers.get("Content-Type", None) - if ct_header != 'application/json': + if ct_header != "application/json": raise tornado.web.HTTPError(403) def write_error(self, status_code: int, **kwargs: Any) -> None: @@ -173,5 +168,5 @@ def write_error(self, status_code: int, **kwargs: Any) -> None: "%s - - %s", self.request.remote_ip, "Exception in WebhookHandler", - exc_info=kwargs['exc_info'], + exc_info=kwargs["exc_info"], ) diff --git a/telegram/files/animation.py b/telegram/files/animation.py index 199cf332826..bc0a82c3b33 100644 --- a/telegram/files/animation.py +++ b/telegram/files/animation.py @@ -66,17 +66,16 @@ class Animation(TelegramObject): """ __slots__ = ( - 'bot', - 'width', - 'file_id', - 'file_size', - 'file_name', - 'thumb', - 'duration', - 'mime_type', - 'height', - 'file_unique_id', - '_id_attrs', + "bot", + "width", + "file_id", + "file_size", + "file_name", + "thumb", + "duration", + "mime_type", + "height", + "file_unique_id", ) def __init__( @@ -90,7 +89,7 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -109,20 +108,20 @@ def __init__( self._id_attrs = (self.file_unique_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Animation']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Animation"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) return cls(bot=bot, **data) def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/files/audio.py b/telegram/files/audio.py index d95711acd96..2df99709fc2 100644 --- a/telegram/files/audio.py +++ b/telegram/files/audio.py @@ -70,17 +70,16 @@ class Audio(TelegramObject): """ __slots__ = ( - 'file_id', - 'bot', - 'file_size', - 'file_name', - 'thumb', - 'title', - 'duration', - 'performer', - 'mime_type', - 'file_unique_id', - '_id_attrs', + "file_id", + "bot", + "file_size", + "file_name", + "thumb", + "title", + "duration", + "performer", + "mime_type", + "file_unique_id", ) def __init__( @@ -93,7 +92,7 @@ def __init__( mime_type: str = None, file_size: int = None, thumb: PhotoSize = None, - bot: 'Bot' = None, + bot: "Bot" = None, file_name: str = None, **_kwargs: Any, ): @@ -113,20 +112,20 @@ def __init__( self._id_attrs = (self.file_unique_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Audio']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Audio"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) return cls(bot=bot, **data) def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/files/chatphoto.py b/telegram/files/chatphoto.py index 5302c7e9826..b397de78fbe 100644 --- a/telegram/files/chatphoto.py +++ b/telegram/files/chatphoto.py @@ -66,12 +66,11 @@ class ChatPhoto(TelegramObject): """ __slots__ = ( - 'big_file_unique_id', - 'bot', - 'small_file_id', - 'small_file_unique_id', - 'big_file_id', - '_id_attrs', + "big_file_unique_id", + "bot", + "small_file_id", + "small_file_unique_id", + "big_file_id", ) def __init__( @@ -80,7 +79,7 @@ def __init__( small_file_unique_id: str, big_file_id: str, big_file_unique_id: str, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): self.small_file_id = small_file_id @@ -97,7 +96,7 @@ def __init__( def get_small_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the small (160x160) chat photo @@ -116,7 +115,7 @@ def get_small_file( def get_big_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` for getting the big (640x640) chat photo diff --git a/telegram/files/contact.py b/telegram/files/contact.py index 257fdf474be..9c3432dc978 100644 --- a/telegram/files/contact.py +++ b/telegram/files/contact.py @@ -46,7 +46,7 @@ class Contact(TelegramObject): """ - __slots__ = ('vcard', 'user_id', 'first_name', 'last_name', 'phone_number', '_id_attrs') + __slots__ = ("vcard", "user_id", "first_name", "last_name", "phone_number") def __init__( self, diff --git a/telegram/files/document.py b/telegram/files/document.py index dad9f9bf37f..8bf06d97bb6 100644 --- a/telegram/files/document.py +++ b/telegram/files/document.py @@ -61,18 +61,15 @@ class Document(TelegramObject): """ __slots__ = ( - 'bot', - 'file_id', - 'file_size', - 'file_name', - 'thumb', - 'mime_type', - 'file_unique_id', - '_id_attrs', + "bot", + "file_id", + "file_size", + "file_name", + "thumb", + "mime_type", + "file_unique_id", ) - _id_keys = ('file_id',) - def __init__( self, file_id: str, @@ -81,7 +78,7 @@ def __init__( file_name: str = None, mime_type: str = None, file_size: int = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -97,20 +94,20 @@ def __init__( self._id_attrs = (self.file_unique_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Document']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Document"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) return cls(bot=bot, **data) def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/files/file.py b/telegram/files/file.py index c3391bd95ca..9ecaf17164b 100644 --- a/telegram/files/file.py +++ b/telegram/files/file.py @@ -68,20 +68,19 @@ class File(TelegramObject): """ __slots__ = ( - 'bot', - 'file_id', - 'file_size', - 'file_unique_id', - 'file_path', - '_credentials', - '_id_attrs', + "bot", + "file_id", + "file_size", + "file_unique_id", + "file_path", + "_credentials", ) def __init__( self, file_id: str, file_unique_id: str, - bot: 'Bot' = None, + bot: "Bot" = None, file_size: int = None, file_path: str = None, **_kwargs: Any, @@ -93,7 +92,7 @@ def __init__( self.file_size = file_size self.file_path = file_path self.bot = bot - self._credentials: Optional['FileCredentials'] = None + self._credentials: Optional["FileCredentials"] = None self._id_attrs = (self.file_unique_id,) @@ -130,7 +129,7 @@ def download( """ if custom_path is not None and out is not None: - raise ValueError('custom_path and out are mutually exclusive') + raise ValueError("custom_path and out are mutually exclusive") local_file = is_local_file(self.file_path) @@ -142,7 +141,7 @@ def download( if out: if local_file: - with open(url, 'rb') as file: + with open(url, "rb") as file: buf = file.read() else: buf = self.bot.request.retrieve(url) @@ -171,7 +170,7 @@ def download( buf = decrypt( b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf ) - with open(filename, 'wb') as fobj: + with open(filename, "wb") as fobj: fobj.write(buf) return filename @@ -204,7 +203,7 @@ def download_as_bytearray(self, buf: bytearray = None) -> bytes: buf.extend(self.bot.request.retrieve(self._get_encoded_url())) return buf - def set_credentials(self, credentials: 'FileCredentials') -> None: + def set_credentials(self, credentials: "FileCredentials") -> None: """Sets the passport credentials for the file. Args: diff --git a/telegram/files/inputfile.py b/telegram/files/inputfile.py index 583f4a60d61..7d456046206 100644 --- a/telegram/files/inputfile.py +++ b/telegram/files/inputfile.py @@ -26,9 +26,7 @@ from typing import IO, Optional, Tuple, Union from uuid import uuid4 -from telegram.utils.deprecate import set_new_attribute_deprecated - -DEFAULT_MIME_TYPE = 'application/octet-stream' +DEFAULT_MIME_TYPE = "application/octet-stream" logger = logging.getLogger(__name__) @@ -52,7 +50,7 @@ class InputFile: """ - __slots__ = ('filename', 'attach', 'input_file_content', 'mimetype', '__dict__') + __slots__ = ("filename", "attach", "input_file_content", "mimetype") def __init__(self, obj: Union[IO, bytes], filename: str = None, attach: bool = None): self.filename = None @@ -60,11 +58,11 @@ def __init__(self, obj: Union[IO, bytes], filename: str = None, attach: bool = N self.input_file_content = obj else: self.input_file_content = obj.read() - self.attach = 'attached' + uuid4().hex if attach else None + self.attach = "attached" + uuid4().hex if attach else None if filename: self.filename = filename - elif hasattr(obj, 'name') and not isinstance(obj.name, int): # type: ignore[union-attr] + elif hasattr(obj, "name") and not isinstance(obj.name, int): # type: ignore[union-attr] self.filename = os.path.basename(obj.name) # type: ignore[union-attr] image_mime_type = self.is_image(self.input_file_content) @@ -76,10 +74,7 @@ def __init__(self, obj: Union[IO, bytes], filename: str = None, attach: bool = N self.mimetype = DEFAULT_MIME_TYPE if not self.filename: - self.filename = self.mimetype.replace('/', '.') - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) + self.filename = self.mimetype.replace("/", ".") @property def field_tuple(self) -> Tuple[str, bytes, str]: # skipcq: PY-D0003 @@ -100,7 +95,7 @@ def is_image(stream: bytes) -> Optional[str]: try: image = imghdr.what(None, stream) if image: - return f'image/{image}' + return f"image/{image}" return None except Exception: logger.debug( @@ -110,10 +105,10 @@ def is_image(stream: bytes) -> Optional[str]: @staticmethod def is_file(obj: object) -> bool: # skipcq: PY-D0003 - return hasattr(obj, 'read') + return hasattr(obj, "read") def to_dict(self) -> Optional[str]: """See :meth:`telegram.TelegramObject.to_dict`.""" if self.attach: - return 'attach://' + self.attach + return "attach://" + self.attach return None diff --git a/telegram/files/inputmedia.py b/telegram/files/inputmedia.py index f59cf4d01bd..c7d09976b9e 100644 --- a/telegram/files/inputmedia.py +++ b/telegram/files/inputmedia.py @@ -51,7 +51,7 @@ def to_dict(self) -> JSONDict: data = super().to_dict() if self.caption_entities: - data['caption_entities'] = [ + data["caption_entities"] = [ ce.to_dict() for ce in self.caption_entities # pylint: disable=E1133 ] @@ -115,15 +115,15 @@ class InputMediaAnimation(InputMedia): """ __slots__ = ( - 'caption_entities', - 'width', - 'media', - 'thumb', - 'caption', - 'duration', - 'parse_mode', - 'height', - 'type', + "caption_entities", + "width", + "media", + "thumb", + "caption", + "duration", + "parse_mode", + "height", + "type", ) def __init__( @@ -138,7 +138,7 @@ def __init__( caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, ): - self.type = 'animation' + self.type = "animation" if isinstance(media, Animation): self.media: Union[str, InputFile] = media.file_id @@ -198,7 +198,7 @@ class InputMediaPhoto(InputMedia): """ - __slots__ = ('caption_entities', 'media', 'caption', 'parse_mode', 'type') + __slots__ = ("caption_entities", "media", "caption", "parse_mode", "type") def __init__( self, @@ -208,7 +208,7 @@ def __init__( caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, ): - self.type = 'photo' + self.type = "photo" self.media = parse_file_input(media, PhotoSize, attach=True, filename=filename) if caption: @@ -281,16 +281,16 @@ class InputMediaVideo(InputMedia): """ __slots__ = ( - 'caption_entities', - 'width', - 'media', - 'thumb', - 'supports_streaming', - 'caption', - 'duration', - 'parse_mode', - 'height', - 'type', + "caption_entities", + "width", + "media", + "thumb", + "supports_streaming", + "caption", + "duration", + "parse_mode", + "height", + "type", ) def __init__( @@ -306,7 +306,7 @@ def __init__( caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, ): - self.type = 'video' + self.type = "video" if isinstance(media, Video): self.media: Union[str, InputFile] = media.file_id @@ -393,15 +393,15 @@ class InputMediaAudio(InputMedia): """ __slots__ = ( - 'caption_entities', - 'media', - 'thumb', - 'caption', - 'title', - 'duration', - 'type', - 'parse_mode', - 'performer', + "caption_entities", + "media", + "thumb", + "caption", + "title", + "duration", + "type", + "parse_mode", + "performer", ) def __init__( @@ -416,7 +416,7 @@ def __init__( caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, ): - self.type = 'audio' + self.type = "audio" if isinstance(media, Audio): self.media: Union[str, InputFile] = media.file_id @@ -493,13 +493,13 @@ class InputMediaDocument(InputMedia): """ __slots__ = ( - 'caption_entities', - 'media', - 'thumb', - 'caption', - 'parse_mode', - 'type', - 'disable_content_type_detection', + "caption_entities", + "media", + "thumb", + "caption", + "parse_mode", + "type", + "disable_content_type_detection", ) def __init__( @@ -512,7 +512,7 @@ def __init__( caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...]] = None, filename: str = None, ): - self.type = 'document' + self.type = "document" self.media = parse_file_input(media, Document, attach=True, filename=filename) if thumb: diff --git a/telegram/files/location.py b/telegram/files/location.py index 8f5c1c63daa..2af07964805 100644 --- a/telegram/files/location.py +++ b/telegram/files/location.py @@ -57,13 +57,12 @@ class Location(TelegramObject): """ __slots__ = ( - 'longitude', - 'horizontal_accuracy', - 'proximity_alert_radius', - 'live_period', - 'latitude', - 'heading', - '_id_attrs', + "longitude", + "horizontal_accuracy", + "proximity_alert_radius", + "live_period", + "latitude", + "heading", ) def __init__( diff --git a/telegram/files/photosize.py b/telegram/files/photosize.py index 831a7c01194..d72dd6be1bd 100644 --- a/telegram/files/photosize.py +++ b/telegram/files/photosize.py @@ -58,7 +58,7 @@ class PhotoSize(TelegramObject): """ - __slots__ = ('bot', 'width', 'file_id', 'file_size', 'height', 'file_unique_id', '_id_attrs') + __slots__ = ("bot", "width", "file_id", "file_size", "height", "file_unique_id") def __init__( self, @@ -67,7 +67,7 @@ def __init__( width: int, height: int, file_size: int = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -83,7 +83,7 @@ def __init__( def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/files/sticker.py b/telegram/files/sticker.py index 681c7087b24..129cf6fb5db 100644 --- a/telegram/files/sticker.py +++ b/telegram/files/sticker.py @@ -74,18 +74,17 @@ class Sticker(TelegramObject): """ __slots__ = ( - 'bot', - 'width', - 'file_id', - 'is_animated', - 'file_size', - 'thumb', - 'set_name', - 'mask_position', - 'height', - 'file_unique_id', - 'emoji', - '_id_attrs', + "bot", + "width", + "file_id", + "is_animated", + "file_size", + "thumb", + "set_name", + "mask_position", + "height", + "file_unique_id", + "emoji", ) def __init__( @@ -99,8 +98,8 @@ def __init__( emoji: str = None, file_size: int = None, set_name: str = None, - mask_position: 'MaskPosition' = None, - bot: 'Bot' = None, + mask_position: "MaskPosition" = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -120,21 +119,21 @@ def __init__( self._id_attrs = (self.file_unique_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Sticker']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Sticker"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - data['mask_position'] = MaskPosition.de_json(data.get('mask_position'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) + data["mask_position"] = MaskPosition.de_json(data.get("mask_position"), bot) return cls(bot=bot, **data) def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. @@ -176,13 +175,12 @@ class StickerSet(TelegramObject): """ __slots__ = ( - 'is_animated', - 'contains_masks', - 'thumb', - 'title', - 'stickers', - 'name', - '_id_attrs', + "is_animated", + "contains_masks", + "thumb", + "title", + "stickers", + "name", ) def __init__( @@ -206,13 +204,13 @@ def __init__( self._id_attrs = (self.name,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['StickerSet']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["StickerSet"]: """See :meth:`telegram.TelegramObject.de_json`.""" if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) - data['stickers'] = Sticker.de_list(data.get('stickers'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) + data["stickers"] = Sticker.de_list(data.get("stickers"), bot) return cls(bot=bot, **data) @@ -220,7 +218,7 @@ 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')] + data["stickers"] = [s.to_dict() for s in data.get("stickers")] return data @@ -258,7 +256,7 @@ class MaskPosition(TelegramObject): """ - __slots__ = ('point', 'scale', 'x_shift', 'y_shift', '_id_attrs') + __slots__ = ("point", "scale", "x_shift", "y_shift") FOREHEAD: ClassVar[str] = constants.STICKER_FOREHEAD """:const:`telegram.constants.STICKER_FOREHEAD`""" @@ -278,7 +276,7 @@ def __init__(self, point: str, x_shift: float, y_shift: float, scale: float, **_ self._id_attrs = (self.point, self.x_shift, self.y_shift, self.scale) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MaskPosition']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MaskPosition"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) diff --git a/telegram/files/venue.py b/telegram/files/venue.py index 3ba2c53a376..a799032e096 100644 --- a/telegram/files/venue.py +++ b/telegram/files/venue.py @@ -61,14 +61,13 @@ class Venue(TelegramObject): """ __slots__ = ( - 'google_place_type', - 'location', - 'title', - 'address', - 'foursquare_type', - 'foursquare_id', - 'google_place_id', - '_id_attrs', + "google_place_type", + "location", + "title", + "address", + "foursquare_type", + "foursquare_id", + "google_place_id", ) def __init__( @@ -95,13 +94,13 @@ def __init__( self._id_attrs = (self.location, self.title) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Venue']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Venue"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['location'] = Location.de_json(data.get('location'), bot) + data["location"] = Location.de_json(data.get("location"), bot) return cls(**data) diff --git a/telegram/files/video.py b/telegram/files/video.py index 76bb07cda7a..1ab0beeb5c1 100644 --- a/telegram/files/video.py +++ b/telegram/files/video.py @@ -67,17 +67,16 @@ class Video(TelegramObject): """ __slots__ = ( - 'bot', - 'width', - 'file_id', - 'file_size', - 'file_name', - 'thumb', - 'duration', - 'mime_type', - 'height', - 'file_unique_id', - '_id_attrs', + "bot", + "width", + "file_id", + "file_size", + "file_name", + "thumb", + "duration", + "mime_type", + "height", + "file_unique_id", ) def __init__( @@ -90,7 +89,7 @@ def __init__( thumb: PhotoSize = None, mime_type: str = None, file_size: int = None, - bot: 'Bot' = None, + bot: "Bot" = None, file_name: str = None, **_kwargs: Any, ): @@ -110,20 +109,20 @@ def __init__( self._id_attrs = (self.file_unique_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Video']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Video"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) return cls(bot=bot, **data) def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/files/videonote.py b/telegram/files/videonote.py index 8c704069ed7..3c26be34c58 100644 --- a/telegram/files/videonote.py +++ b/telegram/files/videonote.py @@ -62,14 +62,13 @@ class VideoNote(TelegramObject): """ __slots__ = ( - 'bot', - 'length', - 'file_id', - 'file_size', - 'thumb', - 'duration', - 'file_unique_id', - '_id_attrs', + "bot", + "length", + "file_id", + "file_size", + "thumb", + "duration", + "file_unique_id", ) def __init__( @@ -80,7 +79,7 @@ def __init__( duration: int, thumb: PhotoSize = None, file_size: int = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -96,20 +95,20 @@ def __init__( self._id_attrs = (self.file_unique_id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VideoNote']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["VideoNote"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['thumb'] = PhotoSize.de_json(data.get('thumb'), bot) + data["thumb"] = PhotoSize.de_json(data.get("thumb"), bot) return cls(bot=bot, **data) def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/files/voice.py b/telegram/files/voice.py index f65c5c590ca..0f064a511be 100644 --- a/telegram/files/voice.py +++ b/telegram/files/voice.py @@ -59,13 +59,12 @@ class Voice(TelegramObject): """ __slots__ = ( - 'bot', - 'file_id', - 'file_size', - 'duration', - 'mime_type', - 'file_unique_id', - '_id_attrs', + "bot", + "file_id", + "file_size", + "duration", + "mime_type", + "file_unique_id", ) def __init__( @@ -75,7 +74,7 @@ def __init__( duration: int, mime_type: str = None, file_size: int = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -91,7 +90,7 @@ def __init__( def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """Convenience wrapper over :attr:`telegram.Bot.get_file` For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`. diff --git a/telegram/forcereply.py b/telegram/forcereply.py index baa9782810e..196367dd151 100644 --- a/telegram/forcereply.py +++ b/telegram/forcereply.py @@ -33,6 +33,10 @@ class ForceReply(ReplyMarkup): Objects of this class are comparable in terms of equality. Two objects of this class are considered equal, if their :attr:`selective` is equal. + .. versionchanged:: 14.0 + The (undocumented) argument ``force_reply`` was removed and instead :attr:`force_reply` + is now always set to :obj:`True` as expected by the Bot API. + Args: selective (:obj:`bool`, optional): Use this parameter if you want to force reply from specific users only. Targets: @@ -60,18 +64,15 @@ class ForceReply(ReplyMarkup): """ - __slots__ = ('selective', 'force_reply', 'input_field_placeholder', '_id_attrs') + __slots__ = ("selective", "force_reply", "input_field_placeholder") def __init__( self, - force_reply: bool = True, selective: bool = False, input_field_placeholder: str = None, **_kwargs: Any, ): - # Required - self.force_reply = bool(force_reply) - # Optionals + self.force_reply = True self.selective = bool(selective) self.input_field_placeholder = input_field_placeholder diff --git a/telegram/games/game.py b/telegram/games/game.py index d56bebe0275..df344ef9cd1 100644 --- a/telegram/games/game.py +++ b/telegram/games/game.py @@ -68,13 +68,12 @@ class Game(TelegramObject): """ __slots__ = ( - 'title', - 'photo', - 'description', - 'text_entities', - 'text', - 'animation', - '_id_attrs', + "title", + "photo", + "description", + "text_entities", + "text", + "animation", ) def __init__( @@ -99,16 +98,16 @@ def __init__( self._id_attrs = (self.title, self.description, self.photo) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Game']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Game"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['photo'] = PhotoSize.de_list(data.get('photo'), bot) - data['text_entities'] = MessageEntity.de_list(data.get('text_entities'), bot) - data['animation'] = Animation.de_json(data.get('animation'), bot) + data["photo"] = PhotoSize.de_list(data.get("photo"), bot) + data["text_entities"] = MessageEntity.de_list(data.get("text_entities"), bot) + data["animation"] = Animation.de_json(data.get("animation"), bot) return cls(**data) @@ -116,9 +115,9 @@ 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] + data["photo"] = [p.to_dict() for p in self.photo] if self.text_entities: - data['text_entities'] = [x.to_dict() for x in self.text_entities] + data["text_entities"] = [x.to_dict() for x in self.text_entities] return data @@ -147,10 +146,10 @@ def parse_text_entity(self, entity: MessageEntity) -> str: # Is it a narrow build, if so we don't need to convert if sys.maxunicode == 0xFFFF: return self.text[entity.offset : entity.offset + entity.length] - entity_text = self.text.encode('utf-16-le') + entity_text = self.text.encode("utf-16-le") entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - return entity_text.decode('utf-16-le') + return entity_text.decode("utf-16-le") def parse_text_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: """ diff --git a/telegram/games/gamehighscore.py b/telegram/games/gamehighscore.py index bfa7cbfbf15..1e7ea571483 100644 --- a/telegram/games/gamehighscore.py +++ b/telegram/games/gamehighscore.py @@ -45,7 +45,7 @@ class GameHighScore(TelegramObject): """ - __slots__ = ('position', 'user', 'score', '_id_attrs') + __slots__ = ("position", "user", "score") def __init__(self, position: int, user: User, score: int): self.position = position @@ -55,13 +55,13 @@ def __init__(self, position: int, user: User, score: int): self._id_attrs = (self.position, self.user, self.score) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['GameHighScore']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["GameHighScore"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['user'] = User.de_json(data.get('user'), bot) + data["user"] = User.de_json(data.get("user"), bot) return cls(**data) diff --git a/telegram/inline/inlinekeyboardbutton.py b/telegram/inline/inlinekeyboardbutton.py index b9d0c32165a..21a69d7ef4c 100644 --- a/telegram/inline/inlinekeyboardbutton.py +++ b/telegram/inline/inlinekeyboardbutton.py @@ -99,15 +99,14 @@ class InlineKeyboardButton(TelegramObject): """ __slots__ = ( - 'callback_game', - 'url', - 'switch_inline_query_current_chat', - 'callback_data', - 'pay', - 'switch_inline_query', - 'text', - '_id_attrs', - 'login_url', + "callback_game", + "url", + "switch_inline_query_current_chat", + "callback_data", + "pay", + "switch_inline_query", + "text", + "login_url", ) def __init__( @@ -117,9 +116,9 @@ def __init__( callback_data: object = None, switch_inline_query: str = None, switch_inline_query_current_chat: str = None, - callback_game: 'CallbackGame' = None, + callback_game: "CallbackGame" = None, pay: bool = None, - login_url: 'LoginUrl' = None, + login_url: "LoginUrl" = None, **_kwargs: Any, ): # Required diff --git a/telegram/inline/inlinekeyboardmarkup.py b/telegram/inline/inlinekeyboardmarkup.py index a917d96f3e9..ce3ca203fec 100644 --- a/telegram/inline/inlinekeyboardmarkup.py +++ b/telegram/inline/inlinekeyboardmarkup.py @@ -45,7 +45,7 @@ class InlineKeyboardMarkup(ReplyMarkup): """ - __slots__ = ('inline_keyboard', '_id_attrs') + __slots__ = ("inline_keyboard",) def __init__(self, inline_keyboard: List[List[InlineKeyboardButton]], **_kwargs: Any): # Required @@ -57,14 +57,14 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - data['inline_keyboard'] = [] + data["inline_keyboard"] = [] for inline_keyboard in self.inline_keyboard: - data['inline_keyboard'].append([x.to_dict() for x in inline_keyboard]) + data["inline_keyboard"].append([x.to_dict() for x in inline_keyboard]) return data @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboardMarkup']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineKeyboardMarkup"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) @@ -72,7 +72,7 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboa return None keyboard = [] - for row in data['inline_keyboard']: + for row in data["inline_keyboard"]: tmp = [] for col in row: btn = InlineKeyboardButton.de_json(col, bot) @@ -83,7 +83,7 @@ def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboa return cls(keyboard) @classmethod - def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> 'InlineKeyboardMarkup': + def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> "InlineKeyboardMarkup": """Shortcut for:: InlineKeyboardMarkup([[button]], **kwargs) @@ -100,7 +100,7 @@ def from_button(cls, button: InlineKeyboardButton, **kwargs: object) -> 'InlineK @classmethod def from_row( cls, button_row: List[InlineKeyboardButton], **kwargs: object - ) -> 'InlineKeyboardMarkup': + ) -> "InlineKeyboardMarkup": """Shortcut for:: InlineKeyboardMarkup([button_row], **kwargs) @@ -118,7 +118,7 @@ def from_row( @classmethod def from_column( cls, button_column: List[InlineKeyboardButton], **kwargs: object - ) -> 'InlineKeyboardMarkup': + ) -> "InlineKeyboardMarkup": """Shortcut for:: InlineKeyboardMarkup([[button] for button in button_column], **kwargs) diff --git a/telegram/inline/inlinequery.py b/telegram/inline/inlinequery.py index 412188db49b..fcab379de76 100644 --- a/telegram/inline/inlinequery.py +++ b/telegram/inline/inlinequery.py @@ -71,7 +71,7 @@ class InlineQuery(TelegramObject): """ - __slots__ = ('bot', 'location', 'chat_type', 'id', 'offset', 'from_user', 'query', '_id_attrs') + __slots__ = ("bot", "location", "chat_type", "id", "offset", "from_user", "query") def __init__( self, @@ -80,7 +80,7 @@ def __init__( query: str, offset: str, location: Location = None, - bot: 'Bot' = None, + bot: "Bot" = None, chat_type: str = None, **_kwargs: Any, ): @@ -98,22 +98,22 @@ def __init__( self._id_attrs = (self.id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineQuery']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["InlineQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['from_user'] = User.de_json(data.get('from'), bot) - data['location'] = Location.de_json(data.get('location'), bot) + data["from_user"] = User.de_json(data.get("from"), bot) + data["location"] = Location.de_json(data.get("location"), bot) return cls(bot=bot, **data) def answer( self, results: Union[ - Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] + Sequence["InlineQueryResult"], Callable[[int], Optional[Sequence["InlineQueryResult"]]] ], cache_time: int = 300, is_personal: bool = None, @@ -146,7 +146,7 @@ def answer( if current_offset and auto_pagination: # We raise TypeError instead of ValueError for backwards compatibility with versions # which didn't check this here but let Python do the checking - raise TypeError('current_offset and auto_pagination are mutually exclusive!') + raise TypeError("current_offset and auto_pagination are mutually exclusive!") return self.bot.answer_inline_query( inline_query_id=self.id, current_offset=self.offset if auto_pagination else current_offset, diff --git a/telegram/inline/inlinequeryresult.py b/telegram/inline/inlinequeryresult.py index 756e2fb9ce8..a6569abb96b 100644 --- a/telegram/inline/inlinequeryresult.py +++ b/telegram/inline/inlinequeryresult.py @@ -46,7 +46,7 @@ class InlineQueryResult(TelegramObject): """ - __slots__ = ('type', 'id', '_id_attrs') + __slots__ = ("type", "id") def __init__(self, type: str, id: str, **_kwargs: Any): # Required @@ -61,10 +61,10 @@ def to_dict(self) -> JSONDict: # pylint: disable=E1101 if ( - hasattr(self, 'caption_entities') + hasattr(self, "caption_entities") and self.caption_entities # type: ignore[attr-defined] ): - data['caption_entities'] = [ + data["caption_entities"] = [ ce.to_dict() for ce in self.caption_entities # type: ignore[attr-defined] ] diff --git a/telegram/inline/inlinequeryresultarticle.py b/telegram/inline/inlinequeryresultarticle.py index 3827ae305e0..5e272afe915 100644 --- a/telegram/inline/inlinequeryresultarticle.py +++ b/telegram/inline/inlinequeryresultarticle.py @@ -64,23 +64,23 @@ class InlineQueryResultArticle(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'thumb_width', - 'thumb_height', - 'hide_url', - 'url', - 'title', - 'description', - 'input_message_content', - 'thumb_url', + "reply_markup", + "thumb_width", + "thumb_height", + "hide_url", + "url", + "title", + "description", + "input_message_content", + "thumb_url", ) def __init__( self, id: str, # pylint: disable=W0622 title: str, - input_message_content: 'InputMessageContent', - reply_markup: 'ReplyMarkup' = None, + input_message_content: "InputMessageContent", + reply_markup: "ReplyMarkup" = None, url: str = None, hide_url: bool = None, description: str = None, @@ -91,7 +91,7 @@ def __init__( ): # Required - super().__init__('article', id) + super().__init__("article", id) self.title = title self.input_message_content = input_message_content diff --git a/telegram/inline/inlinequeryresultaudio.py b/telegram/inline/inlinequeryresultaudio.py index 93eaa164948..90f6f9983c5 100644 --- a/telegram/inline/inlinequeryresultaudio.py +++ b/telegram/inline/inlinequeryresultaudio.py @@ -75,15 +75,15 @@ class InlineQueryResultAudio(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'parse_mode', - 'audio_url', - 'performer', - 'input_message_content', - 'audio_duration', + "reply_markup", + "caption_entities", + "caption", + "title", + "parse_mode", + "audio_url", + "performer", + "input_message_content", + "audio_duration", ) def __init__( @@ -94,15 +94,15 @@ def __init__( performer: str = None, audio_duration: int = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('audio', id) + super().__init__("audio", id) self.audio_url = audio_url self.title = title diff --git a/telegram/inline/inlinequeryresultcachedaudio.py b/telegram/inline/inlinequeryresultcachedaudio.py index 41222bbb680..61e38f246f7 100644 --- a/telegram/inline/inlinequeryresultcachedaudio.py +++ b/telegram/inline/inlinequeryresultcachedaudio.py @@ -69,12 +69,12 @@ class InlineQueryResultCachedAudio(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'parse_mode', - 'audio_file_id', - 'input_message_content', + "reply_markup", + "caption_entities", + "caption", + "parse_mode", + "audio_file_id", + "input_message_content", ) def __init__( @@ -82,14 +82,14 @@ def __init__( id: str, # pylint: disable=W0622 audio_file_id: str, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('audio', id) + super().__init__("audio", id) self.audio_file_id = audio_file_id # Optionals diff --git a/telegram/inline/inlinequeryresultcacheddocument.py b/telegram/inline/inlinequeryresultcacheddocument.py index 784ccaffb9c..5275a6290ec 100644 --- a/telegram/inline/inlinequeryresultcacheddocument.py +++ b/telegram/inline/inlinequeryresultcacheddocument.py @@ -76,14 +76,14 @@ class InlineQueryResultCachedDocument(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'document_file_id', - 'caption', - 'title', - 'description', - 'parse_mode', - 'input_message_content', + "reply_markup", + "caption_entities", + "document_file_id", + "caption", + "title", + "description", + "parse_mode", + "input_message_content", ) def __init__( @@ -93,14 +93,14 @@ def __init__( document_file_id: str, description: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('document', id) + super().__init__("document", id) self.title = title self.document_file_id = document_file_id diff --git a/telegram/inline/inlinequeryresultcachedgif.py b/telegram/inline/inlinequeryresultcachedgif.py index ca2fc42106c..a368d20cf7d 100644 --- a/telegram/inline/inlinequeryresultcachedgif.py +++ b/telegram/inline/inlinequeryresultcachedgif.py @@ -74,13 +74,13 @@ class InlineQueryResultCachedGif(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'input_message_content', - 'parse_mode', - 'gif_file_id', + "reply_markup", + "caption_entities", + "caption", + "title", + "input_message_content", + "parse_mode", + "gif_file_id", ) def __init__( @@ -89,14 +89,14 @@ def __init__( gif_file_id: str, title: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('gif', id) + super().__init__("gif", id) self.gif_file_id = gif_file_id # Optionals diff --git a/telegram/inline/inlinequeryresultcachedmpeg4gif.py b/telegram/inline/inlinequeryresultcachedmpeg4gif.py index 4f0f85cf59c..e2ff6a65278 100644 --- a/telegram/inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/inline/inlinequeryresultcachedmpeg4gif.py @@ -74,13 +74,13 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'mpeg4_file_id', - 'caption', - 'title', - 'parse_mode', - 'input_message_content', + "reply_markup", + "caption_entities", + "mpeg4_file_id", + "caption", + "title", + "parse_mode", + "input_message_content", ) def __init__( @@ -89,14 +89,14 @@ def __init__( mpeg4_file_id: str, title: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('mpeg4_gif', id) + super().__init__("mpeg4_gif", id) self.mpeg4_file_id = mpeg4_file_id # Optionals diff --git a/telegram/inline/inlinequeryresultcachedphoto.py b/telegram/inline/inlinequeryresultcachedphoto.py index 4a929dd2bb3..9825e105ddf 100644 --- a/telegram/inline/inlinequeryresultcachedphoto.py +++ b/telegram/inline/inlinequeryresultcachedphoto.py @@ -77,14 +77,14 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'description', - 'parse_mode', - 'photo_file_id', - 'input_message_content', + "reply_markup", + "caption_entities", + "caption", + "title", + "description", + "parse_mode", + "photo_file_id", + "input_message_content", ) def __init__( @@ -94,14 +94,14 @@ def __init__( title: str = None, description: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('photo', id) + super().__init__("photo", id) self.photo_file_id = photo_file_id # Optionals diff --git a/telegram/inline/inlinequeryresultcachedsticker.py b/telegram/inline/inlinequeryresultcachedsticker.py index f369bdd4aa5..c528c9619fa 100644 --- a/telegram/inline/inlinequeryresultcachedsticker.py +++ b/telegram/inline/inlinequeryresultcachedsticker.py @@ -52,18 +52,18 @@ class InlineQueryResultCachedSticker(InlineQueryResult): """ - __slots__ = ('reply_markup', 'input_message_content', 'sticker_file_id') + __slots__ = ("reply_markup", "input_message_content", "sticker_file_id") def __init__( self, id: str, # pylint: disable=W0622 sticker_file_id: str, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, **_kwargs: Any, ): # Required - super().__init__('sticker', id) + super().__init__("sticker", id) self.sticker_file_id = sticker_file_id # Optionals diff --git a/telegram/inline/inlinequeryresultcachedvideo.py b/telegram/inline/inlinequeryresultcachedvideo.py index ee91515f1eb..041becfb5db 100644 --- a/telegram/inline/inlinequeryresultcachedvideo.py +++ b/telegram/inline/inlinequeryresultcachedvideo.py @@ -76,14 +76,14 @@ class InlineQueryResultCachedVideo(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'description', - 'parse_mode', - 'input_message_content', - 'video_file_id', + "reply_markup", + "caption_entities", + "caption", + "title", + "description", + "parse_mode", + "input_message_content", + "video_file_id", ) def __init__( @@ -93,14 +93,14 @@ def __init__( title: str, description: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('video', id) + super().__init__("video", id) self.video_file_id = video_file_id self.title = title diff --git a/telegram/inline/inlinequeryresultcachedvoice.py b/telegram/inline/inlinequeryresultcachedvoice.py index ff2ef227087..e21d1df37bb 100644 --- a/telegram/inline/inlinequeryresultcachedvoice.py +++ b/telegram/inline/inlinequeryresultcachedvoice.py @@ -71,13 +71,13 @@ class InlineQueryResultCachedVoice(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'parse_mode', - 'voice_file_id', - 'input_message_content', + "reply_markup", + "caption_entities", + "caption", + "title", + "parse_mode", + "voice_file_id", + "input_message_content", ) def __init__( @@ -86,14 +86,14 @@ def __init__( voice_file_id: str, title: str, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('voice', id) + super().__init__("voice", id) self.voice_file_id = voice_file_id self.title = title diff --git a/telegram/inline/inlinequeryresultcontact.py b/telegram/inline/inlinequeryresultcontact.py index 42dd75d4bb9..c94aa1e425b 100644 --- a/telegram/inline/inlinequeryresultcontact.py +++ b/telegram/inline/inlinequeryresultcontact.py @@ -67,15 +67,15 @@ class InlineQueryResultContact(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'thumb_width', - 'thumb_height', - 'vcard', - 'first_name', - 'last_name', - 'phone_number', - 'input_message_content', - 'thumb_url', + "reply_markup", + "thumb_width", + "thumb_height", + "vcard", + "first_name", + "last_name", + "phone_number", + "input_message_content", + "thumb_url", ) def __init__( @@ -84,8 +84,8 @@ def __init__( phone_number: str, first_name: str, last_name: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, @@ -93,7 +93,7 @@ def __init__( **_kwargs: Any, ): # Required - super().__init__('contact', id) + super().__init__("contact", id) self.phone_number = phone_number self.first_name = first_name diff --git a/telegram/inline/inlinequeryresultdocument.py b/telegram/inline/inlinequeryresultdocument.py index 4e3c0b0b228..60220437832 100644 --- a/telegram/inline/inlinequeryresultdocument.py +++ b/telegram/inline/inlinequeryresultdocument.py @@ -86,18 +86,18 @@ class InlineQueryResultDocument(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'document_url', - 'thumb_width', - 'thumb_height', - 'caption', - 'title', - 'description', - 'parse_mode', - 'mime_type', - 'thumb_url', - 'input_message_content', + "reply_markup", + "caption_entities", + "document_url", + "thumb_width", + "thumb_height", + "caption", + "title", + "description", + "parse_mode", + "mime_type", + "thumb_url", + "input_message_content", ) def __init__( @@ -108,8 +108,8 @@ def __init__( mime_type: str, caption: str = None, description: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, @@ -118,7 +118,7 @@ def __init__( **_kwargs: Any, ): # Required - super().__init__('document', id) + super().__init__("document", id) self.document_url = document_url self.title = title self.mime_type = mime_type diff --git a/telegram/inline/inlinequeryresultgame.py b/telegram/inline/inlinequeryresultgame.py index f8535b44b1c..ff2046e2ee6 100644 --- a/telegram/inline/inlinequeryresultgame.py +++ b/telegram/inline/inlinequeryresultgame.py @@ -45,17 +45,17 @@ class InlineQueryResultGame(InlineQueryResult): """ - __slots__ = ('reply_markup', 'game_short_name') + __slots__ = ("reply_markup", "game_short_name") def __init__( self, id: str, # pylint: disable=W0622 game_short_name: str, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, **_kwargs: Any, ): # Required - super().__init__('game', id) + super().__init__("game", id) self.id = id # pylint: disable=W0622 self.game_short_name = game_short_name diff --git a/telegram/inline/inlinequeryresultgif.py b/telegram/inline/inlinequeryresultgif.py index 619af4508d5..7e24ac54764 100644 --- a/telegram/inline/inlinequeryresultgif.py +++ b/telegram/inline/inlinequeryresultgif.py @@ -87,18 +87,18 @@ class InlineQueryResultGif(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'gif_height', - 'thumb_mime_type', - 'caption_entities', - 'gif_width', - 'title', - 'caption', - 'parse_mode', - 'gif_duration', - 'input_message_content', - 'gif_url', - 'thumb_url', + "reply_markup", + "gif_height", + "thumb_mime_type", + "caption_entities", + "gif_width", + "title", + "caption", + "parse_mode", + "gif_duration", + "input_message_content", + "gif_url", + "thumb_url", ) def __init__( @@ -110,8 +110,8 @@ def __init__( gif_height: int = None, title: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, gif_duration: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, @@ -120,7 +120,7 @@ def __init__( ): # Required - super().__init__('gif', id) + super().__init__("gif", id) self.gif_url = gif_url self.thumb_url = thumb_url diff --git a/telegram/inline/inlinequeryresultlocation.py b/telegram/inline/inlinequeryresultlocation.py index 2591b6361b1..e2aefe21087 100644 --- a/telegram/inline/inlinequeryresultlocation.py +++ b/telegram/inline/inlinequeryresultlocation.py @@ -80,18 +80,18 @@ class InlineQueryResultLocation(InlineQueryResult): """ __slots__ = ( - 'longitude', - 'reply_markup', - 'thumb_width', - 'thumb_height', - 'heading', - 'title', - 'live_period', - 'proximity_alert_radius', - 'input_message_content', - 'latitude', - 'horizontal_accuracy', - 'thumb_url', + "longitude", + "reply_markup", + "thumb_width", + "thumb_height", + "heading", + "title", + "live_period", + "proximity_alert_radius", + "input_message_content", + "latitude", + "horizontal_accuracy", + "thumb_url", ) def __init__( @@ -101,8 +101,8 @@ def __init__( longitude: float, title: str, live_period: int = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, @@ -112,7 +112,7 @@ def __init__( **_kwargs: Any, ): # Required - super().__init__('location', id) + super().__init__("location", id) self.latitude = float(latitude) self.longitude = float(longitude) self.title = title diff --git a/telegram/inline/inlinequeryresultmpeg4gif.py b/telegram/inline/inlinequeryresultmpeg4gif.py index 3eb1c21f344..3e5f36290ac 100644 --- a/telegram/inline/inlinequeryresultmpeg4gif.py +++ b/telegram/inline/inlinequeryresultmpeg4gif.py @@ -86,18 +86,18 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'thumb_mime_type', - 'caption_entities', - 'mpeg4_duration', - 'mpeg4_width', - 'title', - 'caption', - 'parse_mode', - 'input_message_content', - 'mpeg4_url', - 'mpeg4_height', - 'thumb_url', + "reply_markup", + "thumb_mime_type", + "caption_entities", + "mpeg4_duration", + "mpeg4_width", + "title", + "caption", + "parse_mode", + "input_message_content", + "mpeg4_url", + "mpeg4_height", + "thumb_url", ) def __init__( @@ -109,8 +109,8 @@ def __init__( mpeg4_height: int = None, title: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, mpeg4_duration: int = None, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb_mime_type: str = None, @@ -119,7 +119,7 @@ def __init__( ): # Required - super().__init__('mpeg4_gif', id) + super().__init__("mpeg4_gif", id) self.mpeg4_url = mpeg4_url self.thumb_url = thumb_url diff --git a/telegram/inline/inlinequeryresultphoto.py b/telegram/inline/inlinequeryresultphoto.py index 98f71856296..bf44ae08072 100644 --- a/telegram/inline/inlinequeryresultphoto.py +++ b/telegram/inline/inlinequeryresultphoto.py @@ -83,17 +83,17 @@ class InlineQueryResultPhoto(InlineQueryResult): """ __slots__ = ( - 'photo_url', - 'reply_markup', - 'caption_entities', - 'photo_width', - 'caption', - 'title', - 'description', - 'parse_mode', - 'input_message_content', - 'photo_height', - 'thumb_url', + "photo_url", + "reply_markup", + "caption_entities", + "photo_width", + "caption", + "title", + "description", + "parse_mode", + "input_message_content", + "photo_height", + "thumb_url", ) def __init__( @@ -106,14 +106,14 @@ def __init__( title: str = None, description: str = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('photo', id) + super().__init__("photo", id) self.photo_url = photo_url self.thumb_url = thumb_url diff --git a/telegram/inline/inlinequeryresultvenue.py b/telegram/inline/inlinequeryresultvenue.py index 9930f7ab72e..7361967dd47 100644 --- a/telegram/inline/inlinequeryresultvenue.py +++ b/telegram/inline/inlinequeryresultvenue.py @@ -80,19 +80,19 @@ class InlineQueryResultVenue(InlineQueryResult): """ __slots__ = ( - 'longitude', - 'reply_markup', - 'google_place_type', - 'thumb_width', - 'thumb_height', - 'title', - 'address', - 'foursquare_id', - 'foursquare_type', - 'google_place_id', - 'input_message_content', - 'latitude', - 'thumb_url', + "longitude", + "reply_markup", + "google_place_type", + "thumb_width", + "thumb_height", + "title", + "address", + "foursquare_id", + "foursquare_type", + "google_place_id", + "input_message_content", + "latitude", + "thumb_url", ) def __init__( @@ -104,8 +104,8 @@ def __init__( address: str, foursquare_id: str = None, foursquare_type: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, @@ -115,7 +115,7 @@ def __init__( ): # Required - super().__init__('venue', id) + super().__init__("venue", id) self.latitude = latitude self.longitude = longitude self.title = title diff --git a/telegram/inline/inlinequeryresultvideo.py b/telegram/inline/inlinequeryresultvideo.py index b84a3f2b963..de2a98f12fb 100644 --- a/telegram/inline/inlinequeryresultvideo.py +++ b/telegram/inline/inlinequeryresultvideo.py @@ -93,19 +93,19 @@ class InlineQueryResultVideo(InlineQueryResult): """ __slots__ = ( - 'video_url', - 'reply_markup', - 'caption_entities', - 'caption', - 'title', - 'description', - 'video_duration', - 'parse_mode', - 'mime_type', - 'input_message_content', - 'video_height', - 'video_width', - 'thumb_url', + "video_url", + "reply_markup", + "caption_entities", + "caption", + "title", + "description", + "video_duration", + "parse_mode", + "mime_type", + "input_message_content", + "video_height", + "video_width", + "thumb_url", ) def __init__( @@ -120,15 +120,15 @@ def __init__( video_height: int = None, video_duration: int = None, description: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('video', id) + super().__init__("video", id) self.video_url = video_url self.mime_type = mime_type self.thumb_url = thumb_url diff --git a/telegram/inline/inlinequeryresultvoice.py b/telegram/inline/inlinequeryresultvoice.py index 531f04b2354..bd861c56968 100644 --- a/telegram/inline/inlinequeryresultvoice.py +++ b/telegram/inline/inlinequeryresultvoice.py @@ -74,14 +74,14 @@ class InlineQueryResultVoice(InlineQueryResult): """ __slots__ = ( - 'reply_markup', - 'caption_entities', - 'voice_duration', - 'caption', - 'title', - 'voice_url', - 'parse_mode', - 'input_message_content', + "reply_markup", + "caption_entities", + "voice_duration", + "caption", + "title", + "voice_url", + "parse_mode", + "input_message_content", ) def __init__( @@ -91,15 +91,15 @@ def __init__( title: str, voice_duration: int = None, caption: str = None, - reply_markup: 'ReplyMarkup' = None, - input_message_content: 'InputMessageContent' = None, + reply_markup: "ReplyMarkup" = None, + input_message_content: "InputMessageContent" = None, parse_mode: ODVInput[str] = DEFAULT_NONE, caption_entities: Union[Tuple[MessageEntity, ...], List[MessageEntity]] = None, **_kwargs: Any, ): # Required - super().__init__('voice', id) + super().__init__("voice", id) self.voice_url = voice_url self.title = title diff --git a/telegram/inline/inputcontactmessagecontent.py b/telegram/inline/inputcontactmessagecontent.py index 22e9460c76a..d5e4b928967 100644 --- a/telegram/inline/inputcontactmessagecontent.py +++ b/telegram/inline/inputcontactmessagecontent.py @@ -46,7 +46,7 @@ class InputContactMessageContent(InputMessageContent): """ - __slots__ = ('vcard', 'first_name', 'last_name', 'phone_number', '_id_attrs') + __slots__ = ("vcard", "first_name", "last_name", "phone_number") def __init__( self, diff --git a/telegram/inline/inputinvoicemessagecontent.py b/telegram/inline/inputinvoicemessagecontent.py index 2cbbcb8f437..49fe00dffb7 100644 --- a/telegram/inline/inputinvoicemessagecontent.py +++ b/telegram/inline/inputinvoicemessagecontent.py @@ -124,27 +124,26 @@ class InputInvoiceMessageContent(InputMessageContent): """ __slots__ = ( - 'title', - 'description', - 'payload', - 'provider_token', - 'currency', - 'prices', - 'max_tip_amount', - 'suggested_tip_amounts', - 'provider_data', - 'photo_url', - 'photo_size', - 'photo_width', - 'photo_height', - 'need_name', - 'need_phone_number', - 'need_email', - 'need_shipping_address', - 'send_phone_number_to_provider', - 'send_email_to_provider', - 'is_flexible', - '_id_attrs', + "title", + "description", + "payload", + "provider_token", + "currency", + "prices", + "max_tip_amount", + "suggested_tip_amounts", + "provider_data", + "photo_url", + "photo_size", + "photo_width", + "photo_height", + "need_name", + "need_phone_number", + "need_email", + "need_shipping_address", + "send_phone_number_to_provider", + "send_email_to_provider", + "is_flexible", ) def __init__( @@ -223,20 +222,20 @@ 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] + data["prices"] = [price.to_dict() for price in self.prices] return data @classmethod def de_json( - cls, data: Optional[JSONDict], bot: 'Bot' - ) -> Optional['InputInvoiceMessageContent']: + cls, data: Optional[JSONDict], bot: "Bot" + ) -> Optional["InputInvoiceMessageContent"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['prices'] = LabeledPrice.de_list(data.get('prices'), bot) + data["prices"] = LabeledPrice.de_list(data.get("prices"), bot) return cls(**data, bot=bot) diff --git a/telegram/inline/inputlocationmessagecontent.py b/telegram/inline/inputlocationmessagecontent.py index fe8662882be..9d06713ad85 100644 --- a/telegram/inline/inputlocationmessagecontent.py +++ b/telegram/inline/inputlocationmessagecontent.py @@ -60,7 +60,7 @@ class InputLocationMessageContent(InputMessageContent): """ __slots__ = ('longitude', 'horizontal_accuracy', 'proximity_alert_radius', 'live_period', - 'latitude', 'heading', '_id_attrs') + 'latitude', 'heading') # fmt: on def __init__( diff --git a/telegram/inline/inputtextmessagecontent.py b/telegram/inline/inputtextmessagecontent.py index 3d60f456c0d..c4e94920c21 100644 --- a/telegram/inline/inputtextmessagecontent.py +++ b/telegram/inline/inputtextmessagecontent.py @@ -59,7 +59,7 @@ class InputTextMessageContent(InputMessageContent): """ - __slots__ = ('disable_web_page_preview', 'parse_mode', 'entities', 'message_text', '_id_attrs') + __slots__ = ("disable_web_page_preview", "parse_mode", "entities", "message_text") def __init__( self, @@ -83,6 +83,6 @@ def to_dict(self) -> JSONDict: data = super().to_dict() if self.entities: - data['entities'] = [ce.to_dict() for ce in self.entities] + data["entities"] = [ce.to_dict() for ce in self.entities] return data diff --git a/telegram/inline/inputvenuemessagecontent.py b/telegram/inline/inputvenuemessagecontent.py index 55652d2a9a9..8d0bfd774ef 100644 --- a/telegram/inline/inputvenuemessagecontent.py +++ b/telegram/inline/inputvenuemessagecontent.py @@ -61,15 +61,14 @@ class InputVenueMessageContent(InputMessageContent): """ __slots__ = ( - 'longitude', - 'google_place_type', - 'title', - 'address', - 'foursquare_id', - 'foursquare_type', - 'google_place_id', - 'latitude', - '_id_attrs', + "longitude", + "google_place_type", + "title", + "address", + "foursquare_id", + "foursquare_type", + "google_place_id", + "latitude", ) def __init__( diff --git a/telegram/keyboardbutton.py b/telegram/keyboardbutton.py index 590801b2c42..d5aa2164486 100644 --- a/telegram/keyboardbutton.py +++ b/telegram/keyboardbutton.py @@ -58,7 +58,7 @@ class KeyboardButton(TelegramObject): """ - __slots__ = ('request_location', 'request_contact', 'request_poll', 'text', '_id_attrs') + __slots__ = ("request_location", "request_contact", "request_poll", "text") def __init__( self, diff --git a/telegram/keyboardbuttonpolltype.py b/telegram/keyboardbuttonpolltype.py index 89be62a0213..2817f3c0e5c 100644 --- a/telegram/keyboardbuttonpolltype.py +++ b/telegram/keyboardbuttonpolltype.py @@ -37,7 +37,7 @@ class KeyboardButtonPollType(TelegramObject): create a poll of any type. """ - __slots__ = ('type', '_id_attrs') + __slots__ = ("type",) def __init__(self, type: str = None, **_kwargs: Any): # pylint: disable=W0622 self.type = type diff --git a/telegram/loginurl.py b/telegram/loginurl.py index a5f38300a61..207ff1855bb 100644 --- a/telegram/loginurl.py +++ b/telegram/loginurl.py @@ -69,7 +69,7 @@ class LoginUrl(TelegramObject): """ - __slots__ = ('bot_username', 'request_write_access', 'url', 'forward_text', '_id_attrs') + __slots__ = ("bot_username", "request_write_access", "url", "forward_text") def __init__( self, diff --git a/telegram/message.py b/telegram/message.py index 63e18bf8069..ccca9299542 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -332,105 +332,104 @@ class Message(TelegramObject): # fmt: on __slots__ = ( - 'reply_markup', - 'audio', - 'contact', - 'migrate_to_chat_id', - 'forward_signature', - 'chat', - 'successful_payment', - 'game', - 'text', - 'forward_sender_name', - 'document', - 'new_chat_title', - 'forward_date', - 'group_chat_created', - 'media_group_id', - 'caption', - 'video', - 'bot', - 'entities', - 'via_bot', - 'new_chat_members', - 'connected_website', - 'animation', - 'migrate_from_chat_id', - 'forward_from', - 'sticker', - 'location', - 'venue', - 'edit_date', - 'reply_to_message', - 'passport_data', - 'pinned_message', - 'forward_from_chat', - 'new_chat_photo', - 'message_id', - 'delete_chat_photo', - 'from_user', - 'author_signature', - 'proximity_alert_triggered', - 'sender_chat', - 'dice', - 'forward_from_message_id', - 'caption_entities', - 'voice', - 'date', - 'supergroup_chat_created', - 'poll', - 'left_chat_member', - 'photo', - 'channel_chat_created', - 'invoice', - 'video_note', - '_effective_attachment', - 'message_auto_delete_timer_changed', - 'voice_chat_ended', - 'voice_chat_participants_invited', - 'voice_chat_started', - 'voice_chat_scheduled', - '_id_attrs', + "reply_markup", + "audio", + "contact", + "migrate_to_chat_id", + "forward_signature", + "chat", + "successful_payment", + "game", + "text", + "forward_sender_name", + "document", + "new_chat_title", + "forward_date", + "group_chat_created", + "media_group_id", + "caption", + "video", + "bot", + "entities", + "via_bot", + "new_chat_members", + "connected_website", + "animation", + "migrate_from_chat_id", + "forward_from", + "sticker", + "location", + "venue", + "edit_date", + "reply_to_message", + "passport_data", + "pinned_message", + "forward_from_chat", + "new_chat_photo", + "message_id", + "delete_chat_photo", + "from_user", + "author_signature", + "proximity_alert_triggered", + "sender_chat", + "dice", + "forward_from_message_id", + "caption_entities", + "voice", + "date", + "supergroup_chat_created", + "poll", + "left_chat_member", + "photo", + "channel_chat_created", + "invoice", + "video_note", + "_effective_attachment", + "message_auto_delete_timer_changed", + "voice_chat_ended", + "voice_chat_participants_invited", + "voice_chat_started", + "voice_chat_scheduled", ) ATTACHMENT_TYPES: ClassVar[List[str]] = [ - 'audio', - 'game', - 'animation', - 'document', - 'photo', - 'sticker', - 'video', - 'voice', - 'video_note', - 'contact', - 'location', - 'venue', - 'invoice', - 'successful_payment', + "audio", + "game", + "animation", + "document", + "photo", + "sticker", + "video", + "voice", + "video_note", + "contact", + "location", + "venue", + "invoice", + "successful_payment", ] MESSAGE_TYPES: ClassVar[List[str]] = [ - 'text', - 'new_chat_members', - 'left_chat_member', - 'new_chat_title', - 'new_chat_photo', - 'delete_chat_photo', - 'group_chat_created', - 'supergroup_chat_created', - 'channel_chat_created', - 'message_auto_delete_timer_changed', - 'migrate_to_chat_id', - 'migrate_from_chat_id', - 'pinned_message', - 'poll', - 'dice', - 'passport_data', - 'proximity_alert_triggered', - 'voice_chat_scheduled', - 'voice_chat_started', - 'voice_chat_ended', - 'voice_chat_participants_invited', + "text", + "new_chat_members", + "left_chat_member", + "new_chat_title", + "new_chat_photo", + "delete_chat_photo", + "group_chat_created", + "supergroup_chat_created", + "channel_chat_created", + "message_auto_delete_timer_changed", + "migrate_to_chat_id", + "migrate_from_chat_id", + "pinned_message", + "poll", + "dice", + "passport_data", + "proximity_alert_triggered", + "voice_chat_scheduled", + "voice_chat_started", + "voice_chat_ended", + "voice_chat_participants_invited", ] + ATTACHMENT_TYPES def __init__( @@ -443,11 +442,11 @@ def __init__( forward_from_chat: Chat = None, forward_from_message_id: int = None, forward_date: datetime.datetime = None, - reply_to_message: 'Message' = None, + reply_to_message: "Message" = None, edit_date: datetime.datetime = None, text: str = None, - entities: List['MessageEntity'] = None, - caption_entities: List['MessageEntity'] = None, + entities: List["MessageEntity"] = None, + caption_entities: List["MessageEntity"] = None, audio: Audio = None, document: Document = None, game: Game = None, @@ -470,7 +469,7 @@ def __init__( channel_chat_created: bool = False, migrate_to_chat_id: int = None, migrate_from_chat_id: int = None, - pinned_message: 'Message' = None, + pinned_message: "Message" = None, invoice: Invoice = None, successful_payment: SuccessfulPayment = None, forward_signature: str = None, @@ -482,7 +481,7 @@ def __init__( poll: Poll = None, forward_sender_name: str = None, reply_markup: InlineKeyboardMarkup = None, - bot: 'Bot' = None, + bot: "Bot" = None, dice: Dice = None, via_bot: User = None, proximity_alert_triggered: ProximityAlertTriggered = None, @@ -578,60 +577,60 @@ def link(self) -> Optional[str]: return None @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Message']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Message"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['from_user'] = User.de_json(data.get('from'), bot) - data['sender_chat'] = Chat.de_json(data.get('sender_chat'), bot) - data['date'] = from_timestamp(data['date']) - data['chat'] = Chat.de_json(data.get('chat'), bot) - data['entities'] = MessageEntity.de_list(data.get('entities'), bot) - data['caption_entities'] = MessageEntity.de_list(data.get('caption_entities'), bot) - data['forward_from'] = User.de_json(data.get('forward_from'), bot) - data['forward_from_chat'] = Chat.de_json(data.get('forward_from_chat'), bot) - data['forward_date'] = from_timestamp(data.get('forward_date')) - data['reply_to_message'] = Message.de_json(data.get('reply_to_message'), bot) - data['edit_date'] = from_timestamp(data.get('edit_date')) - data['audio'] = Audio.de_json(data.get('audio'), bot) - data['document'] = Document.de_json(data.get('document'), bot) - data['animation'] = Animation.de_json(data.get('animation'), bot) - data['game'] = Game.de_json(data.get('game'), bot) - data['photo'] = PhotoSize.de_list(data.get('photo'), bot) - data['sticker'] = Sticker.de_json(data.get('sticker'), bot) - data['video'] = Video.de_json(data.get('video'), bot) - data['voice'] = Voice.de_json(data.get('voice'), bot) - data['video_note'] = VideoNote.de_json(data.get('video_note'), bot) - data['contact'] = Contact.de_json(data.get('contact'), bot) - data['location'] = Location.de_json(data.get('location'), bot) - data['venue'] = Venue.de_json(data.get('venue'), bot) - data['new_chat_members'] = User.de_list(data.get('new_chat_members'), bot) - data['left_chat_member'] = User.de_json(data.get('left_chat_member'), bot) - data['new_chat_photo'] = PhotoSize.de_list(data.get('new_chat_photo'), bot) - data['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json( - data.get('message_auto_delete_timer_changed'), bot + data["from_user"] = User.de_json(data.get("from"), bot) + data["sender_chat"] = Chat.de_json(data.get("sender_chat"), bot) + data["date"] = from_timestamp(data["date"]) + data["chat"] = Chat.de_json(data.get("chat"), bot) + data["entities"] = MessageEntity.de_list(data.get("entities"), bot) + data["caption_entities"] = MessageEntity.de_list(data.get("caption_entities"), bot) + data["forward_from"] = User.de_json(data.get("forward_from"), bot) + data["forward_from_chat"] = Chat.de_json(data.get("forward_from_chat"), bot) + data["forward_date"] = from_timestamp(data.get("forward_date")) + data["reply_to_message"] = Message.de_json(data.get("reply_to_message"), bot) + data["edit_date"] = from_timestamp(data.get("edit_date")) + data["audio"] = Audio.de_json(data.get("audio"), bot) + data["document"] = Document.de_json(data.get("document"), bot) + data["animation"] = Animation.de_json(data.get("animation"), bot) + data["game"] = Game.de_json(data.get("game"), bot) + data["photo"] = PhotoSize.de_list(data.get("photo"), bot) + data["sticker"] = Sticker.de_json(data.get("sticker"), bot) + data["video"] = Video.de_json(data.get("video"), bot) + data["voice"] = Voice.de_json(data.get("voice"), bot) + data["video_note"] = VideoNote.de_json(data.get("video_note"), bot) + data["contact"] = Contact.de_json(data.get("contact"), bot) + data["location"] = Location.de_json(data.get("location"), bot) + data["venue"] = Venue.de_json(data.get("venue"), bot) + data["new_chat_members"] = User.de_list(data.get("new_chat_members"), bot) + data["left_chat_member"] = User.de_json(data.get("left_chat_member"), bot) + data["new_chat_photo"] = PhotoSize.de_list(data.get("new_chat_photo"), bot) + data["message_auto_delete_timer_changed"] = MessageAutoDeleteTimerChanged.de_json( + data.get("message_auto_delete_timer_changed"), bot ) - data['pinned_message'] = Message.de_json(data.get('pinned_message'), bot) - data['invoice'] = Invoice.de_json(data.get('invoice'), bot) - data['successful_payment'] = SuccessfulPayment.de_json(data.get('successful_payment'), bot) - data['passport_data'] = PassportData.de_json(data.get('passport_data'), bot) - data['poll'] = Poll.de_json(data.get('poll'), bot) - data['dice'] = Dice.de_json(data.get('dice'), bot) - data['via_bot'] = User.de_json(data.get('via_bot'), bot) - data['proximity_alert_triggered'] = ProximityAlertTriggered.de_json( - data.get('proximity_alert_triggered'), bot + data["pinned_message"] = Message.de_json(data.get("pinned_message"), bot) + data["invoice"] = Invoice.de_json(data.get("invoice"), bot) + data["successful_payment"] = SuccessfulPayment.de_json(data.get("successful_payment"), bot) + data["passport_data"] = PassportData.de_json(data.get("passport_data"), bot) + data["poll"] = Poll.de_json(data.get("poll"), bot) + data["dice"] = Dice.de_json(data.get("dice"), bot) + data["via_bot"] = User.de_json(data.get("via_bot"), bot) + data["proximity_alert_triggered"] = ProximityAlertTriggered.de_json( + data.get("proximity_alert_triggered"), bot ) - data['reply_markup'] = InlineKeyboardMarkup.de_json(data.get('reply_markup'), bot) - data['voice_chat_scheduled'] = VoiceChatScheduled.de_json( - data.get('voice_chat_scheduled'), bot + data["reply_markup"] = InlineKeyboardMarkup.de_json(data.get("reply_markup"), bot) + data["voice_chat_scheduled"] = VoiceChatScheduled.de_json( + data.get("voice_chat_scheduled"), bot ) - data['voice_chat_started'] = VoiceChatStarted.de_json(data.get('voice_chat_started'), bot) - data['voice_chat_ended'] = VoiceChatEnded.de_json(data.get('voice_chat_ended'), bot) - data['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json( - data.get('voice_chat_participants_invited'), bot + data["voice_chat_started"] = VoiceChatStarted.de_json(data.get("voice_chat_started"), bot) + data["voice_chat_ended"] = VoiceChatEnded.de_json(data.get("voice_chat_ended"), bot) + data["voice_chat_participants_invited"] = VoiceChatParticipantsInvited.de_json( + data.get("voice_chat_participants_invited"), bot ) return cls(bot=bot, **data) @@ -685,29 +684,29 @@ def effective_attachment( return self._effective_attachment # type: ignore def __getitem__(self, item: str) -> Any: # pylint: disable=R1710 - return self.chat.id if item == 'chat_id' else super().__getitem__(item) + return self.chat.id if item == "chat_id" else super().__getitem__(item) def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() # Required - data['date'] = to_timestamp(self.date) + data["date"] = to_timestamp(self.date) # Optionals if self.forward_date: - data['forward_date'] = to_timestamp(self.forward_date) + data["forward_date"] = to_timestamp(self.forward_date) if self.edit_date: - data['edit_date'] = to_timestamp(self.edit_date) + data["edit_date"] = to_timestamp(self.edit_date) if self.photo: - data['photo'] = [p.to_dict() for p in self.photo] + data["photo"] = [p.to_dict() for p in self.photo] if self.entities: - data['entities'] = [e.to_dict() for e in self.entities] + data["entities"] = [e.to_dict() for e in self.entities] if self.caption_entities: - data['caption_entities'] = [e.to_dict() for e in self.caption_entities] + data["caption_entities"] = [e.to_dict() for e in self.caption_entities] if self.new_chat_photo: - data['new_chat_photo'] = [p.to_dict() for p in self.new_chat_photo] + data["new_chat_photo"] = [p.to_dict() for p in self.new_chat_photo] if self.new_chat_members: - data['new_chat_members'] = [u.to_dict() for u in self.new_chat_members] + data["new_chat_members"] = [u.to_dict() for u in self.new_chat_members] return data @@ -741,9 +740,9 @@ def reply_text( timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_message(update.effective_message.chat_id, *args, **kwargs) @@ -785,9 +784,9 @@ def reply_markdown( timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_message( @@ -839,9 +838,9 @@ def reply_markdown_v2( timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_message( @@ -889,9 +888,9 @@ def reply_html( timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_message( @@ -932,7 +931,7 @@ def reply_html( def reply_media_group( self, media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] ], disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, @@ -940,7 +939,7 @@ def reply_media_group( api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> List['Message']: + ) -> List["Message"]: """Shortcut for:: bot.send_media_group(update.effective_message.chat_id, *args, **kwargs) @@ -972,7 +971,7 @@ def reply_media_group( def reply_photo( self, - photo: Union[FileInput, 'PhotoSize'], + photo: Union[FileInput, "PhotoSize"], caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, @@ -981,10 +980,10 @@ def reply_photo( parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_photo(update.effective_message.chat_id, *args, **kwargs) @@ -1019,7 +1018,7 @@ def reply_photo( def reply_audio( self, - audio: Union[FileInput, 'Audio'], + audio: Union[FileInput, "Audio"], duration: int = None, performer: str = None, title: str = None, @@ -1032,10 +1031,10 @@ def reply_audio( thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_audio(update.effective_message.chat_id, *args, **kwargs) @@ -1074,7 +1073,7 @@ def reply_audio( def reply_document( self, - document: Union[FileInput, 'Document'], + document: Union[FileInput, "Document"], filename: str = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1086,9 +1085,9 @@ def reply_document( api_kwargs: JSONDict = None, disable_content_type_detection: bool = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_document(update.effective_message.chat_id, *args, **kwargs) @@ -1125,7 +1124,7 @@ def reply_document( def reply_animation( self, - animation: Union[FileInput, 'Animation'], + animation: Union[FileInput, "Animation"], duration: int = None, width: int = None, height: int = None, @@ -1138,10 +1137,10 @@ def reply_animation( timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_animation(update.effective_message.chat_id, *args, **kwargs) @@ -1180,7 +1179,7 @@ def reply_animation( def reply_sticker( self, - sticker: Union[FileInput, 'Sticker'], + sticker: Union[FileInput, "Sticker"], disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, reply_markup: ReplyMarkup = None, @@ -1188,7 +1187,7 @@ def reply_sticker( api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_sticker(update.effective_message.chat_id, *args, **kwargs) @@ -1219,7 +1218,7 @@ def reply_sticker( def reply_video( self, - video: Union[FileInput, 'Video'], + video: Union[FileInput, "Video"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1233,10 +1232,10 @@ def reply_video( thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_video(update.effective_message.chat_id, *args, **kwargs) @@ -1276,7 +1275,7 @@ def reply_video( def reply_video_note( self, - video_note: Union[FileInput, 'VideoNote'], + video_note: Union[FileInput, "VideoNote"], duration: int = None, length: int = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1288,7 +1287,7 @@ def reply_video_note( allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, filename: str = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_video_note(update.effective_message.chat_id, *args, **kwargs) @@ -1323,7 +1322,7 @@ def reply_video_note( def reply_voice( self, - voice: Union[FileInput, 'Voice'], + voice: Union[FileInput, "Voice"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, @@ -1333,10 +1332,10 @@ def reply_voice( parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_voice(update.effective_message.chat_id, *args, **kwargs) @@ -1386,7 +1385,7 @@ def reply_location( proximity_alert_radius: int = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_location(update.effective_message.chat_id, *args, **kwargs) @@ -1439,7 +1438,7 @@ def reply_venue( google_place_type: str = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_venue(update.effective_message.chat_id, *args, **kwargs) @@ -1490,7 +1489,7 @@ def reply_contact( api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_contact(update.effective_message.chat_id, *args, **kwargs) @@ -1542,9 +1541,9 @@ def reply_poll( close_date: Union[int, datetime.datetime] = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + explanation_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_poll(update.effective_message.chat_id, *args, **kwargs) @@ -1594,7 +1593,7 @@ def reply_dice( api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_dice(update.effective_message.chat_id, *args, **kwargs) @@ -1653,12 +1652,12 @@ def reply_game( game_short_name: str, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, quote: bool = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_game(update.effective_message.chat_id, *args, **kwargs) @@ -1696,7 +1695,7 @@ def reply_invoice( payload: str, provider_token: str, currency: str, - prices: List['LabeledPrice'], + prices: List["LabeledPrice"], start_parameter: str = None, photo_url: str = None, photo_size: int = None, @@ -1709,7 +1708,7 @@ def reply_invoice( is_flexible: bool = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, provider_data: Union[str, object] = None, send_phone_number_to_provider: bool = None, send_email_to_provider: bool = None, @@ -1719,7 +1718,7 @@ def reply_invoice( quote: bool = None, max_tip_amount: int = None, suggested_tip_amounts: List[int] = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_invoice(update.effective_message.chat_id, *args, **kwargs) @@ -1784,7 +1783,7 @@ def forward( disable_notification: DVInput[bool] = DEFAULT_NONE, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.forward_message(chat_id=chat_id, @@ -1813,14 +1812,14 @@ def copy( chat_id: Union[int, str], caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, reply_markup: ReplyMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: bot.copy_message(chat_id=chat_id, @@ -1856,7 +1855,7 @@ def reply_copy( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, @@ -1864,7 +1863,7 @@ def reply_copy( timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, quote: bool = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: bot.copy_message(chat_id=message.chat.id, @@ -1911,8 +1910,8 @@ def edit_text( reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union['Message', bool]: + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> Union["Message", bool]: """Shortcut for:: bot.edit_message_text(chat_id=message.chat_id, @@ -1952,8 +1951,8 @@ def edit_caption( timeout: ODVInput[float] = DEFAULT_NONE, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> Union['Message', bool]: + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> Union["Message", bool]: """Shortcut for:: bot.edit_message_caption(chat_id=message.chat_id, @@ -1988,11 +1987,11 @@ def edit_caption( def edit_media( self, - media: 'InputMedia' = None, + media: "InputMedia", reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: + ) -> Union["Message", bool]: """Shortcut for:: bot.edit_message_media(chat_id=message.chat_id, @@ -2009,14 +2008,14 @@ def edit_media( behaviour is undocumented and might be changed by Telegram. Returns: - :class:`telegram.Message`: On success, if edited message is sent by the bot, the + :class:`telegram.Message`: On success, if edited message is not an inline message, the edited Message is returned, otherwise ``True`` is returned. """ return self.bot.edit_message_media( + media=media, chat_id=self.chat_id, message_id=self.message_id, - media=media, reply_markup=reply_markup, timeout=timeout, api_kwargs=api_kwargs, @@ -2025,10 +2024,10 @@ def edit_media( def edit_reply_markup( self, - reply_markup: Optional['InlineKeyboardMarkup'] = None, + reply_markup: Optional["InlineKeyboardMarkup"] = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: + ) -> Union["Message", bool]: """Shortcut for:: bot.edit_message_reply_markup(chat_id=message.chat_id, @@ -2068,7 +2067,7 @@ def edit_live_location( horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, - ) -> Union['Message', bool]: + ) -> Union["Message", bool]: """Shortcut for:: bot.edit_message_live_location(chat_id=message.chat_id, @@ -2108,7 +2107,7 @@ def stop_live_location( reply_markup: InlineKeyboardMarkup = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: + ) -> Union["Message", bool]: """Shortcut for:: bot.stop_message_live_location(chat_id=message.chat_id, @@ -2145,7 +2144,7 @@ def set_game_score( disable_edit_message: bool = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Union['Message', bool]: + ) -> Union["Message", bool]: """Shortcut for:: bot.set_game_score(chat_id=message.chat_id, @@ -2181,7 +2180,7 @@ def get_game_high_scores( user_id: Union[int, str], timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> List['GameHighScore']: + ) -> List["GameHighScore"]: """Shortcut for:: bot.get_game_high_scores(chat_id=message.chat_id, @@ -2340,9 +2339,9 @@ def parse_entity(self, entity: MessageEntity) -> str: if sys.maxunicode == 0xFFFF: return self.text[entity.offset : entity.offset + entity.length] - entity_text = self.text.encode('utf-16-le') + entity_text = self.text.encode("utf-16-le") entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - return entity_text.decode('utf-16-le') + return entity_text.decode("utf-16-le") def parse_caption_entity(self, entity: MessageEntity) -> str: """Returns the text from a given :class:`telegram.MessageEntity`. @@ -2370,9 +2369,9 @@ def parse_caption_entity(self, entity: MessageEntity) -> str: if sys.maxunicode == 0xFFFF: return self.caption[entity.offset : entity.offset + entity.length] - entity_text = self.caption.encode('utf-16-le') + entity_text = self.caption.encode("utf-16-le") entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - return entity_text.decode('utf-16-le') + return entity_text.decode("utf-16-le") def parse_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: """ @@ -2449,9 +2448,9 @@ def _parse_html( return None if sys.maxunicode != 0xFFFF: - message_text = message_text.encode('utf-16-le') # type: ignore + message_text = message_text.encode("utf-16-le") # type: ignore - html_text = '' + html_text = "" last_offset = 0 sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset)) @@ -2483,20 +2482,20 @@ def _parse_html( elif entity.type == MessageEntity.URL and urled: insert = f'{text}' elif entity.type == MessageEntity.BOLD: - insert = '' + text + '' + insert = "" + text + "" elif entity.type == MessageEntity.ITALIC: - insert = '' + text + '' + insert = "" + text + "" elif entity.type == MessageEntity.CODE: - insert = '' + text + '' + insert = "" + text + "" elif entity.type == MessageEntity.PRE: if entity.language: insert = f'
{text}
' else: - insert = '
' + text + '
' + insert = "
" + text + "
" elif entity.type == MessageEntity.UNDERLINE: - insert = '' + text + '' + insert = "" + text + "" elif entity.type == MessageEntity.STRIKETHROUGH: - insert = '' + text + '' + insert = "" + text + "" else: insert = text @@ -2510,7 +2509,7 @@ def _parse_html( escape( message_text[ # type: ignore last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le') + ].decode("utf-16-le") ) + insert ) @@ -2521,7 +2520,7 @@ def _parse_html( html_text += ( message_text[ # type: ignore last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le') + ].decode("utf-16-le") + insert ) @@ -2532,13 +2531,13 @@ def _parse_html( html_text += escape(message_text[last_offset:]) else: html_text += escape( - message_text[last_offset * 2 :].decode('utf-16-le') # type: ignore + message_text[last_offset * 2 :].decode("utf-16-le") # type: ignore ) else: if sys.maxunicode == 0xFFFF: html_text += message_text[last_offset:] else: - html_text += message_text[last_offset * 2 :].decode('utf-16-le') # type: ignore + html_text += message_text[last_offset * 2 :].decode("utf-16-le") # type: ignore return html_text @@ -2610,9 +2609,9 @@ def _parse_markdown( return None if sys.maxunicode != 0xFFFF: - message_text = message_text.encode('utf-16-le') # type: ignore + message_text = message_text.encode("utf-16-le") # type: ignore - markdown_text = '' + markdown_text = "" last_offset = 0 sorted_entities = sorted(entities.items(), key=(lambda item: item[0].offset)) @@ -2635,7 +2634,7 @@ def _parse_markdown( if nested_entities: if version < 2: raise ValueError( - 'Nested entities are not supported for Markdown ' 'version 1' + "Nested entities are not supported for Markdown " "version 1" ) text = Message._parse_markdown( @@ -2654,27 +2653,27 @@ def _parse_markdown( url = escape_markdown( entity.url, version=version, entity_type=MessageEntity.TEXT_LINK ) - insert = f'[{text}]({url})' + insert = f"[{text}]({url})" elif entity.type == MessageEntity.TEXT_MENTION and entity.user: - insert = f'[{text}](tg://user?id={entity.user.id})' + insert = f"[{text}](tg://user?id={entity.user.id})" elif entity.type == MessageEntity.URL and urled: if version == 1: link = orig_text else: link = text - insert = f'[{link}]({orig_text})' + insert = f"[{link}]({orig_text})" elif entity.type == MessageEntity.BOLD: - insert = '*' + text + '*' + insert = "*" + text + "*" elif entity.type == MessageEntity.ITALIC: - insert = '_' + text + '_' + insert = "_" + text + "_" elif entity.type == MessageEntity.CODE: # Monospace needs special escaping. Also can't have entities nested within insert = ( - '`' + "`" + escape_markdown( orig_text, version=version, entity_type=MessageEntity.CODE ) - + '`' + + "`" ) elif entity.type == MessageEntity.PRE: # Monospace needs special escaping. Also can't have entities nested within @@ -2682,25 +2681,25 @@ def _parse_markdown( orig_text, version=version, entity_type=MessageEntity.PRE ) if entity.language: - prefix = '```' + entity.language + '\n' + prefix = "```" + entity.language + "\n" else: - if code.startswith('\\'): - prefix = '```' + if code.startswith("\\"): + prefix = "```" else: - prefix = '```\n' - insert = prefix + code + '```' + prefix = "```\n" + insert = prefix + code + "```" elif entity.type == MessageEntity.UNDERLINE: if version == 1: raise ValueError( - 'Underline entities are not supported for Markdown ' 'version 1' + "Underline entities are not supported for Markdown " "version 1" ) - insert = '__' + text + '__' + insert = "__" + text + "__" elif entity.type == MessageEntity.STRIKETHROUGH: if version == 1: raise ValueError( - 'Strikethrough entities are not supported for Markdown ' 'version 1' + "Strikethrough entities are not supported for Markdown " "version 1" ) - insert = '~' + text + '~' + insert = "~" + text + "~" else: insert = text @@ -2717,7 +2716,7 @@ def _parse_markdown( escape_markdown( message_text[ # type: ignore last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le'), + ].decode("utf-16-le"), version=version, ) + insert @@ -2731,7 +2730,7 @@ def _parse_markdown( markdown_text += ( message_text[ # type: ignore last_offset * 2 : (entity.offset - offset) * 2 - ].decode('utf-16-le') + ].decode("utf-16-le") + insert ) @@ -2742,7 +2741,7 @@ def _parse_markdown( markdown_text += escape_markdown(message_text[last_offset:], version=version) else: markdown_text += escape_markdown( - message_text[last_offset * 2 :].decode('utf-16-le'), # type: ignore + message_text[last_offset * 2 :].decode("utf-16-le"), # type: ignore version=version, ) else: @@ -2750,7 +2749,7 @@ def _parse_markdown( markdown_text += message_text[last_offset:] else: markdown_text += message_text[last_offset * 2 :].decode( # type: ignore - 'utf-16-le' + "utf-16-le" ) return markdown_text diff --git a/telegram/messageautodeletetimerchanged.py b/telegram/messageautodeletetimerchanged.py index 3fb1ce91913..78349179578 100644 --- a/telegram/messageautodeletetimerchanged.py +++ b/telegram/messageautodeletetimerchanged.py @@ -44,7 +44,7 @@ class MessageAutoDeleteTimerChanged(TelegramObject): """ - __slots__ = ('message_auto_delete_time', '_id_attrs') + __slots__ = ("message_auto_delete_time",) def __init__( self, diff --git a/telegram/messageentity.py b/telegram/messageentity.py index 0a0350eebbc..c1a8f107108 100644 --- a/telegram/messageentity.py +++ b/telegram/messageentity.py @@ -59,7 +59,7 @@ class MessageEntity(TelegramObject): """ - __slots__ = ('length', 'url', 'user', 'type', 'language', 'offset', '_id_attrs') + __slots__ = ("length", "url", "user", "type", "language", "offset") def __init__( self, @@ -83,14 +83,14 @@ def __init__( self._id_attrs = (self.type, self.offset, self.length) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MessageEntity']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["MessageEntity"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['user'] = User.de_json(data.get('user'), bot) + data["user"] = User.de_json(data.get("user"), bot) return cls(**data) diff --git a/telegram/messageid.py b/telegram/messageid.py index 56eca3a19e6..e687a4ea578 100644 --- a/telegram/messageid.py +++ b/telegram/messageid.py @@ -32,7 +32,7 @@ class MessageId(TelegramObject): message_id (:obj:`int`): Unique message identifier """ - __slots__ = ('message_id', '_id_attrs') + __slots__ = ("message_id",) def __init__(self, message_id: int, **_kwargs: Any): self.message_id = int(message_id) diff --git a/telegram/parsemode.py b/telegram/parsemode.py index 86bc07b368a..2ecdf2b6af2 100644 --- a/telegram/parsemode.py +++ b/telegram/parsemode.py @@ -21,13 +21,12 @@ from typing import ClassVar from telegram import constants -from telegram.utils.deprecate import set_new_attribute_deprecated class ParseMode: """This object represents a Telegram Message Parse Modes.""" - __slots__ = ('__dict__',) + __slots__ = () MARKDOWN: ClassVar[str] = constants.PARSEMODE_MARKDOWN """:const:`telegram.constants.PARSEMODE_MARKDOWN`\n @@ -40,6 +39,3 @@ class ParseMode: """:const:`telegram.constants.PARSEMODE_MARKDOWN_V2`""" HTML: ClassVar[str] = constants.PARSEMODE_HTML """:const:`telegram.constants.PARSEMODE_HTML`""" - - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) diff --git a/telegram/passport/credentials.py b/telegram/passport/credentials.py index 156c79de883..39c80c451e3 100644 --- a/telegram/passport/credentials.py +++ b/telegram/passport/credentials.py @@ -23,7 +23,7 @@ import json # type: ignore[no-redef] from base64 import b64decode -from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union, no_type_check +from typing import TYPE_CHECKING, Any, List, Optional, no_type_check try: from cryptography.hazmat.backends import default_backend @@ -41,26 +41,13 @@ CRYPTO_INSTALLED = False -from telegram import TelegramError, TelegramObject +from telegram import TelegramObject, PassportDecryptionError from telegram.utils.types import JSONDict if TYPE_CHECKING: from telegram import Bot -class TelegramDecryptionError(TelegramError): - """Something went wrong with decryption.""" - - __slots__ = ('_msg',) - - def __init__(self, message: Union[str, Exception]): - super().__init__(f"TelegramDecryptionError: {message}") - self._msg = str(message) - - def __reduce__(self) -> Tuple[type, Tuple[str]]: - return self.__class__, (self._msg,) - - @no_type_check def decrypt(secret, hash, data): """ @@ -77,7 +64,7 @@ def decrypt(secret, hash, data): b64decode it. Raises: - :class:`TelegramDecryptionError`: Given hash does not match hash of decrypted data. + :class:`PassportDecryptionError`: Given hash does not match hash of decrypted data. Returns: :obj:`bytes`: The decrypted data as bytes. @@ -85,8 +72,8 @@ def decrypt(secret, hash, data): """ if not CRYPTO_INSTALLED: raise RuntimeError( - 'To use Telegram Passports, PTB must be installed via `pip install ' - 'python-telegram-bot[passport]`.' + "To use Telegram Passports, PTB must be installed via `pip install " + "python-telegram-bot[passport]`." ) # Make a SHA512 hash of secret + update digest = Hash(SHA512(), backend=default_backend()) @@ -105,7 +92,7 @@ def decrypt(secret, hash, data): # If the newly calculated hash did not match the one telegram gave us if data_hash != hash: # Raise a error that is caught inside telegram.PassportData and transformed into a warning - raise TelegramDecryptionError(f"Hashes are not equal! {data_hash} != {hash}") + raise PassportDecryptionError(f"Hashes are not equal! {data_hash} != {hash}") # Return data without padding return data[data[0] :] @@ -113,7 +100,7 @@ def decrypt(secret, hash, data): @no_type_check def decrypt_json(secret, hash, data): """Decrypts data using secret and hash and then decodes utf-8 string and loads json""" - return json.loads(decrypt(secret, hash, data).decode('utf-8')) + return json.loads(decrypt(secret, hash, data).decode("utf-8")) class EncryptedCredentials(TelegramObject): @@ -146,16 +133,15 @@ class EncryptedCredentials(TelegramObject): """ __slots__ = ( - 'hash', - 'secret', - 'bot', - 'data', - '_id_attrs', - '_decrypted_secret', - '_decrypted_data', + "hash", + "secret", + "bot", + "data", + "_decrypted_secret", + "_decrypted_data", ) - def __init__(self, data: str, hash: str, secret: str, bot: 'Bot' = None, **_kwargs: Any): + def __init__(self, data: str, hash: str, secret: str, bot: "Bot" = None, **_kwargs: Any): # Required self.data = data self.hash = hash @@ -165,7 +151,7 @@ def __init__(self, data: str, hash: str, secret: str, bot: 'Bot' = None, **_kwar self.bot = bot self._decrypted_secret = None - self._decrypted_data: Optional['Credentials'] = None + self._decrypted_data: Optional["Credentials"] = None @property def decrypted_secret(self) -> str: @@ -173,14 +159,14 @@ def decrypted_secret(self) -> str: :obj:`str`: Lazily decrypt and return secret. Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad + telegram.PassportDecryptionError: Decryption failed. Usually due to bad private/public key but can also suggest malformed/tampered data. """ if self._decrypted_secret is None: if not CRYPTO_INSTALLED: raise RuntimeError( - 'To use Telegram Passports, PTB must be installed via `pip install ' - 'python-telegram-bot[passport]`.' + "To use Telegram Passports, PTB must be installed via `pip install " + "python-telegram-bot[passport]`." ) # Try decrypting according to step 1 at # https://core.telegram.org/passport#decrypting-data @@ -195,18 +181,18 @@ def decrypted_secret(self) -> str: ) except ValueError as exception: # If decryption fails raise exception - raise TelegramDecryptionError(exception) from exception + raise PassportDecryptionError(exception) from exception return self._decrypted_secret @property - def decrypted_data(self) -> 'Credentials': + def decrypted_data(self) -> "Credentials": """ :class:`telegram.Credentials`: Lazily decrypt and return credentials data. This object also contains the user specified nonce as `decrypted_data.nonce`. Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad + telegram.PassportDecryptionError: Decryption failed. Usually due to bad private/public key but can also suggest malformed/tampered data. """ if self._decrypted_data is None: @@ -224,9 +210,9 @@ class Credentials(TelegramObject): nonce (:obj:`str`): Bot-specified nonce """ - __slots__ = ('bot', 'nonce', 'secure_data') + __slots__ = ("bot", "nonce", "secure_data") - def __init__(self, secure_data: 'SecureData', nonce: str, bot: 'Bot' = None, **_kwargs: Any): + def __init__(self, secure_data: "SecureData", nonce: str, bot: "Bot" = None, **_kwargs: Any): # Required self.secure_data = secure_data self.nonce = nonce @@ -234,14 +220,14 @@ def __init__(self, secure_data: 'SecureData', nonce: str, bot: 'Bot' = None, **_ self.bot = bot @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Credentials']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Credentials"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['secure_data'] = SecureData.de_json(data.get('secure_data'), bot=bot) + data["secure_data"] = SecureData.de_json(data.get("secure_data"), bot=bot) return cls(bot=bot, **data) @@ -275,34 +261,34 @@ class SecureData(TelegramObject): """ __slots__ = ( - 'bot', - 'utility_bill', - 'personal_details', - 'temporary_registration', - 'address', - 'driver_license', - 'rental_agreement', - 'internal_passport', - 'identity_card', - 'bank_statement', - 'passport', - 'passport_registration', + "bot", + "utility_bill", + "personal_details", + "temporary_registration", + "address", + "driver_license", + "rental_agreement", + "internal_passport", + "identity_card", + "bank_statement", + "passport", + "passport_registration", ) def __init__( self, - personal_details: 'SecureValue' = None, - passport: 'SecureValue' = None, - internal_passport: 'SecureValue' = None, - driver_license: 'SecureValue' = None, - identity_card: 'SecureValue' = None, - address: 'SecureValue' = None, - utility_bill: 'SecureValue' = None, - bank_statement: 'SecureValue' = None, - rental_agreement: 'SecureValue' = None, - passport_registration: 'SecureValue' = None, - temporary_registration: 'SecureValue' = None, - bot: 'Bot' = None, + personal_details: "SecureValue" = None, + passport: "SecureValue" = None, + internal_passport: "SecureValue" = None, + driver_license: "SecureValue" = None, + identity_card: "SecureValue" = None, + address: "SecureValue" = None, + utility_bill: "SecureValue" = None, + bank_statement: "SecureValue" = None, + rental_agreement: "SecureValue" = None, + passport_registration: "SecureValue" = None, + temporary_registration: "SecureValue" = None, + bot: "Bot" = None, **_kwargs: Any, ): # Optionals @@ -321,28 +307,28 @@ def __init__( self.bot = bot @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureData']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureData"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['temporary_registration'] = SecureValue.de_json( - data.get('temporary_registration'), bot=bot + data["temporary_registration"] = SecureValue.de_json( + data.get("temporary_registration"), bot=bot ) - data['passport_registration'] = SecureValue.de_json( - data.get('passport_registration'), bot=bot + data["passport_registration"] = SecureValue.de_json( + data.get("passport_registration"), bot=bot ) - data['rental_agreement'] = SecureValue.de_json(data.get('rental_agreement'), bot=bot) - data['bank_statement'] = SecureValue.de_json(data.get('bank_statement'), bot=bot) - data['utility_bill'] = SecureValue.de_json(data.get('utility_bill'), bot=bot) - data['address'] = SecureValue.de_json(data.get('address'), bot=bot) - data['identity_card'] = SecureValue.de_json(data.get('identity_card'), bot=bot) - data['driver_license'] = SecureValue.de_json(data.get('driver_license'), bot=bot) - data['internal_passport'] = SecureValue.de_json(data.get('internal_passport'), bot=bot) - data['passport'] = SecureValue.de_json(data.get('passport'), bot=bot) - data['personal_details'] = SecureValue.de_json(data.get('personal_details'), bot=bot) + data["rental_agreement"] = SecureValue.de_json(data.get("rental_agreement"), bot=bot) + data["bank_statement"] = SecureValue.de_json(data.get("bank_statement"), bot=bot) + data["utility_bill"] = SecureValue.de_json(data.get("utility_bill"), bot=bot) + data["address"] = SecureValue.de_json(data.get("address"), bot=bot) + data["identity_card"] = SecureValue.de_json(data.get("identity_card"), bot=bot) + data["driver_license"] = SecureValue.de_json(data.get("driver_license"), bot=bot) + data["internal_passport"] = SecureValue.de_json(data.get("internal_passport"), bot=bot) + data["passport"] = SecureValue.de_json(data.get("passport"), bot=bot) + data["personal_details"] = SecureValue.de_json(data.get("personal_details"), bot=bot) return cls(bot=bot, **data) @@ -374,17 +360,17 @@ class SecureValue(TelegramObject): """ - __slots__ = ('data', 'front_side', 'reverse_side', 'selfie', 'files', 'translation', 'bot') + __slots__ = ("data", "front_side", "reverse_side", "selfie", "files", "translation", "bot") def __init__( self, - data: 'DataCredentials' = None, - front_side: 'FileCredentials' = None, - reverse_side: 'FileCredentials' = None, - selfie: 'FileCredentials' = None, - files: List['FileCredentials'] = None, - translation: List['FileCredentials'] = None, - bot: 'Bot' = None, + data: "DataCredentials" = None, + front_side: "FileCredentials" = None, + reverse_side: "FileCredentials" = None, + selfie: "FileCredentials" = None, + files: List["FileCredentials"] = None, + translation: List["FileCredentials"] = None, + bot: "Bot" = None, **_kwargs: Any, ): self.data = data @@ -397,19 +383,19 @@ def __init__( self.bot = bot @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureValue']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SecureValue"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['data'] = DataCredentials.de_json(data.get('data'), bot=bot) - data['front_side'] = FileCredentials.de_json(data.get('front_side'), bot=bot) - data['reverse_side'] = FileCredentials.de_json(data.get('reverse_side'), bot=bot) - data['selfie'] = FileCredentials.de_json(data.get('selfie'), bot=bot) - data['files'] = FileCredentials.de_list(data.get('files'), bot=bot) - data['translation'] = FileCredentials.de_list(data.get('translation'), bot=bot) + data["data"] = DataCredentials.de_json(data.get("data"), bot=bot) + data["front_side"] = FileCredentials.de_json(data.get("front_side"), bot=bot) + data["reverse_side"] = FileCredentials.de_json(data.get("reverse_side"), bot=bot) + data["selfie"] = FileCredentials.de_json(data.get("selfie"), bot=bot) + data["files"] = FileCredentials.de_list(data.get("files"), bot=bot) + data["translation"] = FileCredentials.de_list(data.get("translation"), bot=bot) return cls(bot=bot, **data) @@ -417,8 +403,8 @@ 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] - data['translation'] = [p.to_dict() for p in self.translation] + data["files"] = [p.to_dict() for p in self.files] + data["translation"] = [p.to_dict() for p in self.translation] return data @@ -426,9 +412,9 @@ def to_dict(self) -> JSONDict: class _CredentialsBase(TelegramObject): """Base class for DataCredentials and FileCredentials.""" - __slots__ = ('hash', 'secret', 'file_hash', 'data_hash', 'bot') + __slots__ = ("hash", "secret", "file_hash", "data_hash", "bot") - def __init__(self, hash: str, secret: str, bot: 'Bot' = None, **_kwargs: Any): + def __init__(self, hash: str, secret: str, bot: "Bot" = None, **_kwargs: Any): self.hash = hash self.secret = secret @@ -462,8 +448,8 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - del data['file_hash'] - del data['hash'] + del data["file_hash"] + del data["hash"] return data @@ -491,7 +477,7 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - del data['data_hash'] - del data['hash'] + del data["data_hash"] + del data["hash"] return data diff --git a/telegram/passport/data.py b/telegram/passport/data.py index b17f5d87f9c..51b8007a800 100644 --- a/telegram/passport/data.py +++ b/telegram/passport/data.py @@ -47,17 +47,17 @@ class PersonalDetails(TelegramObject): """ __slots__ = ( - 'middle_name', - 'first_name_native', - 'last_name_native', - 'residence_country_code', - 'first_name', - 'last_name', - 'country_code', - 'gender', - 'bot', - 'middle_name_native', - 'birth_date', + "middle_name", + "first_name_native", + "last_name_native", + "residence_country_code", + "first_name", + "last_name", + "country_code", + "gender", + "bot", + "middle_name_native", + "birth_date", ) def __init__( @@ -72,7 +72,7 @@ def __init__( last_name_native: str = None, middle_name: str = None, middle_name_native: str = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -104,13 +104,13 @@ class ResidentialAddress(TelegramObject): """ __slots__ = ( - 'post_code', - 'city', - 'country_code', - 'street_line2', - 'street_line1', - 'bot', - 'state', + "post_code", + "city", + "country_code", + "street_line2", + "street_line1", + "bot", + "state", ) def __init__( @@ -121,7 +121,7 @@ def __init__( state: str, country_code: str, post_code: str, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -144,9 +144,9 @@ class IdDocumentData(TelegramObject): expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format. """ - __slots__ = ('document_no', 'bot', 'expiry_date') + __slots__ = ("document_no", "bot", "expiry_date") - def __init__(self, document_no: str, expiry_date: str, bot: 'Bot' = None, **_kwargs: Any): + def __init__(self, document_no: str, expiry_date: str, bot: "Bot" = None, **_kwargs: Any): self.document_no = document_no self.expiry_date = expiry_date diff --git a/telegram/passport/encryptedpassportelement.py b/telegram/passport/encryptedpassportelement.py index 74e3aaf6719..a18db7cf025 100644 --- a/telegram/passport/encryptedpassportelement.py +++ b/telegram/passport/encryptedpassportelement.py @@ -52,6 +52,8 @@ class EncryptedPassportElement(TelegramObject): "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email". + hash (:obj:`str`): Base64-encoded element hash for using in + :class:`telegram.PassportElementErrorUnspecified`. data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ :class:`telegram.ResidentialAddress` | :obj:`str`, optional): Decrypted or encrypted data, available for "personal_details", "passport", @@ -77,8 +79,6 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - hash (:obj:`str`): Base64-encoded element hash for using in - :class:`telegram.PassportElementErrorUnspecified`. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. **kwargs (:obj:`dict`): Arbitrary keyword arguments. @@ -87,6 +87,8 @@ class EncryptedPassportElement(TelegramObject): "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email". + hash (:obj:`str`): Base64-encoded element hash for using in + :class:`telegram.PassportElementErrorUnspecified`. data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ :class:`telegram.ResidentialAddress` | :obj:`str`): Optional. Decrypted or encrypted data, available for "personal_details", "passport", @@ -112,30 +114,28 @@ class EncryptedPassportElement(TelegramObject): requested for "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - hash (:obj:`str`): Base64-encoded element hash for using in - :class:`telegram.PassportElementErrorUnspecified`. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. """ __slots__ = ( - 'selfie', - 'files', - 'type', - 'translation', - 'email', - 'hash', - 'phone_number', - 'bot', - 'reverse_side', - 'front_side', - 'data', - '_id_attrs', + "selfie", + "files", + "type", + "translation", + "email", + "hash", + "phone_number", + "bot", + "reverse_side", + "front_side", + "data", ) def __init__( self, type: str, # pylint: disable=W0622 + hash: str, # pylint: disable=W0622 data: PersonalDetails = None, phone_number: str = None, email: str = None, @@ -144,9 +144,8 @@ def __init__( reverse_side: PassportFile = None, selfie: PassportFile = None, translation: List[PassportFile] = None, - hash: str = None, # pylint: disable=W0622 - bot: 'Bot' = None, - credentials: 'Credentials' = None, # pylint: disable=W0613 + bot: "Bot" = None, + credentials: "Credentials" = None, # pylint: disable=W0613 **_kwargs: Any, ): # Required @@ -176,25 +175,25 @@ def __init__( self.bot = bot @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['EncryptedPassportElement']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["EncryptedPassportElement"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['files'] = PassportFile.de_list(data.get('files'), bot) or None - data['front_side'] = PassportFile.de_json(data.get('front_side'), bot) - data['reverse_side'] = PassportFile.de_json(data.get('reverse_side'), bot) - data['selfie'] = PassportFile.de_json(data.get('selfie'), bot) - data['translation'] = PassportFile.de_list(data.get('translation'), bot) or None + data["files"] = PassportFile.de_list(data.get("files"), bot) or None + data["front_side"] = PassportFile.de_json(data.get("front_side"), bot) + data["reverse_side"] = PassportFile.de_json(data.get("reverse_side"), bot) + data["selfie"] = PassportFile.de_json(data.get("selfie"), bot) + data["translation"] = PassportFile.de_list(data.get("translation"), bot) or None return cls(bot=bot, **data) @classmethod def de_json_decrypted( - cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'Credentials' - ) -> Optional['EncryptedPassportElement']: + cls, data: Optional[JSONDict], bot: "Bot", credentials: "Credentials" + ) -> Optional["EncryptedPassportElement"]: """Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account passport credentials. @@ -210,44 +209,44 @@ def de_json_decrypted( if not data: return None - if data['type'] not in ('phone_number', 'email'): - secure_data = getattr(credentials.secure_data, data['type']) + if data["type"] not in ("phone_number", "email"): + secure_data = getattr(credentials.secure_data, data["type"]) if secure_data.data is not None: # If not already decrypted - if not isinstance(data['data'], dict): - data['data'] = decrypt_json( + if not isinstance(data["data"], dict): + data["data"] = decrypt_json( b64decode(secure_data.data.secret), b64decode(secure_data.data.hash), - b64decode(data['data']), + b64decode(data["data"]), ) - if data['type'] == 'personal_details': - data['data'] = PersonalDetails.de_json(data['data'], bot=bot) - elif data['type'] in ( - 'passport', - 'internal_passport', - 'driver_license', - 'identity_card', + if data["type"] == "personal_details": + data["data"] = PersonalDetails.de_json(data["data"], bot=bot) + elif data["type"] in ( + "passport", + "internal_passport", + "driver_license", + "identity_card", ): - data['data'] = IdDocumentData.de_json(data['data'], bot=bot) - elif data['type'] == 'address': - data['data'] = ResidentialAddress.de_json(data['data'], bot=bot) + data["data"] = IdDocumentData.de_json(data["data"], bot=bot) + elif data["type"] == "address": + data["data"] = ResidentialAddress.de_json(data["data"], bot=bot) - data['files'] = ( - PassportFile.de_list_decrypted(data.get('files'), bot, secure_data.files) or None + data["files"] = ( + PassportFile.de_list_decrypted(data.get("files"), bot, secure_data.files) or None ) - data['front_side'] = PassportFile.de_json_decrypted( - data.get('front_side'), bot, secure_data.front_side + data["front_side"] = PassportFile.de_json_decrypted( + data.get("front_side"), bot, secure_data.front_side ) - data['reverse_side'] = PassportFile.de_json_decrypted( - data.get('reverse_side'), bot, secure_data.reverse_side + data["reverse_side"] = PassportFile.de_json_decrypted( + data.get("reverse_side"), bot, secure_data.reverse_side ) - data['selfie'] = PassportFile.de_json_decrypted( - data.get('selfie'), bot, secure_data.selfie + data["selfie"] = PassportFile.de_json_decrypted( + data.get("selfie"), bot, secure_data.selfie ) - data['translation'] = ( + data["translation"] = ( PassportFile.de_list_decrypted( - data.get('translation'), bot, secure_data.translation + data.get("translation"), bot, secure_data.translation ) or None ) @@ -259,8 +258,8 @@ def to_dict(self) -> JSONDict: data = super().to_dict() if self.files: - data['files'] = [p.to_dict() for p in self.files] + data["files"] = [p.to_dict() for p in self.files] if self.translation: - data['translation'] = [p.to_dict() for p in self.translation] + data["translation"] = [p.to_dict() for p in self.translation] return data diff --git a/telegram/passport/passportdata.py b/telegram/passport/passportdata.py index a8d1ede0202..7832b6332c2 100644 --- a/telegram/passport/passportdata.py +++ b/telegram/passport/passportdata.py @@ -51,13 +51,13 @@ class PassportData(TelegramObject): """ - __slots__ = ('bot', 'credentials', 'data', '_decrypted_data', '_id_attrs') + __slots__ = ("bot", "credentials", "data", "_decrypted_data") def __init__( self, data: List[EncryptedPassportElement], credentials: EncryptedCredentials, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): self.data = data @@ -68,15 +68,15 @@ def __init__( self._id_attrs = tuple([x.type for x in data] + [credentials.hash]) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PassportData']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PassportData"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['data'] = EncryptedPassportElement.de_list(data.get('data'), bot) - data['credentials'] = EncryptedCredentials.de_json(data.get('credentials'), bot) + data["data"] = EncryptedPassportElement.de_list(data.get("data"), bot) + data["credentials"] = EncryptedCredentials.de_json(data.get("credentials"), bot) return cls(bot=bot, **data) @@ -84,7 +84,7 @@ 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] + data["data"] = [e.to_dict() for e in self.data] return data @@ -95,7 +95,7 @@ def decrypted_data(self) -> List[EncryptedPassportElement]: about documents and other Telegram Passport elements which were shared with the bot. Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad + telegram.PassportDecryptionError: Decryption failed. Usually due to bad private/public key but can also suggest malformed/tampered data. """ if self._decrypted_data is None: @@ -108,14 +108,14 @@ def decrypted_data(self) -> List[EncryptedPassportElement]: return self._decrypted_data @property - def decrypted_credentials(self) -> 'Credentials': + def decrypted_credentials(self) -> "Credentials": """ :class:`telegram.Credentials`: Lazily decrypt and return credentials that were used to decrypt the data. This object also contains the user specified payload as `decrypted_data.payload`. Raises: - telegram.TelegramDecryptionError: Decryption failed. Usually due to bad + telegram.PassportDecryptionError: Decryption failed. Usually due to bad private/public key but can also suggest malformed/tampered data. """ return self.credentials.decrypted_data diff --git a/telegram/passport/passportelementerrors.py b/telegram/passport/passportelementerrors.py index 4d61f962b42..829b9652b0a 100644 --- a/telegram/passport/passportelementerrors.py +++ b/telegram/passport/passportelementerrors.py @@ -45,8 +45,7 @@ class PassportElementError(TelegramObject): """ - # All subclasses of this class won't have _id_attrs in slots since it's added here. - __slots__ = ('message', 'source', 'type', '_id_attrs') + __slots__ = ("message", "source", "type") def __init__(self, source: str, type: str, message: str, **_kwargs: Any): # Required @@ -85,11 +84,11 @@ class PassportElementErrorDataField(PassportElementError): """ - __slots__ = ('data_hash', 'field_name') + __slots__ = ("data_hash", "field_name") def __init__(self, type: str, field_name: str, data_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('data', type, message) + super().__init__("data", type, message) self.field_name = field_name self.data_hash = data_hash @@ -122,11 +121,11 @@ class PassportElementErrorFile(PassportElementError): """ - __slots__ = ('file_hash',) + __slots__ = ("file_hash",) def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('file', type, message) + super().__init__("file", type, message) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -158,11 +157,11 @@ class PassportElementErrorFiles(PassportElementError): """ - __slots__ = ('file_hashes',) + __slots__ = ("file_hashes",) def __init__(self, type: str, file_hashes: str, message: str, **_kwargs: Any): # Required - super().__init__('files', type, message) + super().__init__("files", type, message) self.file_hashes = file_hashes self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) @@ -194,11 +193,11 @@ class PassportElementErrorFrontSide(PassportElementError): """ - __slots__ = ('file_hash',) + __slots__ = ("file_hash",) def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('front_side', type, message) + super().__init__("front_side", type, message) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -230,11 +229,11 @@ class PassportElementErrorReverseSide(PassportElementError): """ - __slots__ = ('file_hash',) + __slots__ = ("file_hash",) def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('reverse_side', type, message) + super().__init__("reverse_side", type, message) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -264,11 +263,11 @@ class PassportElementErrorSelfie(PassportElementError): """ - __slots__ = ('file_hash',) + __slots__ = ("file_hash",) def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('selfie', type, message) + super().__init__("selfie", type, message) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -302,11 +301,11 @@ class PassportElementErrorTranslationFile(PassportElementError): """ - __slots__ = ('file_hash',) + __slots__ = ("file_hash",) def __init__(self, type: str, file_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('translation_file', type, message) + super().__init__("translation_file", type, message) self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) @@ -340,11 +339,11 @@ class PassportElementErrorTranslationFiles(PassportElementError): """ - __slots__ = ('file_hashes',) + __slots__ = ("file_hashes",) def __init__(self, type: str, file_hashes: str, message: str, **_kwargs: Any): # Required - super().__init__('translation_files', type, message) + super().__init__("translation_files", type, message) self.file_hashes = file_hashes self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) @@ -372,11 +371,11 @@ class PassportElementErrorUnspecified(PassportElementError): """ - __slots__ = ('element_hash',) + __slots__ = ("element_hash",) def __init__(self, type: str, element_hash: str, message: str, **_kwargs: Any): # Required - super().__init__('unspecified', type, message) + super().__init__("unspecified", type, message) self.element_hash = element_hash self._id_attrs = (self.source, self.type, self.element_hash, self.message) diff --git a/telegram/passport/passportfile.py b/telegram/passport/passportfile.py index b5f21220044..16c5f59c856 100644 --- a/telegram/passport/passportfile.py +++ b/telegram/passport/passportfile.py @@ -59,13 +59,12 @@ class PassportFile(TelegramObject): """ __slots__ = ( - 'file_date', - 'bot', - 'file_id', - 'file_size', - '_credentials', - 'file_unique_id', - '_id_attrs', + "file_date", + "bot", + "file_id", + "file_size", + "_credentials", + "file_unique_id", ) def __init__( @@ -73,9 +72,9 @@ def __init__( file_id: str, file_unique_id: str, file_date: int, - file_size: int = None, - bot: 'Bot' = None, - credentials: 'FileCredentials' = None, + file_size: int, + bot: "Bot" = None, + credentials: "FileCredentials" = None, **_kwargs: Any, ): # Required @@ -91,8 +90,8 @@ def __init__( @classmethod def de_json_decrypted( - cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'FileCredentials' - ) -> Optional['PassportFile']: + cls, data: Optional[JSONDict], bot: "Bot", credentials: "FileCredentials" + ) -> Optional["PassportFile"]: """Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account passport credentials. @@ -110,14 +109,14 @@ def de_json_decrypted( if not data: return None - data['credentials'] = credentials + data["credentials"] = credentials return cls(bot=bot, **data) @classmethod def de_list_decrypted( - cls, data: Optional[List[JSONDict]], bot: 'Bot', credentials: List['FileCredentials'] - ) -> List[Optional['PassportFile']]: + 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. @@ -140,7 +139,7 @@ def de_list_decrypted( def get_file( self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None - ) -> 'File': + ) -> "File": """ Wrapper over :attr:`telegram.Bot.get_file`. Will automatically assign the correct credentials to the returned :class:`telegram.File` if originating from diff --git a/telegram/payment/invoice.py b/telegram/payment/invoice.py index dea274035b0..72a63da764f 100644 --- a/telegram/payment/invoice.py +++ b/telegram/payment/invoice.py @@ -54,12 +54,11 @@ class Invoice(TelegramObject): """ __slots__ = ( - 'currency', - 'start_parameter', - 'title', - 'description', - 'total_amount', - '_id_attrs', + "currency", + "start_parameter", + "title", + "description", + "total_amount", ) def __init__( diff --git a/telegram/payment/labeledprice.py b/telegram/payment/labeledprice.py index 221c62dbc05..1e11847c392 100644 --- a/telegram/payment/labeledprice.py +++ b/telegram/payment/labeledprice.py @@ -45,7 +45,7 @@ class LabeledPrice(TelegramObject): """ - __slots__ = ('label', '_id_attrs', 'amount') + __slots__ = ("label", "amount") def __init__(self, label: str, amount: int, **_kwargs: Any): self.label = label diff --git a/telegram/payment/orderinfo.py b/telegram/payment/orderinfo.py index 7ebe35851ed..c0d048cc15c 100644 --- a/telegram/payment/orderinfo.py +++ b/telegram/payment/orderinfo.py @@ -49,7 +49,7 @@ class OrderInfo(TelegramObject): """ - __slots__ = ('email', 'shipping_address', 'phone_number', 'name', '_id_attrs') + __slots__ = ("email", "shipping_address", "phone_number", "name") def __init__( self, @@ -67,13 +67,13 @@ def __init__( self._id_attrs = (self.name, self.phone_number, self.email, self.shipping_address) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['OrderInfo']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["OrderInfo"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return cls() - data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot) + data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot) return cls(**data) diff --git a/telegram/payment/precheckoutquery.py b/telegram/payment/precheckoutquery.py index a8f2eb29304..c0ef439ed4c 100644 --- a/telegram/payment/precheckoutquery.py +++ b/telegram/payment/precheckoutquery.py @@ -68,15 +68,14 @@ class PreCheckoutQuery(TelegramObject): """ __slots__ = ( - 'bot', - 'invoice_payload', - 'shipping_option_id', - 'currency', - 'order_info', - 'total_amount', - 'id', - 'from_user', - '_id_attrs', + "bot", + "invoice_payload", + "shipping_option_id", + "currency", + "order_info", + "total_amount", + "id", + "from_user", ) def __init__( @@ -88,7 +87,7 @@ def __init__( invoice_payload: str, shipping_option_id: str = None, order_info: OrderInfo = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): self.id = id # pylint: disable=C0103 @@ -104,15 +103,15 @@ def __init__( self._id_attrs = (self.id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PreCheckoutQuery']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PreCheckoutQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['from_user'] = User.de_json(data.pop('from'), bot) - data['order_info'] = OrderInfo.de_json(data.get('order_info'), bot) + data["from_user"] = User.de_json(data.pop("from"), bot) + data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot) return cls(bot=bot, **data) diff --git a/telegram/payment/shippingaddress.py b/telegram/payment/shippingaddress.py index 2ea5a458ee0..f40ea3e1238 100644 --- a/telegram/payment/shippingaddress.py +++ b/telegram/payment/shippingaddress.py @@ -50,13 +50,12 @@ class ShippingAddress(TelegramObject): """ __slots__ = ( - 'post_code', - 'city', - '_id_attrs', - 'country_code', - 'street_line2', - 'street_line1', - 'state', + "post_code", + "city", + "country_code", + "street_line2", + "street_line1", + "state", ) def __init__( diff --git a/telegram/payment/shippingoption.py b/telegram/payment/shippingoption.py index 6ddbb0bc23d..d02f62233f1 100644 --- a/telegram/payment/shippingoption.py +++ b/telegram/payment/shippingoption.py @@ -46,13 +46,13 @@ class ShippingOption(TelegramObject): """ - __slots__ = ('prices', 'title', 'id', '_id_attrs') + __slots__ = ("prices", "title", "id") def __init__( self, id: str, # pylint: disable=W0622 title: str, - prices: List['LabeledPrice'], + prices: List["LabeledPrice"], **_kwargs: Any, ): self.id = id # pylint: disable=C0103 @@ -65,6 +65,6 @@ 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] + data["prices"] = [p.to_dict() for p in self.prices] return data diff --git a/telegram/payment/shippingquery.py b/telegram/payment/shippingquery.py index bcde858b636..2d933dee372 100644 --- a/telegram/payment/shippingquery.py +++ b/telegram/payment/shippingquery.py @@ -54,7 +54,7 @@ class ShippingQuery(TelegramObject): """ - __slots__ = ('bot', 'invoice_payload', 'shipping_address', 'id', 'from_user', '_id_attrs') + __slots__ = ("bot", "invoice_payload", "shipping_address", "id", "from_user") def __init__( self, @@ -62,7 +62,7 @@ def __init__( from_user: User, invoice_payload: str, shipping_address: ShippingAddress, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): self.id = id # pylint: disable=C0103 @@ -75,15 +75,15 @@ def __init__( self._id_attrs = (self.id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ShippingQuery']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ShippingQuery"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['from_user'] = User.de_json(data.pop('from'), bot) - data['shipping_address'] = ShippingAddress.de_json(data.get('shipping_address'), bot) + data["from_user"] = User.de_json(data.pop("from"), bot) + data["shipping_address"] = ShippingAddress.de_json(data.get("shipping_address"), bot) return cls(bot=bot, **data) diff --git a/telegram/payment/successfulpayment.py b/telegram/payment/successfulpayment.py index 6997ca7354a..52acc0337b3 100644 --- a/telegram/payment/successfulpayment.py +++ b/telegram/payment/successfulpayment.py @@ -63,14 +63,13 @@ class SuccessfulPayment(TelegramObject): """ __slots__ = ( - 'invoice_payload', - 'shipping_option_id', - 'currency', - 'order_info', - 'telegram_payment_charge_id', - 'provider_payment_charge_id', - 'total_amount', - '_id_attrs', + "invoice_payload", + "shipping_option_id", + "currency", + "order_info", + "telegram_payment_charge_id", + "provider_payment_charge_id", + "total_amount", ) def __init__( @@ -95,13 +94,13 @@ def __init__( self._id_attrs = (self.telegram_payment_charge_id, self.provider_payment_charge_id) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SuccessfulPayment']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["SuccessfulPayment"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['order_info'] = OrderInfo.de_json(data.get('order_info'), bot) + data["order_info"] = OrderInfo.de_json(data.get("order_info"), bot) return cls(**data) diff --git a/telegram/poll.py b/telegram/poll.py index 9c28ce57d57..37176e47879 100644 --- a/telegram/poll.py +++ b/telegram/poll.py @@ -48,7 +48,7 @@ class PollOption(TelegramObject): """ - __slots__ = ('voter_count', 'text', '_id_attrs') + __slots__ = ("voter_count", "text") def __init__(self, text: str, voter_count: int, **_kwargs: Any): self.text = text @@ -80,7 +80,7 @@ class PollAnswer(TelegramObject): """ - __slots__ = ('option_ids', 'user', 'poll_id', '_id_attrs') + __slots__ = ("option_ids", "user", "poll_id") def __init__(self, poll_id: str, user: User, option_ids: List[int], **_kwargs: Any): self.poll_id = poll_id @@ -90,14 +90,14 @@ def __init__(self, poll_id: str, user: User, option_ids: List[int], **_kwargs: A self._id_attrs = (self.poll_id, self.user, tuple(self.option_ids)) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PollAnswer']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["PollAnswer"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['user'] = User.de_json(data.get('user'), bot) + data["user"] = User.de_json(data.get("user"), bot) return cls(**data) @@ -151,20 +151,19 @@ class Poll(TelegramObject): """ __slots__ = ( - 'total_voter_count', - 'allows_multiple_answers', - 'open_period', - 'options', - 'type', - 'explanation_entities', - 'is_anonymous', - 'close_date', - 'is_closed', - 'id', - 'explanation', - 'question', - 'correct_option_id', - '_id_attrs', + "total_voter_count", + "allows_multiple_answers", + "open_period", + "options", + "type", + "explanation_entities", + "is_anonymous", + "close_date", + "is_closed", + "id", + "explanation", + "question", + "correct_option_id", ) def __init__( @@ -201,16 +200,16 @@ def __init__( self._id_attrs = (self.id,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Poll']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Poll"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['options'] = [PollOption.de_json(option, bot) for option in data['options']] - data['explanation_entities'] = MessageEntity.de_list(data.get('explanation_entities'), bot) - data['close_date'] = from_timestamp(data.get('close_date')) + data["options"] = [PollOption.de_json(option, bot) for option in data["options"]] + data["explanation_entities"] = MessageEntity.de_list(data.get("explanation_entities"), bot) + data["close_date"] = from_timestamp(data.get("close_date")) return cls(**data) @@ -218,10 +217,10 @@ 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] + data["options"] = [x.to_dict() for x in self.options] if self.explanation_entities: - data['explanation_entities'] = [e.to_dict() for e in self.explanation_entities] - data['close_date'] = to_timestamp(data.get('close_date')) + data["explanation_entities"] = [e.to_dict() for e in self.explanation_entities] + data["close_date"] = to_timestamp(data.get("close_date")) return data @@ -250,10 +249,10 @@ def parse_explanation_entity(self, entity: MessageEntity) -> str: # Is it a narrow build, if so we don't need to convert if sys.maxunicode == 0xFFFF: return self.explanation[entity.offset : entity.offset + entity.length] - entity_text = self.explanation.encode('utf-16-le') + entity_text = self.explanation.encode("utf-16-le") entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] - return entity_text.decode('utf-16-le') + return entity_text.decode("utf-16-le") def parse_explanation_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: """ diff --git a/telegram/proximityalerttriggered.py b/telegram/proximityalerttriggered.py index 507fb779f81..96ade85e46e 100644 --- a/telegram/proximityalerttriggered.py +++ b/telegram/proximityalerttriggered.py @@ -46,7 +46,7 @@ class ProximityAlertTriggered(TelegramObject): """ - __slots__ = ('traveler', 'distance', 'watcher', '_id_attrs') + __slots__ = ("traveler", "distance", "watcher") def __init__(self, traveler: User, watcher: User, distance: int, **_kwargs: Any): self.traveler = traveler @@ -56,14 +56,14 @@ def __init__(self, traveler: User, watcher: User, distance: int, **_kwargs: Any) self._id_attrs = (self.traveler, self.watcher, self.distance) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ProximityAlertTriggered']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["ProximityAlertTriggered"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['traveler'] = User.de_json(data.get('traveler'), bot) - data['watcher'] = User.de_json(data.get('watcher'), bot) + data["traveler"] = User.de_json(data.get("traveler"), bot) + data["watcher"] = User.de_json(data.get("watcher"), bot) return cls(bot=bot, **data) diff --git a/telegram/replykeyboardmarkup.py b/telegram/replykeyboardmarkup.py index 1f365e6aba6..91531f8cf87 100644 --- a/telegram/replykeyboardmarkup.py +++ b/telegram/replykeyboardmarkup.py @@ -76,12 +76,11 @@ class ReplyKeyboardMarkup(ReplyMarkup): """ __slots__ = ( - 'selective', - 'keyboard', - 'resize_keyboard', - 'one_time_keyboard', - 'input_field_placeholder', - '_id_attrs', + "selective", + "keyboard", + "resize_keyboard", + "one_time_keyboard", + "input_field_placeholder", ) def __init__( @@ -116,9 +115,9 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - data['keyboard'] = [] + data["keyboard"] = [] for row in self.keyboard: - data['keyboard'].append([button.to_dict() for button in row]) + data["keyboard"].append([button.to_dict() for button in row]) return data @classmethod @@ -130,7 +129,7 @@ def from_button( selective: bool = False, input_field_placeholder: str = None, **kwargs: object, - ) -> 'ReplyKeyboardMarkup': + ) -> "ReplyKeyboardMarkup": """Shortcut for:: ReplyKeyboardMarkup([[button]], **kwargs) @@ -182,7 +181,7 @@ def from_row( selective: bool = False, input_field_placeholder: str = None, **kwargs: object, - ) -> 'ReplyKeyboardMarkup': + ) -> "ReplyKeyboardMarkup": """Shortcut for:: ReplyKeyboardMarkup([button_row], **kwargs) @@ -235,7 +234,7 @@ def from_column( selective: bool = False, input_field_placeholder: str = None, **kwargs: object, - ) -> 'ReplyKeyboardMarkup': + ) -> "ReplyKeyboardMarkup": """Shortcut for:: ReplyKeyboardMarkup([[button] for button in button_column], **kwargs) diff --git a/telegram/replykeyboardremove.py b/telegram/replykeyboardremove.py index 5f3c255a63e..ca170f4bb15 100644 --- a/telegram/replykeyboardremove.py +++ b/telegram/replykeyboardremove.py @@ -55,7 +55,7 @@ class ReplyKeyboardRemove(ReplyMarkup): """ - __slots__ = ('selective', 'remove_keyboard') + __slots__ = ("selective", "remove_keyboard") def __init__(self, selective: bool = False, **_kwargs: Any): # Required diff --git a/telegram/update.py b/telegram/update.py index 8497ee213a5..712878c2ce9 100644 --- a/telegram/update.py +++ b/telegram/update.py @@ -126,24 +126,23 @@ class Update(TelegramObject): """ __slots__ = ( - 'callback_query', - 'chosen_inline_result', - 'pre_checkout_query', - 'inline_query', - 'update_id', - 'message', - 'shipping_query', - 'poll', - 'poll_answer', - 'channel_post', - 'edited_channel_post', - 'edited_message', - '_effective_user', - '_effective_chat', - '_effective_message', - 'my_chat_member', - 'chat_member', - '_id_attrs', + "callback_query", + "chosen_inline_result", + "pre_checkout_query", + "inline_query", + "update_id", + "message", + "shipping_query", + "poll", + "poll_answer", + "channel_post", + "edited_channel_post", + "edited_message", + "_effective_user", + "_effective_chat", + "_effective_message", + "my_chat_member", + "chat_member", ) MESSAGE = constants.UPDATE_MESSAGE @@ -238,14 +237,14 @@ def __init__( self.my_chat_member = my_chat_member self.chat_member = chat_member - self._effective_user: Optional['User'] = None - self._effective_chat: Optional['Chat'] = None + self._effective_user: Optional["User"] = None + self._effective_chat: Optional["Chat"] = None self._effective_message: Optional[Message] = None self._id_attrs = (self.update_id,) @property - def effective_user(self) -> Optional['User']: + def effective_user(self) -> Optional["User"]: """ :class:`telegram.User`: The user that sent this update, no matter what kind of update this is. Will be :obj:`None` for :attr:`channel_post` and :attr:`poll`. @@ -290,7 +289,7 @@ def effective_user(self) -> Optional['User']: return user @property - def effective_chat(self) -> Optional['Chat']: + def effective_chat(self) -> Optional["Chat"]: """ :class:`telegram.Chat`: The chat that this update was sent in, no matter what kind of update this is. Will be :obj:`None` for :attr:`inline_query`, @@ -362,27 +361,27 @@ def effective_message(self) -> Optional[Message]: return message @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Update']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["Update"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['message'] = Message.de_json(data.get('message'), bot) - data['edited_message'] = Message.de_json(data.get('edited_message'), bot) - data['inline_query'] = InlineQuery.de_json(data.get('inline_query'), bot) - data['chosen_inline_result'] = ChosenInlineResult.de_json( - data.get('chosen_inline_result'), bot + data["message"] = Message.de_json(data.get("message"), bot) + data["edited_message"] = Message.de_json(data.get("edited_message"), bot) + data["inline_query"] = InlineQuery.de_json(data.get("inline_query"), bot) + data["chosen_inline_result"] = ChosenInlineResult.de_json( + data.get("chosen_inline_result"), bot ) - data['callback_query'] = CallbackQuery.de_json(data.get('callback_query'), bot) - data['shipping_query'] = ShippingQuery.de_json(data.get('shipping_query'), bot) - data['pre_checkout_query'] = PreCheckoutQuery.de_json(data.get('pre_checkout_query'), bot) - data['channel_post'] = Message.de_json(data.get('channel_post'), bot) - data['edited_channel_post'] = Message.de_json(data.get('edited_channel_post'), bot) - data['poll'] = Poll.de_json(data.get('poll'), bot) - data['poll_answer'] = PollAnswer.de_json(data.get('poll_answer'), bot) - data['my_chat_member'] = ChatMemberUpdated.de_json(data.get('my_chat_member'), bot) - data['chat_member'] = ChatMemberUpdated.de_json(data.get('chat_member'), bot) + data["callback_query"] = CallbackQuery.de_json(data.get("callback_query"), bot) + data["shipping_query"] = ShippingQuery.de_json(data.get("shipping_query"), bot) + data["pre_checkout_query"] = PreCheckoutQuery.de_json(data.get("pre_checkout_query"), bot) + data["channel_post"] = Message.de_json(data.get("channel_post"), bot) + data["edited_channel_post"] = Message.de_json(data.get("edited_channel_post"), bot) + data["poll"] = Poll.de_json(data.get("poll"), bot) + data["poll_answer"] = PollAnswer.de_json(data.get("poll_answer"), bot) + data["my_chat_member"] = ChatMemberUpdated.de_json(data.get("my_chat_member"), bot) + data["chat_member"] = ChatMemberUpdated.de_json(data.get("chat_member"), bot) return cls(**data) diff --git a/telegram/user.py b/telegram/user.py index 7949e249e2d..e6222e6bb8f 100644 --- a/telegram/user.py +++ b/telegram/user.py @@ -97,17 +97,16 @@ class User(TelegramObject): """ __slots__ = ( - 'is_bot', - 'can_read_all_group_messages', - 'username', - 'first_name', - 'last_name', - 'can_join_groups', - 'supports_inline_queries', - 'id', - 'bot', - 'language_code', - '_id_attrs', + "is_bot", + "can_read_all_group_messages", + "username", + "first_name", + "last_name", + "can_join_groups", + "supports_inline_queries", + "id", + "bot", + "language_code", ) def __init__( @@ -121,7 +120,7 @@ def __init__( can_join_groups: bool = None, can_read_all_group_messages: bool = None, supports_inline_queries: bool = None, - bot: 'Bot' = None, + bot: "Bot" = None, **_kwargs: Any, ): # Required @@ -145,7 +144,7 @@ def name(self) -> str: prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`. """ if self.username: - return f'@{self.username}' + return f"@{self.username}" return self.full_name @property @@ -154,7 +153,7 @@ def full_name(self) -> str: available) :attr:`last_name`. """ if self.last_name: - return f'{self.first_name} {self.last_name}' + return f"{self.first_name} {self.last_name}" return self.first_name @property @@ -172,7 +171,7 @@ def get_profile_photos( limit: int = 100, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> Optional['UserProfilePhotos']: + ) -> Optional["UserProfilePhotos"]: """ Shortcut for:: @@ -316,12 +315,12 @@ def send_message( disable_web_page_preview: ODVInput[bool] = DEFAULT_NONE, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> 'Message': + entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> "Message": """Shortcut for:: bot.send_message(update.effective_user.id, *args, **kwargs) @@ -348,18 +347,18 @@ def send_message( def send_photo( self, - photo: Union[FileInput, 'PhotoSize'], + photo: Union[FileInput, "PhotoSize"], caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_photo(update.effective_user.id, *args, **kwargs) @@ -388,14 +387,14 @@ def send_photo( def send_media_group( self, media: List[ - Union['InputMediaAudio', 'InputMediaDocument', 'InputMediaPhoto', 'InputMediaVideo'] + Union["InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo"] ], disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> List['Message']: + ) -> List["Message"]: """Shortcut for:: bot.send_media_group(update.effective_user.id, *args, **kwargs) @@ -418,22 +417,22 @@ def send_media_group( def send_audio( self, - audio: Union[FileInput, 'Audio'], + audio: Union[FileInput, "Audio"], duration: int = None, performer: str = None, title: str = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_audio(update.effective_user.id, *args, **kwargs) @@ -496,13 +495,13 @@ def send_contact( last_name: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, - contact: 'Contact' = None, + contact: "Contact" = None, vcard: str = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_contact(update.effective_user.id, *args, **kwargs) @@ -532,12 +531,12 @@ def send_dice( self, disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, emoji: str = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_dice(update.effective_user.id, *args, **kwargs) @@ -561,20 +560,20 @@ def send_dice( def send_document( self, - document: Union[FileInput, 'Document'], + document: Union[FileInput, "Document"], filename: str = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, thumb: FileInput = None, api_kwargs: JSONDict = None, disable_content_type_detection: bool = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> 'Message': + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> "Message": """Shortcut for:: bot.send_document(update.effective_user.id, *args, **kwargs) @@ -607,11 +606,11 @@ def send_game( game_short_name: str, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_game(update.effective_user.id, *args, **kwargs) @@ -640,7 +639,7 @@ def send_invoice( payload: str, provider_token: str, currency: str, - prices: List['LabeledPrice'], + prices: List["LabeledPrice"], start_parameter: str = None, photo_url: str = None, photo_size: int = None, @@ -653,7 +652,7 @@ def send_invoice( is_flexible: bool = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'InlineKeyboardMarkup' = None, + reply_markup: "InlineKeyboardMarkup" = None, provider_data: Union[str, object] = None, send_phone_number_to_provider: bool = None, send_email_to_provider: bool = None, @@ -662,7 +661,7 @@ def send_invoice( allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, max_tip_amount: int = None, suggested_tip_amounts: List[int] = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_invoice(update.effective_user.id, *args, **kwargs) @@ -718,16 +717,16 @@ def send_location( longitude: float = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, - location: 'Location' = None, + location: "Location" = None, live_period: int = None, api_kwargs: JSONDict = None, horizontal_accuracy: float = None, heading: int = None, proximity_alert_radius: int = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_location(update.effective_user.id, *args, **kwargs) @@ -757,7 +756,7 @@ def send_location( def send_animation( self, - animation: Union[FileInput, 'Animation'], + animation: Union[FileInput, "Animation"], duration: int = None, width: int = None, height: int = None, @@ -766,13 +765,13 @@ def send_animation( parse_mode: ODVInput[str] = DEFAULT_NONE, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_animation(update.effective_user.id, *args, **kwargs) @@ -804,14 +803,14 @@ def send_animation( def send_sticker( self, - sticker: Union[FileInput, 'Sticker'], + sticker: Union[FileInput, "Sticker"], disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_sticker(update.effective_user.id, *args, **kwargs) @@ -835,12 +834,12 @@ def send_sticker( def send_video( self, - video: Union[FileInput, 'Video'], + video: Union[FileInput, "Video"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, width: int = None, height: int = None, @@ -849,9 +848,9 @@ def send_video( thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_video(update.effective_user.id, *args, **kwargs) @@ -891,15 +890,15 @@ def send_venue( foursquare_id: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, - venue: 'Venue' = None, + venue: "Venue" = None, foursquare_type: str = None, api_kwargs: JSONDict = None, google_place_id: str = None, google_place_type: str = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_venue(update.effective_user.id, *args, **kwargs) @@ -931,18 +930,18 @@ def send_venue( def send_video_note( self, - video_note: Union[FileInput, 'VideoNote'], + video_note: Union[FileInput, "VideoNote"], duration: int = None, length: int = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, thumb: FileInput = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_video_note(update.effective_user.id, *args, **kwargs) @@ -970,19 +969,19 @@ def send_video_note( def send_voice( self, - voice: Union[FileInput, 'Voice'], + voice: Union[FileInput, "Voice"], duration: int = None, caption: str = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: DVInput[float] = DEFAULT_20, parse_mode: ODVInput[str] = DEFAULT_NONE, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - caption_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, + caption_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, filename: str = None, - ) -> 'Message': + ) -> "Message": """Shortcut for:: bot.send_voice(update.effective_user.id, *args, **kwargs) @@ -1021,7 +1020,7 @@ def send_poll( is_closed: bool = None, disable_notification: ODVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, explanation: str = None, explanation_parse_mode: ODVInput[str] = DEFAULT_NONE, @@ -1029,8 +1028,8 @@ def send_poll( close_date: Union[int, datetime] = None, api_kwargs: JSONDict = None, allow_sending_without_reply: ODVInput[bool] = DEFAULT_NONE, - explanation_entities: Union[List['MessageEntity'], Tuple['MessageEntity', ...]] = None, - ) -> 'Message': + explanation_entities: Union[List["MessageEntity"], Tuple["MessageEntity", ...]] = None, + ) -> "Message": """Shortcut for:: bot.send_poll(update.effective_user.id, *args, **kwargs) @@ -1069,14 +1068,14 @@ def send_copy( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: bot.copy_message(chat_id=update.effective_user.id, *args, **kwargs) @@ -1108,14 +1107,14 @@ def copy_message( message_id: int, caption: str = None, parse_mode: ODVInput[str] = DEFAULT_NONE, - caption_entities: Union[Tuple['MessageEntity', ...], List['MessageEntity']] = None, + caption_entities: Union[Tuple["MessageEntity", ...], List["MessageEntity"]] = None, disable_notification: DVInput[bool] = DEFAULT_NONE, reply_to_message_id: int = None, allow_sending_without_reply: DVInput[bool] = DEFAULT_NONE, - reply_markup: 'ReplyMarkup' = None, + reply_markup: "ReplyMarkup" = None, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None, - ) -> 'MessageId': + ) -> "MessageId": """Shortcut for:: bot.copy_message(from_chat_id=update.effective_user.id, *args, **kwargs) diff --git a/telegram/userprofilephotos.py b/telegram/userprofilephotos.py index bd277bf1fb7..edd8abeb49d 100644 --- a/telegram/userprofilephotos.py +++ b/telegram/userprofilephotos.py @@ -44,7 +44,7 @@ class UserProfilePhotos(TelegramObject): """ - __slots__ = ('photos', 'total_count', '_id_attrs') + __slots__ = ("photos", "total_count") def __init__(self, total_count: int, photos: List[List[PhotoSize]], **_kwargs: Any): # Required @@ -54,14 +54,14 @@ def __init__(self, total_count: int, photos: List[List[PhotoSize]], **_kwargs: A self._id_attrs = (self.total_count, self.photos) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['UserProfilePhotos']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["UserProfilePhotos"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['photos'] = [PhotoSize.de_list(photo, bot) for photo in data['photos']] + data["photos"] = [PhotoSize.de_list(photo, bot) for photo in data["photos"]] return cls(**data) @@ -69,9 +69,9 @@ def to_dict(self) -> JSONDict: """See :meth:`telegram.TelegramObject.to_dict`.""" data = super().to_dict() - data['photos'] = [] + data["photos"] = [] for photo in self.photos: - data['photos'].append([x.to_dict() for x in photo]) + data["photos"].append([x.to_dict() for x in photo]) return data diff --git a/telegram/utils/deprecate.py b/telegram/utils/deprecate.py index ebccc6eb922..7945695937b 100644 --- a/telegram/utils/deprecate.py +++ b/telegram/utils/deprecate.py @@ -16,9 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module facilitates the deprecation of functions.""" - -import warnings +"""This module contains a class which is used for deprecation warnings.""" # We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it @@ -28,20 +26,3 @@ class TelegramDeprecationWarning(Warning): """Custom warning class for deprecations in this library.""" __slots__ = () - - -# Function to warn users that setting custom attributes is deprecated (Use only in __setattr__!) -# Checks if a custom attribute is added by checking length of dictionary before & after -# assigning attribute. This is the fastest way to do it (I hope!). -def set_new_attribute_deprecated(self: object, key: str, value: object) -> None: - """Warns the user if they set custom attributes on PTB objects.""" - org = len(self.__dict__) - object.__setattr__(self, key, value) - new = len(self.__dict__) - if new > org: - warnings.warn( - f"Setting custom attributes such as {key!r} on objects such as " - f"{self.__class__.__name__!r} of the PTB library is deprecated.", - TelegramDeprecationWarning, - stacklevel=3, - ) diff --git a/telegram/utils/helpers.py b/telegram/utils/helpers.py index 6705cc90662..411f9e2b79f 100644 --- a/telegram/utils/helpers.py +++ b/telegram/utils/helpers.py @@ -67,7 +67,7 @@ _signames = { v: k for k, v in reversed(sorted(vars(signal).items())) - if k.startswith('SIG') and not k.startswith('SIG_') + if k.startswith("SIG") and not k.startswith("SIG_") } @@ -94,11 +94,11 @@ def is_local_file(obj: Optional[Union[str, Path]]) -> bool: def parse_file_input( - file_input: Union[FileInput, 'TelegramObject'], - tg_type: Type['TelegramObject'] = None, + file_input: Union[FileInput, "TelegramObject"], + tg_type: Type["TelegramObject"] = None, attach: bool = None, filename: str = None, -) -> Union[str, 'InputFile', Any]: +) -> Union[str, "InputFile", Any]: """ Parses input for sending files: @@ -128,7 +128,7 @@ def parse_file_input( # Importing on file-level yields cyclic Import Errors from telegram import InputFile # pylint: disable=C0415 - if isinstance(file_input, str) and file_input.startswith('file://'): + if isinstance(file_input, str) and file_input.startswith("file://"): return file_input if isinstance(file_input, (str, Path)): if is_local_file(file_input): @@ -160,18 +160,18 @@ def escape_markdown(text: str, version: int = 1, entity_type: str = None) -> str ``version=2``, will be ignored else. """ if int(version) == 1: - escape_chars = r'_*`[' + escape_chars = r"_*`[" elif int(version) == 2: - if entity_type in ['pre', 'code']: - escape_chars = r'\`' - elif entity_type == 'text_link': - escape_chars = r'\)' + if entity_type in ["pre", "code"]: + escape_chars = r"\`" + elif entity_type == "text_link": + escape_chars = r"\)" else: - escape_chars = r'_*[]()~`>#+-=|{}.!' + escape_chars = r"_*[]()~`>#+-=|{}.!" else: - raise ValueError('Markdown version must be either 1 or 2!') + raise ValueError("Markdown version must be either 1 or 2!") - return re.sub(f'([{re.escape(escape_chars)}])', r'\\\1', text) + return re.sub(f"([{re.escape(escape_chars)}])", r"\\\1", text) # -------- date/time related helpers -------- @@ -251,7 +251,7 @@ def to_float_timestamp( if reference_timestamp is None: reference_timestamp = time.time() elif isinstance(time_object, dtm.datetime): - raise ValueError('t is an (absolute) datetime while reference_timestamp is not None') + raise ValueError("t is an (absolute) datetime while reference_timestamp is not None") if isinstance(time_object, dtm.timedelta): return reference_timestamp + time_object.total_seconds() @@ -281,7 +281,7 @@ def to_float_timestamp( time_object = _localize(time_object, tzinfo) return _datetime_to_float_timestamp(time_object) - raise TypeError(f'Unable to convert {type(time_object).__name__} object to timestamp') + raise TypeError(f"Unable to convert {type(time_object).__name__} object to timestamp") def to_timestamp( @@ -350,10 +350,10 @@ def mention_markdown(user_id: Union[int, str], name: str, version: int = 1) -> s Returns: :obj:`str`: The inline mention for the user as Markdown. """ - return f'[{escape_markdown(name, version=version)}](tg://user?id={user_id})' + return f"[{escape_markdown(name, version=version)}](tg://user?id={user_id})" -def effective_message_type(entity: Union['Message', 'Update']) -> Optional[str]: +def effective_message_type(entity: Union["Message", "Update"]) -> Optional[str]: """ Extracts the type of message as a string identifier from a :class:`telegram.Message` or a :class:`telegram.Update`. @@ -410,25 +410,25 @@ def create_deep_linked_url(bot_username: str, payload: str = None, group: bool = if bot_username is None or len(bot_username) <= 3: raise ValueError("You must provide a valid bot_username.") - base_url = f'https://t.me/{bot_username}' + base_url = f"https://t.me/{bot_username}" if not payload: return base_url if len(payload) > 64: raise ValueError("The deep-linking payload must not exceed 64 characters.") - if not re.match(r'^[A-Za-z0-9_-]+$', payload): + if not re.match(r"^[A-Za-z0-9_-]+$", payload): raise ValueError( "Only the following characters are allowed for deep-linked " "URLs: A-Z, a-z, 0-9, _ and -" ) if group: - key = 'startgroup' + key = "startgroup" else: - key = 'start' + key = "start" - return f'{base_url}?{key}={payload}' + return f"{base_url}?{key}={payload}" def encode_conversations_to_json(conversations: Dict[str, Dict[Tuple, object]]) -> str: @@ -492,8 +492,8 @@ def decode_user_chat_data_from_json(data: str) -> DefaultDict[int, Dict[object, return tmp -DVType = TypeVar('DVType', bound=object) -OT = TypeVar('OT', bound=object) +DVType = TypeVar("DVType", bound=object) +OT = TypeVar("OT", bound=object) class DefaultValue(Generic[DVType]): @@ -544,7 +544,7 @@ def f(arg=DefaultOne): """ - __slots__ = ('value', '__dict__') + __slots__ = ("value",) def __init__(self, value: DVType = None): self.value = value @@ -554,7 +554,7 @@ def __bool__(self) -> bool: @overload @staticmethod - def get_value(obj: 'DefaultValue[OT]') -> OT: + def get_value(obj: "DefaultValue[OT]") -> OT: ... @overload @@ -563,7 +563,7 @@ def get_value(obj: OT) -> OT: ... @staticmethod - def get_value(obj: Union[OT, 'DefaultValue[OT]']) -> OT: + def get_value(obj: Union[OT, "DefaultValue[OT]"]) -> OT: """ Shortcut for:: @@ -579,7 +579,7 @@ def get_value(obj: Union[OT, 'DefaultValue[OT]']) -> OT: # This is mostly here for readability during debugging def __str__(self) -> str: - return f'DefaultValue({self.value})' + return f"DefaultValue({self.value})" # This is here to have the default instances nicely rendered in the docs def __repr__(self) -> str: diff --git a/telegram/utils/promise.py b/telegram/utils/promise.py deleted file mode 100644 index c25d56d46e3..00000000000 --- a/telegram/utils/promise.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the :class:`telegram.ext.utils.promise.Promise` class for backwards -compatibility. -""" -import warnings - -import telegram.ext.utils.promise as promise -from telegram.utils.deprecate import TelegramDeprecationWarning - -warnings.warn( - 'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.', - TelegramDeprecationWarning, -) - -Promise = promise.Promise -""" -:class:`telegram.ext.utils.promise.Promise` - -.. deprecated:: v13.2 - Use :class:`telegram.ext.utils.promise.Promise` instead. -""" diff --git a/telegram/utils/request.py b/telegram/utils/request.py index 7362be590c9..4c01808f3f8 100644 --- a/telegram/utils/request.py +++ b/telegram/utils/request.py @@ -47,8 +47,8 @@ from urllib3.util.timeout import Timeout # type: ignore[no-redef] warnings.warn( - 'python-telegram-bot is using upstream urllib3. This is allowed but not ' - 'supported by python-telegram-bot maintainers.' + "python-telegram-bot is using upstream urllib3. This is allowed but not " + "supported by python-telegram-bot maintainers." ) except ImportError: warnings.warn( @@ -70,7 +70,6 @@ Unauthorized, ) from telegram.utils.types import JSONDict -from telegram.utils.deprecate import set_new_attribute_deprecated def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: disable=W0613 @@ -79,16 +78,16 @@ def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: d Content-Disposition headers since telegram servers don't understand it. Instead just escape \\ and " and replace any \n and \r with a space. """ - value = value.replace('\\', '\\\\').replace('"', '\\"') - value = value.replace('\r', ' ').replace('\n', ' ') + value = value.replace("\\", "\\\\").replace('"', '\\"') + value = value.replace("\r", " ").replace("\n", " ") return f'{name}="{value}"' RequestField._render_part = _render_part # type: ignore # pylint: disable=W0212 -logging.getLogger('telegram.vendor.ptb_urllib3.urllib3').setLevel(logging.WARNING) +logging.getLogger("telegram.vendor.ptb_urllib3.urllib3").setLevel(logging.WARNING) -USER_AGENT = 'Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)' +USER_AGENT = "Python Telegram Bot (https://github.com/python-telegram-bot/python-telegram-bot)" class Request: @@ -112,7 +111,7 @@ class Request: """ - __slots__ = ('_connect_timeout', '_con_pool_size', '_con_pool', '__dict__') + __slots__ = ("_connect_timeout", "_con_pool_size", "_con_pool") def __init__( self, @@ -132,7 +131,7 @@ def __init__( ] # TODO: Support other platforms like mac and windows. - if 'linux' in sys.platform: + if "linux" in sys.platform: sockopts.append( (socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 120) # pylint: disable=no-member ) @@ -147,7 +146,7 @@ def __init__( kwargs = dict( maxsize=con_pool_size, - cert_reqs='CERT_REQUIRED', + cert_reqs="CERT_REQUIRED", ca_certs=certifi.where(), socket_options=sockopts, timeout=urllib3.Timeout(connect=self._connect_timeout, read=read_timeout, total=None), @@ -160,12 +159,12 @@ def __init__( # * None (if no proxy is configured) if not proxy_url: - proxy_url = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy') + proxy_url = os.environ.get("HTTPS_PROXY") or os.environ.get("https_proxy") self._con_pool: Union[ urllib3.PoolManager, appengine.AppEngineManager, - 'SOCKSProxyManager', # noqa: F821 + "SOCKSProxyManager", # noqa: F821 urllib3.ProxyManager, ] = None # type: ignore if not proxy_url: @@ -176,12 +175,12 @@ def __init__( self._con_pool = urllib3.PoolManager(**kwargs) else: kwargs.update(urllib3_proxy_kwargs) - if proxy_url.startswith('socks'): + if proxy_url.startswith("socks"): try: # pylint: disable=C0415 from telegram.vendor.ptb_urllib3.urllib3.contrib.socks import SOCKSProxyManager except ImportError as exc: - raise RuntimeError('PySocks is missing') from exc + raise RuntimeError("PySocks is missing") from exc self._con_pool = SOCKSProxyManager(proxy_url, **kwargs) else: mgr = urllib3.proxy_from_url(proxy_url, **kwargs) @@ -192,9 +191,6 @@ def __init__( self._con_pool = mgr - def __setattr__(self, key: str, value: object) -> None: - set_new_attribute_deprecated(self, key, value) - @property def con_pool_size(self) -> int: """The size of the connection pool used.""" @@ -212,26 +208,26 @@ def _parse(json_data: bytes) -> Union[JSONDict, bool]: dict: A JSON parsed as Python dict with results - on error this dict will be empty. """ - decoded_s = json_data.decode('utf-8', 'replace') + decoded_s = json_data.decode("utf-8", "replace") try: data = json.loads(decoded_s) except ValueError as exc: - raise TelegramError('Invalid server response') from exc + raise TelegramError("Invalid server response") from exc - if not data.get('ok'): # pragma: no cover - description = data.get('description') - parameters = data.get('parameters') + if not data.get("ok"): # pragma: no cover + description = data.get("description") + parameters = data.get("parameters") if parameters: - migrate_to_chat_id = parameters.get('migrate_to_chat_id') + migrate_to_chat_id = parameters.get("migrate_to_chat_id") if migrate_to_chat_id: raise ChatMigrated(migrate_to_chat_id) - retry_after = parameters.get('retry_after') + retry_after = parameters.get("retry_after") if retry_after: raise RetryAfter(retry_after) if description: return description - return data['result'] + return data["result"] def _request_wrapper(self, *args: object, **kwargs: Any) -> bytes: """Wraps urllib3 request for handling known exceptions. @@ -249,11 +245,11 @@ def _request_wrapper(self, *args: object, **kwargs: Any) -> bytes: """ # Make sure to hint Telegram servers that we reuse connections by sending # "Connection: keep-alive" in the HTTP headers. - if 'headers' not in kwargs: - kwargs['headers'] = {} - kwargs['headers']['connection'] = 'keep-alive' + if "headers" not in kwargs: + kwargs["headers"] = {} + kwargs["headers"]["connection"] = "keep-alive" # Also set our user agent - kwargs['headers']['user-agent'] = USER_AGENT + kwargs["headers"]["user-agent"] = USER_AGENT try: resp = self._con_pool.request(*args, **kwargs) @@ -262,7 +258,7 @@ def _request_wrapper(self, *args: object, **kwargs: Any) -> bytes: except urllib3.exceptions.HTTPError as error: # HTTPError must come last as its the base urllib3 exception class # TODO: do something smart here; for now just raise NetworkError - raise NetworkError(f'urllib3 HTTPError {error}') from error + raise NetworkError(f"urllib3 HTTPError {error}") from error if 200 <= resp.status <= 299: # 200-299 range are HTTP success statuses @@ -271,7 +267,7 @@ def _request_wrapper(self, *args: object, **kwargs: Any) -> bytes: try: message = str(self._parse(resp.data)) except ValueError: - message = 'Unknown HTTPError' + message = "Unknown HTTPError" if resp.status in (401, 403): raise Unauthorized(message) @@ -283,12 +279,12 @@ def _request_wrapper(self, *args: object, **kwargs: Any) -> bytes: raise Conflict(message) if resp.status == 413: raise NetworkError( - 'File too large. Check telegram api limits ' - 'https://core.telegram.org/bots/api#senddocument' + "File too large. Check telegram api limits " + "https://core.telegram.org/bots/api#senddocument" ) if resp.status == 502: - raise NetworkError('Bad Gateway') - raise NetworkError(f'{message} ({resp.status})') + raise NetworkError("Bad Gateway") + raise NetworkError(f"{message} ({resp.status})") def post(self, url: str, data: JSONDict, timeout: float = None) -> Union[JSONDict, bool]: """Request an URL. @@ -307,7 +303,7 @@ def post(self, url: str, data: JSONDict, timeout: float = None) -> Union[JSONDic urlopen_kwargs = {} if timeout is not None: - urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout) + urlopen_kwargs["timeout"] = Timeout(read=timeout, connect=self._connect_timeout) if data is None: data = {} @@ -324,7 +320,7 @@ def post(self, url: str, data: JSONDict, timeout: float = None) -> Union[JSONDic elif isinstance(val, (float, int)): # Urllib3 doesn't like floats it seems data[key] = str(val) - elif key == 'media': + elif key == "media": files = True # List of media if isinstance(val, list): @@ -356,13 +352,13 @@ def post(self, url: str, data: JSONDict, timeout: float = None) -> Union[JSONDic # Use multipart upload if we're uploading files, otherwise use JSON if files: - result = self._request_wrapper('POST', url, fields=data, **urlopen_kwargs) + result = self._request_wrapper("POST", url, fields=data, **urlopen_kwargs) else: result = self._request_wrapper( - 'POST', + "POST", url, - body=json.dumps(data).encode('utf-8'), - headers={'Content-Type': 'application/json'}, + body=json.dumps(data).encode("utf-8"), + headers={"Content-Type": "application/json"}, **urlopen_kwargs, ) @@ -380,9 +376,9 @@ def retrieve(self, url: str, timeout: float = None) -> bytes: """ urlopen_kwargs = {} if timeout is not None: - urlopen_kwargs['timeout'] = Timeout(read=timeout, connect=self._connect_timeout) + urlopen_kwargs["timeout"] = Timeout(read=timeout, connect=self._connect_timeout) - return self._request_wrapper('GET', url, **urlopen_kwargs) + return self._request_wrapper("GET", url, **urlopen_kwargs) def download(self, url: str, filename: str, timeout: float = None) -> None: """Download a file by its URL. @@ -396,5 +392,5 @@ def download(self, url: str, filename: str, timeout: float = None) -> None: """ buf = self.retrieve(url, timeout=timeout) - with open(filename, 'wb') as fobj: + with open(filename, "wb") as fobj: fobj.write(buf) diff --git a/telegram/utils/types.py b/telegram/utils/types.py index 2f9ff8f20e9..128c73127d2 100644 --- a/telegram/utils/types.py +++ b/telegram/utils/types.py @@ -34,7 +34,7 @@ from telegram import InputFile # noqa: F401 from telegram.utils.helpers import DefaultValue # noqa: F401 -FileLike = Union[IO, 'InputFile'] +FileLike = Union[IO, "InputFile"] """Either an open file handler or a :class:`telegram.InputFile`.""" FileInput = Union[str, bytes, FileLike, Path] @@ -44,11 +44,11 @@ JSONDict = Dict[str, Any] """Dictionary containing response from Telegram or data to send to the API.""" -DVType = TypeVar('DVType') -ODVInput = Optional[Union['DefaultValue[DVType]', DVType]] +DVType = TypeVar("DVType") +ODVInput = Optional[Union["DefaultValue[DVType]", DVType]] """Generic type for bot method parameters which can have defaults. ``ODVInput[type]`` is the same as ``Optional[Union[DefaultValue, type]]``.""" -DVInput = Union['DefaultValue[DVType]', DVType] +DVInput = Union["DefaultValue[DVType]", DVType] """Generic type for bot method parameters which can have defaults. ``DVInput[type]`` is the same as ``Union[DefaultValue, type]``.""" diff --git a/telegram/utils/webhookhandler.py b/telegram/utils/webhookhandler.py deleted file mode 100644 index 727eecbc7b2..00000000000 --- a/telegram/utils/webhookhandler.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -"""This module contains the :class:`telegram.ext.utils.webhookhandler.WebhookHandler` class for -backwards compatibility. -""" -import warnings - -import telegram.ext.utils.webhookhandler as webhook_handler -from telegram.utils.deprecate import TelegramDeprecationWarning - -warnings.warn( - 'telegram.utils.webhookhandler is deprecated. Please use telegram.ext.utils.webhookhandler ' - 'instead.', - TelegramDeprecationWarning, -) - -WebhookHandler = webhook_handler.WebhookHandler -WebhookServer = webhook_handler.WebhookServer -WebhookAppClass = webhook_handler.WebhookAppClass diff --git a/telegram/version.py b/telegram/version.py index 653ace5dcc3..16dd1e0fd92 100644 --- a/telegram/version.py +++ b/telegram/version.py @@ -20,5 +20,5 @@ from telegram import constants -__version__ = '13.7' +__version__ = "13.7" bot_api_version = constants.BOT_API_VERSION # pylint: disable=C0103 diff --git a/telegram/voicechat.py b/telegram/voicechat.py index 4fb7b539891..fa35e31e3c3 100644 --- a/telegram/voicechat.py +++ b/telegram/voicechat.py @@ -20,7 +20,7 @@ """This module contains objects related to Telegram voice chats.""" import datetime as dtm -from typing import TYPE_CHECKING, Any, Optional, List +from typing import TYPE_CHECKING, Optional, List from telegram import TelegramObject, User from telegram.utils.helpers import from_timestamp, to_timestamp @@ -40,7 +40,7 @@ class VoiceChatStarted(TelegramObject): __slots__ = () - def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049 + def __init__(self, **_kwargs: object): # skipcq: PTC-W0049 pass @@ -64,9 +64,9 @@ class VoiceChatEnded(TelegramObject): """ - __slots__ = ('duration', '_id_attrs') + __slots__ = ("duration",) - def __init__(self, duration: int, **_kwargs: Any) -> None: + def __init__(self, duration: int, **_kwargs: object) -> None: self.duration = int(duration) if duration is not None else None self._id_attrs = (self.duration,) @@ -83,45 +83,46 @@ class VoiceChatParticipantsInvited(TelegramObject): .. versionadded:: 13.4 Args: - users (List[:class:`telegram.User`]): New members that + users (List[:class:`telegram.User`], optional): New members that were invited to the voice chat. **kwargs (:obj:`dict`): Arbitrary keyword arguments. Attributes: - users (List[:class:`telegram.User`]): New members that + users (List[:class:`telegram.User`]): Optional. New members that were invited to the voice chat. """ - __slots__ = ('users', '_id_attrs') + __slots__ = ("users",) - def __init__(self, users: List[User], **_kwargs: Any) -> None: + def __init__(self, users: List[User] = None, **_kwargs: object) -> None: self.users = users self._id_attrs = (self.users,) - def __hash__(self) -> int: - return hash(tuple(self.users)) - @classmethod def de_json( - cls, data: Optional[JSONDict], bot: 'Bot' - ) -> Optional['VoiceChatParticipantsInvited']: + cls, data: Optional[JSONDict], bot: "Bot" + ) -> Optional["VoiceChatParticipantsInvited"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['users'] = User.de_list(data.get('users', []), bot) + data["users"] = User.de_list(data.get("users", []), bot) 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] + if self.users is not None: + data["users"] = [u.to_dict() for u in self.users] return data + def __hash__(self) -> int: + return hash(None) if self.users is None else hash(tuple(self.users)) + class VoiceChatScheduled(TelegramObject): """This object represents a service message about a voice chat scheduled in the chat. @@ -140,22 +141,22 @@ class VoiceChatScheduled(TelegramObject): """ - __slots__ = ('start_date', '_id_attrs') + __slots__ = ("start_date",) - def __init__(self, start_date: dtm.datetime, **_kwargs: Any) -> None: + def __init__(self, start_date: dtm.datetime, **_kwargs: object) -> None: self.start_date = start_date self._id_attrs = (self.start_date,) @classmethod - def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VoiceChatScheduled']: + def de_json(cls, data: Optional[JSONDict], bot: "Bot") -> Optional["VoiceChatScheduled"]: """See :meth:`telegram.TelegramObject.de_json`.""" data = cls._parse_data(data) if not data: return None - data['start_date'] = from_timestamp(data['start_date']) + data["start_date"] = from_timestamp(data["start_date"]) return cls(**data, bot=bot) @@ -164,6 +165,6 @@ def to_dict(self) -> JSONDict: data = super().to_dict() # Required - data['start_date'] = to_timestamp(self.start_date) + data["start_date"] = to_timestamp(self.start_date) return data diff --git a/telegram/webhookinfo.py b/telegram/webhookinfo.py index 0fc6741e498..5da38be1cc1 100644 --- a/telegram/webhookinfo.py +++ b/telegram/webhookinfo.py @@ -63,15 +63,14 @@ class WebhookInfo(TelegramObject): """ __slots__ = ( - 'allowed_updates', - 'url', - 'max_connections', - 'last_error_date', - 'ip_address', - 'last_error_message', - 'pending_update_count', - 'has_custom_certificate', - '_id_attrs', + "allowed_updates", + "url", + "max_connections", + "last_error_date", + "ip_address", + "last_error_message", + "pending_update_count", + "has_custom_certificate", ) def __init__( diff --git a/tests/bots.py b/tests/bots.py index 7d5c4d3820f..2b1f594b2ff 100644 --- a/tests/bots.py +++ b/tests/bots.py @@ -29,25 +29,25 @@ # These bots are only able to talk in our test chats, so they are quite useless for other # purposes than testing. FALLBACKS = ( - 'W3sidG9rZW4iOiAiNTc5Njk0NzE0OkFBRnBLOHc2emtrVXJENHhTZVl3RjNNTzhlLTRHcm1jeTdjIiwgInBheW1lbnRfc' - 'HJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpRME5qWmxOekk1WWpKaSIsICJjaGF0X2lkIjogIjY3NTY2Nj' - 'IyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTMxMDkxMTEzNSIsICJjaGFubmVsX2lkIjogIkBweXRob250ZWxlZ3J' - 'hbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAxIiwgImJvdF91c2VybmFtZSI6ICJAcHRi' - 'X2ZhbGxiYWNrXzFfYm90In0sIHsidG9rZW4iOiAiNTU4MTk0MDY2OkFBRndEUElGbHpHVWxDYVdIdFRPRVg0UkZyWDh1O' - 'URNcWZvIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WWpFd09EUXdNVEZtTkRjeSIsIC' - 'JjaGF0X2lkIjogIjY3NTY2NjIyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTIyMTIxNjgzMCIsICJjaGFubmVsX2l' - 'kIjogIkBweXRob250ZWxlZ3JhbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAyIiwgImJv' - 'dF91c2VybmFtZSI6ICJAcHRiX2ZhbGxiYWNrXzJfYm90In1d' + "W3sidG9rZW4iOiAiNTc5Njk0NzE0OkFBRnBLOHc2emtrVXJENHhTZVl3RjNNTzhlLTRHcm1jeTdjIiwgInBheW1lbnRfc" + "HJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TmpRME5qWmxOekk1WWpKaSIsICJjaGF0X2lkIjogIjY3NTY2Nj" + "IyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTMxMDkxMTEzNSIsICJjaGFubmVsX2lkIjogIkBweXRob250ZWxlZ3J" + "hbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAxIiwgImJvdF91c2VybmFtZSI6ICJAcHRi" + "X2ZhbGxiYWNrXzFfYm90In0sIHsidG9rZW4iOiAiNTU4MTk0MDY2OkFBRndEUElGbHpHVWxDYVdIdFRPRVg0UkZyWDh1O" + "URNcWZvIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WWpFd09EUXdNVEZtTkRjeSIsIC" + "JjaGF0X2lkIjogIjY3NTY2NjIyNCIsICJzdXBlcl9ncm91cF9pZCI6ICItMTAwMTIyMTIxNjgzMCIsICJjaGFubmVsX2l" + "kIjogIkBweXRob250ZWxlZ3JhbWJvdHRlc3RzIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBmYWxsYmFjayAyIiwgImJv" + "dF91c2VybmFtZSI6ICJAcHRiX2ZhbGxiYWNrXzJfYm90In1d" ) -GITHUB_ACTION = os.getenv('GITHUB_ACTION', None) -BOTS = os.getenv('BOTS', None) -JOB_INDEX = os.getenv('JOB_INDEX', None) +GITHUB_ACTION = os.getenv("GITHUB_ACTION", None) +BOTS = os.getenv("BOTS", None) +JOB_INDEX = os.getenv("JOB_INDEX", None) if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None: - BOTS = json.loads(base64.b64decode(BOTS).decode('utf-8')) + BOTS = json.loads(base64.b64decode(BOTS).decode("utf-8")) JOB_INDEX = int(JOB_INDEX) -FALLBACKS = json.loads(base64.b64decode(FALLBACKS).decode('utf-8')) +FALLBACKS = json.loads(base64.b64decode(FALLBACKS).decode("utf-8")) def get(name, fallback): @@ -80,9 +80,9 @@ def patient_request_wrapper(*args, **kwargs): try: return original_request_wrapper(*args, **kwargs) except RetryAfter as e: - pytest.xfail(f'Not waiting for flood control: {e}') + pytest.xfail(f"Not waiting for flood control: {e}") except TimedOut as e: - pytest.xfail(f'Ignoring TimedOut error: {e}') + pytest.xfail(f"Ignoring TimedOut error: {e}") Request._request_wrapper = patient_request_wrapper diff --git a/tests/conftest.py b/tests/conftest.py index 6eae0a71fc8..b1d47198b20 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -44,6 +44,7 @@ ChosenInlineResult, File, ChatPermissions, + Bot, ) from telegram.ext import ( Dispatcher, @@ -56,20 +57,21 @@ ) from telegram.error import BadRequest from telegram.utils.helpers import DefaultValue, DEFAULT_NONE +from telegram.utils.request import Request from tests.bots import get_bot # This is here instead of in setup.cfg due to https://github.com/pytest-dev/pytest/issues/8343 def pytest_runtestloop(session): session.add_marker( - pytest.mark.filterwarnings('ignore::telegram.utils.deprecate.TelegramDeprecationWarning') + pytest.mark.filterwarnings("ignore::telegram.utils.deprecate.TelegramDeprecationWarning") ) -GITHUB_ACTION = os.getenv('GITHUB_ACTION', False) +GITHUB_ACTION = os.getenv("GITHUB_ACTION", False) if GITHUB_ACTION: - pytest_plugins = ['tests.plugin_github_group'] + pytest_plugins = ["tests.plugin_github_group"] # THIS KEY IS OBVIOUSLY COMPROMISED # DO NOT USE IN PRODUCTION! @@ -81,75 +83,83 @@ def env_var_2_bool(env_var: object) -> bool: return env_var if not isinstance(env_var, str): return False - return env_var.lower().strip() == 'true' + return env_var.lower().strip() == "true" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def bot_info(): return get_bot() -@pytest.fixture(scope='session') -def bot(bot_info): - class DictExtBot( - ExtBot - ): # Subclass Bot to allow monkey patching of attributes and functions, would - pass # come into effect when we __dict__ is dropped from slots +# Below Dict* classes are used to monkeypatch attributes since parent classes don't have __dict__ +class DictRequest(Request): + pass + + +class DictExtBot(ExtBot): + pass - return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY) + +class DictBot(Bot): + pass + + +@pytest.fixture(scope="session") +def bot(bot_info): + return DictExtBot(bot_info["token"], private_key=PRIVATE_KEY, request=DictRequest()) DEFAULT_BOTS = {} -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def default_bot(request, bot_info): - param = request.param if hasattr(request, 'param') else {} + param = request.param if hasattr(request, "param") else {} defaults = Defaults(**param) default_bot = DEFAULT_BOTS.get(defaults) if default_bot: return default_bot - default_bot = make_bot(bot_info, **{'defaults': defaults}) + default_bot = make_bot(bot_info, **{"defaults": defaults}) DEFAULT_BOTS[defaults] = default_bot return default_bot -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def tz_bot(timezone, bot_info): defaults = Defaults(tzinfo=timezone) default_bot = DEFAULT_BOTS.get(defaults) if default_bot: return default_bot - default_bot = make_bot(bot_info, **{'defaults': defaults}) + default_bot = make_bot(bot_info, **{"defaults": defaults}) DEFAULT_BOTS[defaults] = default_bot return default_bot -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def chat_id(bot_info): - return bot_info['chat_id'] + return bot_info["chat_id"] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def super_group_id(bot_info): - return bot_info['super_group_id'] + return bot_info["super_group_id"] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def channel_id(bot_info): - return bot_info['channel_id'] + return bot_info["channel_id"] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def provider_token(bot_info): - return bot_info['payment_provider_token'] + return bot_info["payment_provider_token"] def create_dp(bot): # Dispatcher is heavy to init (due to many threads and such) so we have a single session # scoped one here, but before each test, reset it (dp fixture below) - dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(), workers=2, use_context=False) + dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(), workers=2) dispatcher.job_queue.set_dispatcher(dispatcher) thr = Thread(target=dispatcher.start) thr.start() @@ -161,12 +171,12 @@ def create_dp(bot): thr.join() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def _dp(bot): yield from create_dp(bot) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def dp(_dp): # Reset the dispatcher first while not _dp.update_queue.empty(): @@ -180,49 +190,41 @@ def dp(_dp): _dp.error_handlers = {} # For some reason if we setattr with the name mangled, then some tests(like async) run forever, # due to threads not acquiring, (blocking). This adds these attributes to the __dict__. - object.__setattr__(_dp, '__stop_event', Event()) - object.__setattr__(_dp, '__exception_event', Event()) - object.__setattr__(_dp, '__async_queue', Queue()) - object.__setattr__(_dp, '__async_threads', set()) + object.__setattr__(_dp, "__stop_event", Event()) + object.__setattr__(_dp, "__exception_event", Event()) + object.__setattr__(_dp, "__async_queue", Queue()) + object.__setattr__(_dp, "__async_threads", set()) _dp.persistence = None - _dp.use_context = False if _dp._Dispatcher__singleton_semaphore.acquire(blocking=0): Dispatcher._set_singleton(_dp) yield _dp Dispatcher._Dispatcher__singleton_semaphore.release() -@pytest.fixture(scope='function') -def cdp(dp): - dp.use_context = True - yield dp - dp.use_context = False - - -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def updater(bot): - up = Updater(bot=bot, workers=2, use_context=False) + up = Updater(bot=bot, workers=2) yield up if up.running: up.stop() -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def thumb_file(): - f = open('tests/data/thumb.jpg', 'rb') + f = open("tests/data/thumb.jpg", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def class_thumb_file(): - f = open('tests/data/thumb.jpg', 'rb') + f = open("tests/data/thumb.jpg", "rb") yield f f.close() def pytest_configure(config): - config.addinivalue_line('filterwarnings', 'ignore::ResourceWarning') + config.addinivalue_line("filterwarnings", "ignore::ResourceWarning") # TODO: Write so good code that we don't need to ignore ResourceWarnings anymore @@ -230,10 +232,10 @@ def make_bot(bot_info, **kwargs): """ Tests are executed on tg.ext.ExtBot, as that class only extends the functionality of tg.bot """ - return ExtBot(bot_info['token'], private_key=PRIVATE_KEY, **kwargs) + return ExtBot(bot_info["token"], private_key=PRIVATE_KEY, request=DictRequest(), **kwargs) -CMD_PATTERN = re.compile(r'/[\da-z_]{1,32}(?:@\w{1,32})?') +CMD_PATTERN = re.compile(r"/[\da-z_]{1,32}(?:@\w{1,32})?") DATE = datetime.datetime.now() @@ -246,11 +248,11 @@ def make_message(text, **kwargs): """ return Message( message_id=1, - from_user=kwargs.pop('user', User(id=1, first_name='', is_bot=False)), - date=kwargs.pop('date', DATE), - chat=kwargs.pop('chat', Chat(id=1, type='')), + from_user=kwargs.pop("user", User(id=1, first_name="", is_bot=False)), + date=kwargs.pop("date", DATE), + chat=kwargs.pop("chat", Chat(id=1, type="")), text=text, - bot=kwargs.pop('bot', make_bot(get_bot())), + bot=kwargs.pop("bot", make_bot(get_bot())), **kwargs, ) @@ -293,7 +295,7 @@ def make_message_update(message, message_factory=make_message, edited=False, **k """ if not isinstance(message, Message): message = message_factory(message, **kwargs) - update_kwargs = {'message' if not edited else 'edited_message': message} + update_kwargs = {"message" if not edited else "edited_message": message} return Update(0, **update_kwargs) @@ -309,12 +311,12 @@ def make_command_update(message, edited=False, **kwargs): @pytest.fixture( - scope='class', - params=[{'class': MessageFilter}, {'class': UpdateFilter}], - ids=['MessageFilter', 'UpdateFilter'], + scope="class", + params=[{"class": MessageFilter}, {"class": UpdateFilter}], + ids=["MessageFilter", "UpdateFilter"], ) def mock_filter(request): - class MockFilter(request.param['class']): + class MockFilter(request.param["class"]): def __init__(self): self.tested = False @@ -325,27 +327,27 @@ def filter(self, _): def get_false_update_fixture_decorator_params(): - message = Message(1, DATE, Chat(1, ''), from_user=User(1, '', False), text='test') + message = Message(1, DATE, Chat(1, ""), from_user=User(1, "", False), text="test") params = [ - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = tuple(key for kwargs in params for key in kwargs) - return {'params': params, 'ids': ids} + return {"params": params, "ids": ids} -@pytest.fixture(scope='function', **get_false_update_fixture_decorator_params()) +@pytest.fixture(scope="function", **get_false_update_fixture_decorator_params()) def false_update(request): return Update(update_id=1, **request.param) -@pytest.fixture(params=['Europe/Berlin', 'Asia/Singapore', 'UTC']) +@pytest.fixture(params=["Europe/Berlin", "Asia/Singapore", "UTC"]) def tzinfo(request): return pytz.timezone(request.param) @@ -361,9 +363,9 @@ def _mro_slots(_class): return [ attr for cls in _class.__class__.__mro__[:-1] - if hasattr(cls, '__slots__') # ABC doesn't have slots in py 3.7 and below + if hasattr(cls, "__slots__") # The Exception class doesn't have slots for attr in cls.__slots__ - if attr != '__dict__' + if attr != "__dict__" # left here for classes which still has __dict__ ] return _mro_slots @@ -387,7 +389,7 @@ def expect_bad_request(func, message, reason): return func() except BadRequest as e: if message in str(e): - pytest.xfail(f'{reason}. {e}') + pytest.xfail(f"{reason}. {e}") else: raise e @@ -413,15 +415,15 @@ def check_shortcut_signature( """ shortcut_sig = inspect.signature(shortcut) effective_shortcut_args = set(shortcut_sig.parameters.keys()).difference(additional_kwargs) - effective_shortcut_args.discard('self') + effective_shortcut_args.discard("self") bot_sig = inspect.signature(bot_method) expected_args = set(bot_sig.parameters.keys()).difference(shortcut_kwargs) - expected_args.discard('self') + expected_args.discard("self") args_check = expected_args == effective_shortcut_args if not args_check: - raise Exception(f'Expected arguments {expected_args}, got {effective_shortcut_args}') + raise Exception(f"Expected arguments {expected_args}, got {effective_shortcut_args}") # TODO: Also check annotation of return type. Would currently be a hassle b/c typing doesn't # resolve `ForwardRef('Type')` to `Type`. For now we rely on MyPy, which probably allows the @@ -434,13 +436,13 @@ def check_shortcut_signature( shortcut_sig.parameters[kwarg].annotation ): raise Exception( - f'For argument {kwarg} I expected {bot_sig.parameters[kwarg].annotation}, ' - f'but got {shortcut_sig.parameters[kwarg].annotation}' + f"For argument {kwarg} I expected {bot_sig.parameters[kwarg].annotation}, " + f"but got {shortcut_sig.parameters[kwarg].annotation}" ) else: raise Exception( - f'For argument {kwarg} I expected {bot_sig.parameters[kwarg].annotation}, but ' - f'got {shortcut_sig.parameters[kwarg].annotation}' + f"For argument {kwarg} I expected {bot_sig.parameters[kwarg].annotation}, but " + f"got {shortcut_sig.parameters[kwarg].annotation}" ) bot_method_sig = inspect.signature(bot_method) @@ -448,7 +450,7 @@ def check_shortcut_signature( for arg in expected_args: if not shortcut_sig.parameters[arg].default == bot_method_sig.parameters[arg].default: raise Exception( - f'Default for argument {arg} does not match the default of the Bot method.' + f"Default for argument {arg} does not match the default of the Bot method." ) return True @@ -483,7 +485,7 @@ def check_shortcut_call( orig_bot_method = getattr(bot, bot_method_name) bot_signature = inspect.signature(orig_bot_method) - expected_args = set(bot_signature.parameters.keys()) - {'self'} - set(skip_params) + expected_args = set(bot_signature.parameters.keys()) - {"self"} - set(skip_params) positional_args = { name for name, param in bot_signature.parameters.items() if param.default == param.empty } @@ -491,7 +493,7 @@ def check_shortcut_call( shortcut_signature = inspect.signature(shortcut_method) # auto_pagination: Special casing for InlineQuery.answer - kwargs = {name: name for name in shortcut_signature.parameters if name != 'auto_pagination'} + kwargs = {name: name for name in shortcut_signature.parameters if name != "auto_pagination"} def make_assertion(**kw): # name == value makes sure that @@ -502,14 +504,14 @@ def make_assertion(**kw): } if not received_kwargs == expected_args: raise Exception( - f'{orig_bot_method.__name__} did not receive correct value for the parameters ' - f'{expected_args - received_kwargs}' + f"{orig_bot_method.__name__} did not receive correct value for the parameters " + f"{expected_args - received_kwargs}" ) - if bot_method_name == 'get_file': + if bot_method_name == "get_file": # This is here mainly for PassportFile.get_file, which calls .set_credentials on the # return value - return File(file_id='result', file_unique_id='result') + return File(file_id="result", file_unique_id="result") return True setattr(bot, bot_method_name, make_assertion) @@ -545,13 +547,13 @@ def build_kwargs(signature: inspect.Signature, default_kwargs, dfv: Any = DEFAUL # For required params we need to pass something if param.default == param.empty: # Some special casing - if name == 'permissions': + if name == "permissions": kws[name] = ChatPermissions() - elif name in ['prices', 'media', 'results', 'commands', 'errors']: + elif name in ["prices", "media", "results", "commands", "errors"]: kws[name] = [] - elif name == 'ok': - kws['ok'] = False - kws['error_message'] = 'error' + elif name == "ok": + kws["ok"] = False + kws["error_message"] = "error" else: kws[name] = True # pass values for params that can have defaults only if we don't want to use the @@ -560,7 +562,7 @@ def build_kwargs(signature: inspect.Signature, default_kwargs, dfv: Any = DEFAUL if dfv != DEFAULT_NONE: kws[name] = dfv # Some special casing for methods that have "exactly one of the optionals" type args - elif name in ['location', 'contact', 'venue', 'inline_message_id']: + elif name in ["location", "contact", "venue", "inline_message_id"]: kws[name] = True return kws @@ -571,16 +573,16 @@ def build_kwargs(signature: inspect.Signature, default_kwargs, dfv: Any = DEFAUL if isinstance(value.default, DefaultValue) ] # shortcut_signature.parameters['timeout'] is of type DefaultValue - method_timeout = shortcut_signature.parameters['timeout'].default.value + method_timeout = shortcut_signature.parameters["timeout"].default.value default_kwarg_names = kwargs_need_default # special case explanation_parse_mode of Bot.send_poll: - if 'explanation_parse_mode' in default_kwarg_names: - default_kwarg_names.remove('explanation_parse_mode') + if "explanation_parse_mode" in default_kwarg_names: + default_kwarg_names.remove("explanation_parse_mode") defaults_no_custom_defaults = Defaults() defaults_custom_defaults = Defaults( - **{kwarg: 'custom_default' for kwarg in default_kwarg_names} + **{kwarg: "custom_default" for kwarg in default_kwarg_names} ) expected_return_values = [None, []] if return_value is None else [return_value] @@ -590,22 +592,22 @@ def make_assertion(_, data, timeout=DEFAULT_NONE, df_value=DEFAULT_NONE): if timeout != expected_timeout: pytest.fail(f'Got value {timeout} for "timeout", expected {expected_timeout}') - for arg in (dkw for dkw in kwargs_need_default if dkw != 'timeout'): + for arg in (dkw for dkw in kwargs_need_default if dkw != "timeout"): # 'None' should not be passed along to Telegram if df_value in [None, DEFAULT_NONE]: if arg in data: pytest.fail( - f'Got value {data[arg]} for argument {arg}, expected it to be absent' + f"Got value {data[arg]} for argument {arg}, expected it to be absent" ) else: - value = data.get(arg, '`not passed at all`') + value = data.get(arg, "`not passed at all`") if value != df_value: - pytest.fail(f'Got value {value} for argument {arg} instead of {df_value}') + pytest.fail(f"Got value {value} for argument {arg} instead of {df_value}") - if method.__name__ in ['get_file', 'get_small_file', 'get_big_file']: + if method.__name__ in ["get_file", "get_small_file", "get_big_file"]: # This is here mainly for PassportFile.get_file, which calls .set_credentials on the # return value - out = File(file_id='result', file_unique_id='result') + out = File(file_id="result", file_unique_id="result") nonlocal expected_return_values expected_return_values = [out] return out.to_dict() @@ -619,7 +621,7 @@ def make_assertion(_, data, timeout=DEFAULT_NONE, df_value=DEFAULT_NONE): try: for default_value, defaults in [ (DEFAULT_NONE, defaults_no_custom_defaults), - ('custom_default', defaults_custom_defaults), + ("custom_default", defaults_custom_defaults), ]: bot.defaults = defaults # 1: test that we get the correct default value, if we don't specify anything @@ -628,13 +630,13 @@ def make_assertion(_, data, timeout=DEFAULT_NONE, df_value=DEFAULT_NONE): kwargs_need_default, ) assertion_callback = functools.partial(make_assertion, df_value=default_value) - setattr(bot.request, 'post', assertion_callback) + setattr(bot.request, "post", assertion_callback) assert method(**kwargs) in expected_return_values # 2: test that we get the manually passed non-None value - kwargs = build_kwargs(shortcut_signature, kwargs_need_default, dfv='non-None-value') - assertion_callback = functools.partial(make_assertion, df_value='non-None-value') - setattr(bot.request, 'post', assertion_callback) + kwargs = build_kwargs(shortcut_signature, kwargs_need_default, dfv="non-None-value") + assertion_callback = functools.partial(make_assertion, df_value="non-None-value") + setattr(bot.request, "post", assertion_callback) assert method(**kwargs) in expected_return_values # 3: test that we get the manually passed None value @@ -644,12 +646,12 @@ def make_assertion(_, data, timeout=DEFAULT_NONE, df_value=DEFAULT_NONE): dfv=None, ) assertion_callback = functools.partial(make_assertion, df_value=None) - setattr(bot.request, 'post', assertion_callback) + setattr(bot.request, "post", assertion_callback) assert method(**kwargs) in expected_return_values except Exception as exc: raise exc finally: - setattr(bot.request, 'post', orig_post) + setattr(bot.request, "post", orig_post) bot.defaults = None return True diff --git a/tests/plugin_github_group.py b/tests/plugin_github_group.py index cb0430987df..bffc83b57e9 100644 --- a/tests/plugin_github_group.py +++ b/tests/plugin_github_group.py @@ -19,16 +19,16 @@ import _pytest.config import pytest -fold_plugins = {'_cov': 'Coverage report', 'flaky': 'Flaky report'} +fold_plugins = {"_cov": "Coverage report", "flaky": "Flaky report"} def terminal_summary_wrapper(original, plugin_name): text = fold_plugins[plugin_name] def pytest_terminal_summary(terminalreporter): - terminalreporter.write(f'##[group] {text}\n') + terminalreporter.write(f"##[group] {text}\n") original(terminalreporter) - terminalreporter.write('##[endgroup]') + terminalreporter.write("##[endgroup]") return pytest_terminal_summary @@ -45,14 +45,14 @@ def pytest_configure(config): def _get_name(location): - if location[0].startswith('tests/'): + if location[0].startswith("tests/"): return location[0][6:] return location[0] @pytest.mark.trylast def pytest_itemcollected(item): - item._nodeid = item._nodeid.split('::', 1)[1] + item._nodeid = item._nodeid.split("::", 1)[1] @pytest.hookimpl(hookwrapper=True, tryfirst=True) @@ -68,9 +68,9 @@ def pytest_runtest_protocol(item, nextitem): if previous_name is None or previous_name != name: previous_name = name - terminal.write(f'\n##[group] {name}') + terminal.write(f"\n##[group] {name}") yield if nextitem is None or _get_name(nextitem.location) != name: - terminal.write('\n##[endgroup]') + terminal.write("\n##[endgroup]") diff --git a/tests/test_animation.py b/tests/test_animation.py index b90baeafbb1..ef15b0fc657 100644 --- a/tests/test_animation.py +++ b/tests/test_animation.py @@ -28,49 +28,46 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def animation_file(): - f = open('tests/data/game.gif', 'rb') + f = open("tests/data/game.gif", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def animation(bot, chat_id): - with open('tests/data/game.gif', 'rb') as f: + with open("tests/data/game.gif", "rb") as f: return bot.send_animation( - chat_id, animation=f, timeout=50, thumb=open('tests/data/thumb.jpg', 'rb') + chat_id, animation=f, timeout=50, thumb=open("tests/data/thumb.jpg", "rb") ).animation class TestAnimation: - animation_file_id = 'CgADAQADngIAAuyVeEez0xRovKi9VAI' - animation_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI" + animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" width = 320 height = 180 duration = 1 # animation_file_url = 'https://python-telegram-bot.org/static/testfiles/game.gif' # Shortened link, the above one is cached with the wrong duration. - animation_file_url = 'http://bit.ly/2L18jua' - file_name = 'game.gif.mp4' - mime_type = 'video/mp4' + animation_file_url = "http://bit.ly/2L18jua" + file_name = "game.gif.mp4" + mime_type = "video/mp4" file_size = 4127 caption = "Test *animation*" - def test_slot_behaviour(self, animation, recwarn, mro_slots): + def test_slot_behaviour(self, animation, mro_slots): for attr in animation.__slots__: - assert getattr(animation, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not animation.__dict__, f"got missing slot(s): {animation.__dict__}" + assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(animation)) == len(set(mro_slots(animation))), "duplicate slot" - animation.custom, animation.file_name = 'should give warning', self.file_name - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, animation): assert isinstance(animation, Animation) assert isinstance(animation.file_id, str) assert isinstance(animation.file_unique_id, str) - assert animation.file_id != '' - assert animation.file_unique_id != '' + assert animation.file_id != "" + assert animation.file_unique_id != "" def test_expected_values(self, animation): assert animation.file_size == self.file_size @@ -87,7 +84,7 @@ def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file width=self.width, height=self.height, caption=self.caption, - parse_mode='Markdown', + parse_mode="Markdown", disable_notification=False, thumb=thumb_file, ) @@ -95,8 +92,8 @@ def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file assert isinstance(message.animation, Animation) assert isinstance(message.animation.file_id, str) assert isinstance(message.animation.file_unique_id, str) - assert message.animation.file_id != '' - assert message.animation.file_unique_id != '' + assert message.animation.file_id != "" + assert message.animation.file_unique_id != "" assert message.animation.file_name == animation.file_name assert message.animation.mime_type == animation.mime_type assert message.animation.file_size == animation.file_size @@ -106,12 +103,12 @@ def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file @flaky(3, 1) def test_send_animation_custom_filename(self, bot, chat_id, animation_file, monkeypatch): def make_assertion(url, data, **kwargs): - return data['animation'].filename == 'custom_filename' + return data["animation"].filename == "custom_filename" - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) - assert bot.send_animation(chat_id, animation_file, filename='custom_filename') - monkeypatch.delattr(bot.request, 'post') + assert bot.send_animation(chat_id, animation_file, filename="custom_filename") + monkeypatch.delattr(bot.request, "post") @flaky(3, 1) def test_get_and_download(self, bot, animation): @@ -119,11 +116,11 @@ def test_get_and_download(self, bot, animation): assert new_file.file_size == self.file_size assert new_file.file_id == animation.file_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('game.gif') + new_file.download("game.gif") - assert os.path.isfile('game.gif') + assert os.path.isfile("game.gif") @flaky(3, 1) def test_send_animation_url_file(self, bot, chat_id, animation): @@ -136,8 +133,8 @@ def test_send_animation_url_file(self, bot, chat_id, animation): assert isinstance(message.animation, Animation) assert isinstance(message.animation.file_id, str) assert isinstance(message.animation.file_unique_id, str) - assert message.animation.file_id != '' - assert message.animation.file_unique_id != '' + assert message.animation.file_id != "" + assert message.animation.file_unique_id != "" assert message.animation.duration == animation.duration assert message.animation.file_name == animation.file_name @@ -146,7 +143,7 @@ def test_send_animation_url_file(self, bot, chat_id, animation): @flaky(3, 1) def test_send_animation_caption_entities(self, bot, chat_id, animation): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -160,19 +157,19 @@ def test_send_animation_caption_entities(self, bot, chat_id, animation): assert message.caption_entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_animation_default_parse_mode_1(self, default_bot, chat_id, animation_file): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_animation(chat_id, animation_file, caption=test_markdown_string) assert message.caption_markdown == test_markdown_string assert message.caption == test_string @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_animation_default_parse_mode_2(self, default_bot, chat_id, animation_file): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_animation( chat_id, animation_file, caption=test_markdown_string, parse_mode=None @@ -181,12 +178,12 @@ def test_send_animation_default_parse_mode_2(self, default_bot, chat_id, animati assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_animation_default_parse_mode_3(self, default_bot, chat_id, animation_file): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_animation( - chat_id, animation_file, caption=test_markdown_string, parse_mode='HTML' + chat_id, animation_file, caption=test_markdown_string, parse_mode="HTML" ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @@ -194,32 +191,32 @@ def test_send_animation_default_parse_mode_3(self, default_bot, chat_id, animati def test_send_animation_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('animation') == expected and data.get('thumb') == expected + test_flag = data.get("animation") == expected and data.get("thumb") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_animation(chat_id, file, thumb=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_animation_default_allow_sending_without_reply( self, default_bot, chat_id, animation, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_animation( @@ -235,7 +232,7 @@ def test_send_animation_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_animation( chat_id, animation, reply_to_message_id=reply_to_message.message_id ) @@ -248,23 +245,23 @@ def test_resend(self, bot, chat_id, animation): def test_send_with_animation(self, monkeypatch, bot, chat_id, animation): def test(url, data, **kwargs): - return data['animation'] == animation.file_id + return data["animation"] == animation.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_animation(animation=animation, chat_id=chat_id) assert message def test_de_json(self, bot, animation): json_dict = { - 'file_id': self.animation_file_id, - 'file_unique_id': self.animation_file_unique_id, - 'width': self.width, - 'height': self.height, - 'duration': self.duration, - 'thumb': animation.thumb.to_dict(), - 'file_name': self.file_name, - 'mime_type': self.mime_type, - 'file_size': self.file_size, + "file_id": self.animation_file_id, + "file_unique_id": self.animation_file_unique_id, + "width": self.width, + "height": self.height, + "duration": self.duration, + "thumb": animation.thumb.to_dict(), + "file_name": self.file_name, + "mime_type": self.mime_type, + "file_size": self.file_size, } animation = Animation.de_json(json_dict, bot) assert animation.file_id == self.animation_file_id @@ -277,19 +274,19 @@ def test_to_dict(self, animation): animation_dict = animation.to_dict() assert isinstance(animation_dict, dict) - assert animation_dict['file_id'] == animation.file_id - assert animation_dict['file_unique_id'] == animation.file_unique_id - assert animation_dict['width'] == animation.width - assert animation_dict['height'] == animation.height - assert animation_dict['duration'] == animation.duration - assert animation_dict['thumb'] == animation.thumb.to_dict() - assert animation_dict['file_name'] == animation.file_name - assert animation_dict['mime_type'] == animation.mime_type - assert animation_dict['file_size'] == animation.file_size + assert animation_dict["file_id"] == animation.file_id + assert animation_dict["file_unique_id"] == animation.file_unique_id + assert animation_dict["width"] == animation.width + assert animation_dict["height"] == animation.height + assert animation_dict["duration"] == animation.duration + assert animation_dict["thumb"] == animation.thumb.to_dict() + assert animation_dict["file_name"] == animation.file_name + assert animation_dict["mime_type"] == animation.mime_type + assert animation_dict["file_size"] == animation.file_size @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): - animation_file = open(os.devnull, 'rb') + animation_file = open(os.devnull, "rb") with pytest.raises(TelegramError): bot.send_animation(chat_id=chat_id, animation=animation_file) @@ -297,7 +294,7 @@ def test_error_send_empty_file(self, bot, chat_id): @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_animation(chat_id=chat_id, animation='') + bot.send_animation(chat_id=chat_id, animation="") def test_error_send_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -305,13 +302,13 @@ def test_error_send_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, animation): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == animation.file_id + return kwargs["file_id"] == animation.file_id - assert check_shortcut_signature(Animation.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(animation.get_file, animation.bot, 'get_file') + assert check_shortcut_signature(Animation.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(animation.get_file, animation.bot, "get_file") assert check_defaults_handling(animation.get_file, animation.bot) - monkeypatch.setattr(animation.bot, 'get_file', make_assertion) + monkeypatch.setattr(animation.bot, "get_file", make_assertion) assert animation.get_file() def test_equality(self): @@ -322,8 +319,8 @@ def test_equality(self): self.width, self.duration, ) - b = Animation('', self.animation_file_unique_id, self.height, self.width, self.duration) - d = Animation('', '', 0, 0, 0) + b = Animation("", self.animation_file_unique_id, self.height, self.width, self.duration) + d = Animation("", "", 0, 0, 0) e = Voice(self.animation_file_id, self.animation_file_unique_id, 0) assert a == b diff --git a/tests/test_audio.py b/tests/test_audio.py index 924c7220f63..424e9d60e66 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -27,53 +27,50 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def audio_file(): - f = open('tests/data/telegram.mp3', 'rb') + f = open("tests/data/telegram.mp3", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def audio(bot, chat_id): - with open('tests/data/telegram.mp3', 'rb') as f: + with open("tests/data/telegram.mp3", "rb") as f: return bot.send_audio( - chat_id, audio=f, timeout=50, thumb=open('tests/data/thumb.jpg', 'rb') + chat_id, audio=f, timeout=50, thumb=open("tests/data/thumb.jpg", "rb") ).audio class TestAudio: - caption = 'Test *audio*' - performer = 'Leandro Toledo' - title = 'Teste' - file_name = 'telegram.mp3' + caption = "Test *audio*" + performer = "Leandro Toledo" + title = "Teste" + file_name = "telegram.mp3" duration = 3 # audio_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.mp3' # Shortened link, the above one is cached with the wrong duration. - audio_file_url = 'https://goo.gl/3En24v' - mime_type = 'audio/mpeg' + audio_file_url = "https://goo.gl/3En24v" + mime_type = "audio/mpeg" file_size = 122920 thumb_file_size = 1427 thumb_width = 50 thumb_height = 50 - audio_file_id = '5a3128a4d2a04750b5b58397f3b5e812' - audio_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + audio_file_id = "5a3128a4d2a04750b5b58397f3b5e812" + audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" - def test_slot_behaviour(self, audio, recwarn, mro_slots): + def test_slot_behaviour(self, audio, mro_slots): for attr in audio.__slots__: - assert getattr(audio, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not audio.__dict__, f"got missing slot(s): {audio.__dict__}" + assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(audio)) == len(set(mro_slots(audio))), "duplicate slot" - audio.custom, audio.file_name = 'should give warning', self.file_name - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, audio): # Make sure file has been uploaded. assert isinstance(audio, Audio) assert isinstance(audio.file_id, str) assert isinstance(audio.file_unique_id, str) - assert audio.file_id != '' - assert audio.file_unique_id != '' + assert audio.file_id != "" + assert audio.file_unique_id != "" def test_expected_values(self, audio): assert audio.duration == self.duration @@ -95,11 +92,11 @@ def test_send_all_args(self, bot, chat_id, audio_file, thumb_file): performer=self.performer, title=self.title, disable_notification=False, - parse_mode='Markdown', + parse_mode="Markdown", thumb=thumb_file, ) - assert message.caption == self.caption.replace('*', '') + assert message.caption == self.caption.replace("*", "") assert isinstance(message.audio, Audio) assert isinstance(message.audio.file_id, str) @@ -119,11 +116,11 @@ def test_send_all_args(self, bot, chat_id, audio_file, thumb_file): @flaky(3, 1) def test_send_audio_custom_filename(self, bot, chat_id, audio_file, monkeypatch): def make_assertion(url, data, **kwargs): - return data['audio'].filename == 'custom_filename' + return data["audio"].filename == "custom_filename" - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) - assert bot.send_audio(chat_id, audio_file, filename='custom_filename') + assert bot.send_audio(chat_id, audio_file, filename="custom_filename") @flaky(3, 1) def test_get_and_download(self, bot, audio): @@ -132,11 +129,11 @@ def test_get_and_download(self, bot, audio): assert new_file.file_size == self.file_size assert new_file.file_id == audio.file_id assert new_file.file_unique_id == audio.file_unique_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.mp3') + new_file.download("telegram.mp3") - assert os.path.isfile('telegram.mp3') + assert os.path.isfile("telegram.mp3") @flaky(3, 1) def test_send_mp3_url_file(self, bot, chat_id, audio): @@ -161,15 +158,15 @@ def test_resend(self, bot, chat_id, audio): def test_send_with_audio(self, monkeypatch, bot, chat_id, audio): def test(url, data, **kwargs): - return data['audio'] == audio.file_id + return data["audio"] == audio.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_audio(audio=audio, chat_id=chat_id) assert message @flaky(3, 1) def test_send_audio_caption_entities(self, bot, chat_id, audio): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -181,19 +178,19 @@ def test_send_audio_caption_entities(self, bot, chat_id, audio): assert message.caption_entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_audio_default_parse_mode_1(self, default_bot, chat_id, audio_file, thumb_file): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string) assert message.caption_markdown == test_markdown_string assert message.caption == test_string @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file, thumb_file): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_audio( chat_id, audio_file, caption=test_markdown_string, parse_mode=None @@ -202,12 +199,12 @@ def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file, assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file, thumb_file): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_audio( - chat_id, audio_file, caption=test_markdown_string, parse_mode='HTML' + chat_id, audio_file, caption=test_markdown_string, parse_mode="HTML" ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @@ -215,30 +212,30 @@ def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file, def test_send_audio_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('audio') == expected and data.get('thumb') == expected + test_flag = data.get("audio") == expected and data.get("thumb") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_audio(chat_id, file, thumb=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") def test_de_json(self, bot, audio): json_dict = { - 'file_id': self.audio_file_id, - 'file_unique_id': self.audio_file_unique_id, - 'duration': self.duration, - 'performer': self.performer, - 'title': self.title, - 'file_name': self.file_name, - 'caption': self.caption, - 'mime_type': self.mime_type, - 'file_size': self.file_size, - 'thumb': audio.thumb.to_dict(), + "file_id": self.audio_file_id, + "file_unique_id": self.audio_file_unique_id, + "duration": self.duration, + "performer": self.performer, + "title": self.title, + "file_name": self.file_name, + "caption": self.caption, + "mime_type": self.mime_type, + "file_size": self.file_size, + "thumb": audio.thumb.to_dict(), } json_audio = Audio.de_json(json_dict, bot) @@ -256,16 +253,16 @@ def test_to_dict(self, audio): audio_dict = audio.to_dict() assert isinstance(audio_dict, dict) - assert audio_dict['file_id'] == audio.file_id - assert audio_dict['file_unique_id'] == audio.file_unique_id - assert audio_dict['duration'] == audio.duration - assert audio_dict['mime_type'] == audio.mime_type - assert audio_dict['file_size'] == audio.file_size - assert audio_dict['file_name'] == audio.file_name + assert audio_dict["file_id"] == audio.file_id + assert audio_dict["file_unique_id"] == audio.file_unique_id + assert audio_dict["duration"] == audio.duration + assert audio_dict["mime_type"] == audio.mime_type + assert audio_dict["file_size"] == audio.file_size + assert audio_dict["file_name"] == audio.file_name @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): - audio_file = open(os.devnull, 'rb') + audio_file = open(os.devnull, "rb") with pytest.raises(TelegramError): bot.send_audio(chat_id=chat_id, audio=audio_file) @@ -273,7 +270,7 @@ def test_error_send_empty_file(self, bot, chat_id): @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_audio(chat_id=chat_id, audio='') + bot.send_audio(chat_id=chat_id, audio="") def test_error_send_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -281,20 +278,20 @@ def test_error_send_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, audio): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == audio.file_id + return kwargs["file_id"] == audio.file_id - assert check_shortcut_signature(Audio.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(audio.get_file, audio.bot, 'get_file') + assert check_shortcut_signature(Audio.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(audio.get_file, audio.bot, "get_file") assert check_defaults_handling(audio.get_file, audio.bot) - monkeypatch.setattr(audio.bot, 'get_file', make_assertion) + monkeypatch.setattr(audio.bot, "get_file", make_assertion) assert audio.get_file() def test_equality(self, audio): a = Audio(audio.file_id, audio.file_unique_id, audio.duration) - b = Audio('', audio.file_unique_id, audio.duration) + b = Audio("", audio.file_unique_id, audio.duration) c = Audio(audio.file_id, audio.file_unique_id, 0) - d = Audio('', '', audio.duration) + d = Audio("", "", audio.duration) e = Voice(audio.file_id, audio.file_unique_id, audio.duration) assert a == b diff --git a/tests/test_bot.py b/tests/test_bot.py index b869f9f6fd2..dcbe5de4621 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -76,27 +76,27 @@ class BotSubClass(Bot): pass -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def message(bot, chat_id): to_reply_to = bot.send_message( - chat_id, 'Text', disable_web_page_preview=True, disable_notification=True + chat_id, "Text", disable_web_page_preview=True, disable_notification=True ) return bot.send_message( chat_id, - 'Text', + "Text", reply_to_message_id=to_reply_to.message_id, disable_web_page_preview=True, disable_notification=True, ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def media_message(bot, chat_id): - with open('tests/data/telegram.ogg', 'rb') as f: - return bot.send_voice(chat_id, voice=f, caption='my caption', timeout=10) + with open("tests/data/telegram.ogg", "rb") as f: + return bot.send_voice(chat_id, voice=f, caption="my caption", timeout=10) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chat_permissions(): return ChatPermissions(can_send_messages=False, can_change_info=False, can_invite_users=False) @@ -112,7 +112,7 @@ def inline_results_callback(page=None): return None -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_results(): return inline_results_callback() @@ -121,14 +121,14 @@ def inline_results(): xfail = pytest.mark.xfail( bool(GITHUB_ACTION), # This condition is only relevant for github actions game tests. - reason='Can fail due to race conditions when multiple test suites ' - 'with the same bot token are run at the same time', + reason="Can fail due to race conditions when multiple test suites " + "with the same bot token are run at the same time", ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def inst(request, bot_info, default_bot): - return Bot(bot_info['token']) if request.param == 'bot' else default_bot + return Bot(bot_info["token"]) if request.param == "bot" else default_bot class TestBot: @@ -136,40 +136,30 @@ class TestBot: Most are executed on tg.ext.ExtBot, as that class only extends the functionality of tg.bot """ - @pytest.mark.parametrize('inst', ['bot', "default_bot"], indirect=True) - def test_slot_behaviour(self, inst, recwarn, mro_slots): + @pytest.mark.parametrize("inst", ["bot", "default_bot"], indirect=True) + def test_slot_behaviour(self, inst, mro_slots): for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slots: {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.base_url = 'should give warning', inst.base_url - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - - class CustomBot(Bot): - pass # Tests that setting custom attributes of Bot subclass doesn't raise warning - - a = CustomBot(inst.token) - a.my_custom = 'no error!' - assert len(recwarn) == 1 @pytest.mark.parametrize( - 'token', + "token", argvalues=[ - '123', - '12a:abcd1234', - '12:abcd1234', - '1234:abcd1234\n', - ' 1234:abcd1234', - ' 1234:abcd1234\r', - '1234:abcd 1234', + "123", + "12a:abcd1234", + "12:abcd1234", + "1234:abcd1234\n", + " 1234:abcd1234", + " 1234:abcd1234\r", + "1234:abcd 1234", ], ) def test_invalid_token(self, token): - with pytest.raises(InvalidToken, match='Invalid token'): + with pytest.raises(InvalidToken, match="Invalid token"): Bot(token) @pytest.mark.parametrize( - 'acd_in,maxsize,acd', + "acd_in,maxsize,acd", [(True, 1024, True), (False, 1024, False), (0, 0, True), (None, None, True)], ) def test_callback_data_maxsize(self, bot, acd_in, maxsize, acd): @@ -179,23 +169,22 @@ def test_callback_data_maxsize(self, bot, acd_in, maxsize, acd): @flaky(3, 1) def test_invalid_token_server_response(self, monkeypatch): - monkeypatch.setattr('telegram.Bot._validate_token', lambda x, y: True) - bot = Bot('12') + monkeypatch.setattr("telegram.Bot._validate_token", lambda x, y: True) + bot = Bot("12") with pytest.raises(InvalidToken): bot.get_me() def test_unknown_kwargs(self, bot, monkeypatch): def post(url, data, timeout): - assert data['unknown_kwarg_1'] == 7 - assert data['unknown_kwarg_2'] == 5 + assert data["unknown_kwarg_1"] == 7 + assert data["unknown_kwarg_2"] == 5 - monkeypatch.setattr(bot.request, 'post', post) - bot.send_message(123, 'text', api_kwargs={'unknown_kwarg_1': 7, 'unknown_kwarg_2': 5}) + monkeypatch.setattr(bot.request, "post", post) + bot.send_message(123, "text", api_kwargs={"unknown_kwarg_1": 7, "unknown_kwarg_2": 5}) @flaky(3, 1) def test_get_me_and_properties(self, bot): get_me_bot = bot.get_me() - commands = bot.get_my_commands() assert isinstance(get_me_bot, User) assert get_me_bot.id == bot.id @@ -206,10 +195,7 @@ def test_get_me_and_properties(self, bot): assert get_me_bot.can_join_groups == bot.can_join_groups assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages assert get_me_bot.supports_inline_queries == bot.supports_inline_queries - assert f'https://t.me/{get_me_bot.username}' == bot.link - assert commands == bot.commands - bot._commands = None - assert commands == bot.commands + assert f"https://t.me/{get_me_bot.username}" == bot.link def test_equality(self): a = Bot(FALLBACKS[0]["token"]) @@ -239,20 +225,20 @@ def test_to_dict(self, bot): assert to_dict_bot["last_name"] == bot.last_name @pytest.mark.parametrize( - 'bot_method_name', + "bot_method_name", argvalues=[ name for name, _ in inspect.getmembers(Bot, predicate=inspect.isfunction) - if not name.startswith('_') + if not name.startswith("_") and name not in [ - 'de_json', - 'de_list', - 'to_dict', - 'to_json', - 'parse_data', - 'get_updates', - 'getUpdates', + "de_json", + "de_list", + "to_dict", + "to_json", + "parse_data", + "get_updates", + "getUpdates", ] ], ) @@ -279,8 +265,8 @@ def test_ext_bot_signature(self): """ # Some methods of ext.ExtBot global_extra_args = set() - extra_args_per_method = defaultdict(set, {'__init__': {'arbitrary_callback_data'}}) - different_hints_per_method = defaultdict(set, {'__setattr__': {'ext_bot'}}) + extra_args_per_method = defaultdict(set, {"__init__": {"arbitrary_callback_data"}}) + different_hints_per_method = defaultdict(set, {"__setattr__": {"ext_bot"}}) for name, method in inspect.getmembers(Bot, predicate=inspect.isfunction): signature = inspect.signature(method) @@ -288,23 +274,23 @@ def test_ext_bot_signature(self): assert ( ext_signature.return_annotation == signature.return_annotation - ), f'Wrong return annotation for method {name}' + ), f"Wrong return annotation for method {name}" assert ( set(signature.parameters) == set(ext_signature.parameters) - global_extra_args - extra_args_per_method[name] - ), f'Wrong set of parameters for method {name}' + ), f"Wrong set of parameters for method {name}" for param_name, param in signature.parameters.items(): if param_name in different_hints_per_method[name]: continue assert ( param.annotation == ext_signature.parameters[param_name].annotation - ), f'Wrong annotation for parameter {param_name} of method {name}' + ), f"Wrong annotation for parameter {param_name} of method {name}" assert ( param.default == ext_signature.parameters[param_name].default - ), f'Wrong default value for parameter {param_name} of method {name}' + ), f"Wrong default value for parameter {param_name} of method {name}" assert ( param.kind == ext_signature.parameters[param_name].kind - ), f'Wrong parameter kind for parameter {param_name} of method {name}' + ), f"Wrong parameter kind for parameter {param_name} of method {name}" @flaky(3, 1) def test_forward_message(self, bot, chat_id, message): @@ -318,7 +304,7 @@ def test_forward_message(self, bot, chat_id, message): @flaky(3, 1) def test_delete_message(self, bot, chat_id): - message = bot.send_message(chat_id, text='will be deleted') + message = bot.send_message(chat_id, text="will be deleted") time.sleep(2) assert bot.delete_message(chat_id=chat_id, message_id=message.message_id) is True @@ -337,12 +323,12 @@ def test_delete_message_old_message(self, bot, chat_id): def test_send_venue(self, bot, chat_id): longitude = -46.788279 latitude = -23.691288 - title = 'title' - address = 'address' - foursquare_id = 'foursquare id' - foursquare_type = 'foursquare type' - google_place_id = 'google_place id' - google_place_type = 'google_place type' + title = "title" + address = "address" + foursquare_id = "foursquare id" + foursquare_type = "foursquare type" + google_place_id = "google_place id" + google_place_type = "google_place type" message = bot.send_venue( chat_id=chat_id, @@ -387,12 +373,12 @@ def test_send_venue(self, bot, chat_id): @flaky(3, 1) @pytest.mark.xfail(raises=RetryAfter) @pytest.mark.skipif( - python_implementation() == 'PyPy', reason='Unstable on pypy for some reason' + python_implementation() == "PyPy", reason="Unstable on pypy for some reason" ) def test_send_contact(self, bot, chat_id): - phone_number = '+11234567890' - first_name = 'Leandro' - last_name = 'Toledo' + phone_number = "+11234567890" + first_name = "Leandro" + last_name = "Toledo" message = bot.send_contact( chat_id=chat_id, phone_number=phone_number, first_name=first_name, last_name=last_name ) @@ -406,20 +392,20 @@ def test_send_contact(self, bot, chat_id): @flaky(3, 1) @pytest.mark.parametrize( - 'reply_markup', + "reply_markup", [ None, InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='data') + InlineKeyboardButton(text="text", callback_data="data") ), InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='data') + InlineKeyboardButton(text="text", callback_data="data") ).to_dict(), ], ) def test_send_and_stop_poll(self, bot, super_group_id, reply_markup): - question = 'Is this a test?' - answers = ['Yes', 'No', 'Maybe'] + question = "Is this a test?" + answers = ["Yes", "No", "Maybe"] message = bot.send_poll( chat_id=super_group_id, question=question, @@ -458,9 +444,9 @@ def test_send_and_stop_poll(self, bot, super_group_id, reply_markup): assert poll.question == question assert poll.total_voter_count == 0 - explanation = '[Here is a link](https://google.com)' + explanation = "[Here is a link](https://google.com)" explanation_entities = [ - MessageEntity(MessageEntity.TEXT_LINK, 0, 14, url='https://google.com') + MessageEntity(MessageEntity.TEXT_LINK, 0, 14, url="https://google.com") ] message_quiz = bot.send_poll( chat_id=super_group_id, @@ -475,16 +461,16 @@ def test_send_and_stop_poll(self, bot, super_group_id, reply_markup): assert message_quiz.poll.correct_option_id == 2 assert message_quiz.poll.type == Poll.QUIZ assert message_quiz.poll.is_closed - assert message_quiz.poll.explanation == 'Here is a link' + assert message_quiz.poll.explanation == "Here is a link" assert message_quiz.poll.explanation_entities == explanation_entities @flaky(3, 1) - @pytest.mark.parametrize(['open_period', 'close_date'], [(5, None), (None, True)]) + @pytest.mark.parametrize(["open_period", "close_date"], [(5, None), (None, True)]) def test_send_open_period(self, bot, super_group_id, open_period, close_date): - question = 'Is this a test?' - answers = ['Yes', 'No', 'Maybe'] + question = "Is this a test?" + answers = ["Yes", "No", "Maybe"] reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='data') + InlineKeyboardButton(text="text", callback_data="data") ) if close_date: @@ -512,10 +498,10 @@ def test_send_open_period(self, bot, super_group_id, open_period, close_date): @flaky(5, 1) def test_send_close_date_default_tz(self, tz_bot, super_group_id): - question = 'Is this a test?' - answers = ['Yes', 'No', 'Maybe'] + question = "Is this a test?" + answers = ["Yes", "No", "Maybe"] reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='data') + InlineKeyboardButton(text="text", callback_data="data") ) aware_close_date = dtm.datetime.now(tz=tz_bot.defaults.tzinfo) + dtm.timedelta(seconds=5) @@ -543,7 +529,7 @@ def test_send_close_date_default_tz(self, tz_bot, super_group_id): @flaky(3, 1) def test_send_poll_explanation_entities(self, bot, chat_id): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -551,8 +537,8 @@ def test_send_poll_explanation_entities(self, bot, chat_id): ] message = bot.send_poll( chat_id, - 'question', - options=['a', 'b'], + "question", + options=["a", "b"], correct_option_id=0, type=Poll.QUIZ, explanation=test_string, @@ -563,12 +549,12 @@ def test_send_poll_explanation_entities(self, bot, chat_id): assert message.poll.explanation_entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_poll_default_parse_mode(self, default_bot, super_group_id): - explanation = 'Italic Bold Code' - explanation_markdown = '_Italic_ *Bold* `Code`' - question = 'Is this a test?' - answers = ['Yes', 'No', 'Maybe'] + explanation = "Italic Bold Code" + explanation_markdown = "_Italic_ *Bold* `Code`" + question = "Is this a test?" + answers = ["Yes", "No", "Maybe"] message = default_bot.send_poll( chat_id=super_group_id, @@ -607,25 +593,25 @@ def test_send_poll_default_parse_mode(self, default_bot, super_group_id): correct_option_id=2, is_closed=True, explanation=explanation_markdown, - explanation_parse_mode='HTML', + explanation_parse_mode="HTML", ) assert message.poll.explanation == explanation_markdown assert message.poll.explanation_entities == [] @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_poll_default_allow_sending_without_reply(self, default_bot, chat_id, custom): - question = 'Is this a test?' - answers = ['Yes', 'No', 'Maybe'] - reply_to_message = default_bot.send_message(chat_id, 'test') + question = "Is this a test?" + answers = ["Yes", "No", "Maybe"] + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_poll( @@ -645,7 +631,7 @@ def test_send_poll_default_allow_sending_without_reply(self, default_bot, chat_i ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_poll( chat_id, question=question, @@ -654,7 +640,7 @@ def test_send_poll_default_allow_sending_without_reply(self, default_bot, chat_i ) @flaky(3, 1) - @pytest.mark.parametrize('emoji', Dice.ALL_EMOJI + [None]) + @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI + [None]) def test_send_dice(self, bot, chat_id, emoji): message = bot.send_dice(chat_id, emoji=emoji) @@ -666,16 +652,16 @@ def test_send_dice(self, bot, chat_id, emoji): @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_dice_default_allow_sending_without_reply(self, default_bot, chat_id, custom): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_dice( @@ -691,20 +677,18 @@ def test_send_dice_default_allow_sending_without_reply(self, default_bot, chat_i ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_dice(chat_id, reply_to_message_id=reply_to_message.message_id) @flaky(3, 1) @pytest.mark.parametrize( - 'chat_action', + "chat_action", [ ChatAction.FIND_LOCATION, - ChatAction.RECORD_AUDIO, ChatAction.RECORD_VIDEO, ChatAction.RECORD_VIDEO_NOTE, ChatAction.RECORD_VOICE, ChatAction.TYPING, - ChatAction.UPLOAD_AUDIO, ChatAction.UPLOAD_DOCUMENT, ChatAction.UPLOAD_PHOTO, ChatAction.UPLOAD_VIDEO, @@ -714,40 +698,40 @@ def test_send_dice_default_allow_sending_without_reply(self, default_bot, chat_i ) def test_send_chat_action(self, bot, chat_id, chat_action): assert bot.send_chat_action(chat_id, chat_action) - with pytest.raises(BadRequest, match='Wrong parameter action'): - bot.send_chat_action(chat_id, 'unknown action') + with pytest.raises(BadRequest, match="Wrong parameter action"): + bot.send_chat_action(chat_id, "unknown action") # TODO: Needs improvement. We need incoming inline query to test answer. def test_answer_inline_query(self, monkeypatch, bot): # For now just test that our internals pass the correct data def test(url, data, *args, **kwargs): return data == { - 'cache_time': 300, - 'results': [ + "cache_time": 300, + "results": [ { - 'title': 'first', - 'id': '11', - 'type': 'article', - 'input_message_content': {'message_text': 'first'}, + "title": "first", + "id": "11", + "type": "article", + "input_message_content": {"message_text": "first"}, }, { - 'title': 'second', - 'id': '12', - 'type': 'article', - 'input_message_content': {'message_text': 'second'}, + "title": "second", + "id": "12", + "type": "article", + "input_message_content": {"message_text": "second"}, }, ], - 'next_offset': '42', - 'switch_pm_parameter': 'start_pm', - 'inline_query_id': 1234, - 'is_personal': True, - 'switch_pm_text': 'switch pm', + "next_offset": "42", + "switch_pm_parameter": "start_pm", + "inline_query_id": 1234, + "is_personal": True, + "switch_pm_text": "switch pm", } - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) results = [ - InlineQueryResultArticle('11', 'first', InputTextMessageContent('first')), - InlineQueryResultArticle('12', 'second', InputTextMessageContent('second')), + InlineQueryResultArticle("11", "first", InputTextMessageContent("first")), + InlineQueryResultArticle("12", "second", InputTextMessageContent("second")), ] assert bot.answer_inline_query( @@ -755,44 +739,44 @@ def test(url, data, *args, **kwargs): results=results, cache_time=300, is_personal=True, - next_offset='42', - switch_pm_text='switch pm', - switch_pm_parameter='start_pm', + next_offset="42", + switch_pm_text="switch pm", + switch_pm_parameter="start_pm", ) - monkeypatch.delattr(bot.request, 'post') + monkeypatch.delattr(bot.request, "post") def test_answer_inline_query_no_default_parse_mode(self, monkeypatch, bot): def test(url, data, *args, **kwargs): return data == { - 'cache_time': 300, - 'results': [ + "cache_time": 300, + "results": [ { - 'title': 'test_result', - 'id': '123', - 'type': 'document', - 'document_url': 'https://raw.githubusercontent.com/' - 'python-telegram-bot/logos/master/logo/png/' - 'ptb-logo_240.png', - 'mime_type': 'image/png', - 'caption': 'ptb_logo', + "title": "test_result", + "id": "123", + "type": "document", + "document_url": "https://raw.githubusercontent.com/" + "python-telegram-bot/logos/master/logo/png/" + "ptb-logo_240.png", + "mime_type": "image/png", + "caption": "ptb_logo", } ], - 'next_offset': '42', - 'switch_pm_parameter': 'start_pm', - 'inline_query_id': 1234, - 'is_personal': True, - 'switch_pm_text': 'switch pm', + "next_offset": "42", + "switch_pm_parameter": "start_pm", + "inline_query_id": 1234, + "is_personal": True, + "switch_pm_text": "switch pm", } - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) results = [ InlineQueryResultDocument( - id='123', - document_url='https://raw.githubusercontent.com/python-telegram-bot/logos/master/' - 'logo/png/ptb-logo_240.png', - title='test_result', - mime_type='image/png', - caption='ptb_logo', + id="123", + document_url="https://raw.githubusercontent.com/python-telegram-bot/logos/master/" + "logo/png/ptb-logo_240.png", + title="test_result", + mime_type="image/png", + caption="ptb_logo", ) ] @@ -801,45 +785,45 @@ def test(url, data, *args, **kwargs): results=results, cache_time=300, is_personal=True, - next_offset='42', - switch_pm_text='switch pm', - switch_pm_parameter='start_pm', + next_offset="42", + switch_pm_text="switch pm", + switch_pm_parameter="start_pm", ) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_answer_inline_query_default_parse_mode(self, monkeypatch, default_bot): def test(url, data, *args, **kwargs): return data == { - 'cache_time': 300, - 'results': [ + "cache_time": 300, + "results": [ { - 'title': 'test_result', - 'id': '123', - 'type': 'document', - 'document_url': 'https://raw.githubusercontent.com/' - 'python-telegram-bot/logos/master/logo/png/' - 'ptb-logo_240.png', - 'mime_type': 'image/png', - 'caption': 'ptb_logo', - 'parse_mode': 'Markdown', + "title": "test_result", + "id": "123", + "type": "document", + "document_url": "https://raw.githubusercontent.com/" + "python-telegram-bot/logos/master/logo/png/" + "ptb-logo_240.png", + "mime_type": "image/png", + "caption": "ptb_logo", + "parse_mode": "Markdown", } ], - 'next_offset': '42', - 'switch_pm_parameter': 'start_pm', - 'inline_query_id': 1234, - 'is_personal': True, - 'switch_pm_text': 'switch pm', + "next_offset": "42", + "switch_pm_parameter": "start_pm", + "inline_query_id": 1234, + "is_personal": True, + "switch_pm_text": "switch pm", } - monkeypatch.setattr(default_bot.request, 'post', test) + monkeypatch.setattr(default_bot.request, "post", test) results = [ InlineQueryResultDocument( - id='123', - document_url='https://raw.githubusercontent.com/python-telegram-bot/logos/master/' - 'logo/png/ptb-logo_240.png', - title='test_result', - mime_type='image/png', - caption='ptb_logo', + id="123", + document_url="https://raw.githubusercontent.com/python-telegram-bot/logos/master/" + "logo/png/ptb-logo_240.png", + title="test_result", + mime_type="image/png", + caption="ptb_logo", ) ] @@ -848,23 +832,23 @@ def test(url, data, *args, **kwargs): results=results, cache_time=300, is_personal=True, - next_offset='42', - switch_pm_text='switch pm', - switch_pm_parameter='start_pm', + next_offset="42", + switch_pm_text="switch pm", + switch_pm_parameter="start_pm", ) def test_answer_inline_query_current_offset_error(self, bot, inline_results): - with pytest.raises(ValueError, match=('`current_offset` and `next_offset`')): + with pytest.raises(ValueError, match=("`current_offset` and `next_offset`")): bot.answer_inline_query( 1234, results=inline_results, next_offset=42, current_offset=51 ) @pytest.mark.parametrize( - 'current_offset,num_results,id_offset,expected_next_offset', + "current_offset,num_results,id_offset,expected_next_offset", [ - ('', MAX_INLINE_QUERY_RESULTS, 1, 1), + ("", MAX_INLINE_QUERY_RESULTS, 1, 1), (1, MAX_INLINE_QUERY_RESULTS, 51, 2), - (5, 3, 251, ''), + (5, 3, 251, ""), ], ) def test_answer_inline_query_current_offset_1( @@ -879,62 +863,62 @@ def test_answer_inline_query_current_offset_1( ): # For now just test that our internals pass the correct data def make_assertion(url, data, *args, **kwargs): - results = data['results'] + results = data["results"] length_matches = len(results) == num_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) + 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 - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.answer_inline_query(1234, results=inline_results, current_offset=current_offset) def test_answer_inline_query_current_offset_2(self, monkeypatch, bot, inline_results): # For now just test that our internals pass the correct data def make_assertion(url, data, *args, **kwargs): - results = data['results'] + 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)) - next_offset_matches = data['next_offset'] == '1' + 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 - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.answer_inline_query(1234, results=inline_results, current_offset=0) inline_results = inline_results[:30] def make_assertion(url, data, *args, **kwargs): - results = data['results'] + results = data["results"] length_matches = len(results) == 30 - ids_match = all(int(res['id']) == 1 + i for i, res in enumerate(results)) - next_offset_matches = data['next_offset'] == '' + 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 - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.answer_inline_query(1234, results=inline_results, current_offset=0) def test_answer_inline_query_current_offset_callback(self, monkeypatch, bot, caplog): # For now just test that our internals pass the correct data def make_assertion(url, data, *args, **kwargs): - results = data['results'] + results = data["results"] length = len(results) == 5 - ids = all(int(res['id']) == 6 + i for i, res in enumerate(results)) - next_offset = data['next_offset'] == '2' + 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 - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.answer_inline_query(1234, results=inline_results_callback, current_offset=1) def make_assertion(url, data, *args, **kwargs): - results = data['results'] + results = data["results"] length = results == [] - next_offset = data['next_offset'] == '' + next_offset = data["next_offset"] == "" return length and next_offset - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.answer_inline_query(1234, results=inline_results_callback, current_offset=6) @@ -952,135 +936,123 @@ def test_get_one_user_profile_photo(self, bot, chat_id): # get_file is tested multiple times in the test_*media* modules. # Here we only test the behaviour for bot apis in local mode def test_get_file_local_mode(self, bot, monkeypatch): - path = str(Path.cwd() / 'tests' / 'data' / 'game.gif') + path = str(Path.cwd() / "tests" / "data" / "game.gif") def _post(*args, **kwargs): return { - 'file_id': None, - 'file_unique_id': None, - 'file_size': None, - 'file_path': path, + "file_id": None, + "file_unique_id": None, + "file_size": None, + "file_path": path, } - monkeypatch.setattr(bot, '_post', _post) + monkeypatch.setattr(bot, "_post", _post) - resulting_path = bot.get_file('file_id').file_path + resulting_path = bot.get_file("file_id").file_path assert bot.token not in resulting_path assert resulting_path == path - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") # TODO: Needs improvement. No feasible way to test until bots can add members. def test_ban_chat_member(self, monkeypatch, bot): def test(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - user_id = data['user_id'] == 32 - until_date = data.get('until_date', 1577887200) == 1577887200 - revoke_msgs = data.get('revoke_messages', True) is True + chat_id = data["chat_id"] == 2 + user_id = data["user_id"] == 32 + until_date = data.get("until_date", 1577887200) == 1577887200 + revoke_msgs = data.get("revoke_messages", True) is True return chat_id and user_id and until_date and revoke_msgs - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) until = from_timestamp(1577887200) assert bot.ban_chat_member(2, 32) assert bot.ban_chat_member(2, 32, until_date=until) assert bot.ban_chat_member(2, 32, until_date=1577887200) assert bot.ban_chat_member(2, 32, revoke_messages=True) - monkeypatch.delattr(bot.request, 'post') + monkeypatch.delattr(bot.request, "post") def test_ban_chat_member_default_tz(self, monkeypatch, tz_bot): until = dtm.datetime(2020, 1, 11, 16, 13) until_timestamp = to_timestamp(until, tzinfo=tz_bot.defaults.tzinfo) def test(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - user_id = data['user_id'] == 32 - until_date = data.get('until_date', until_timestamp) == until_timestamp + chat_id = data["chat_id"] == 2 + user_id = data["user_id"] == 32 + until_date = data.get("until_date", until_timestamp) == until_timestamp return chat_id and user_id and until_date - monkeypatch.setattr(tz_bot.request, 'post', test) + monkeypatch.setattr(tz_bot.request, "post", test) assert tz_bot.ban_chat_member(2, 32) assert tz_bot.ban_chat_member(2, 32, until_date=until) assert tz_bot.ban_chat_member(2, 32, until_date=until_timestamp) - def test_kick_chat_member_warning(self, monkeypatch, bot, recwarn): - def test(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - user_id = data['user_id'] == 32 - return chat_id and user_id - - monkeypatch.setattr(bot.request, 'post', test) - bot.kick_chat_member(2, 32) - assert len(recwarn) == 1 - assert '`bot.kick_chat_member` is deprecated' in str(recwarn[0].message) - monkeypatch.delattr(bot.request, 'post') - # TODO: Needs improvement. - @pytest.mark.parametrize('only_if_banned', [True, False, None]) + @pytest.mark.parametrize("only_if_banned", [True, False, None]) def test_unban_chat_member(self, monkeypatch, bot, only_if_banned): def make_assertion(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - user_id = data['user_id'] == 32 - o_i_b = data.get('only_if_banned', None) == only_if_banned + chat_id = data["chat_id"] == 2 + user_id = data["user_id"] == 32 + o_i_b = data.get("only_if_banned", None) == only_if_banned return chat_id and user_id and o_i_b - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.unban_chat_member(2, 32, only_if_banned=only_if_banned) def test_set_chat_permissions(self, monkeypatch, bot, chat_permissions): def test(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - permissions = data['permissions'] == chat_permissions.to_dict() + chat_id = data["chat_id"] == 2 + permissions = data["permissions"] == chat_permissions.to_dict() return chat_id and permissions - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.set_chat_permissions(2, chat_permissions) def test_set_chat_administrator_custom_title(self, monkeypatch, bot): def test(url, data, *args, **kwargs): - chat_id = data['chat_id'] == 2 - user_id = data['user_id'] == 32 - custom_title = data['custom_title'] == 'custom_title' + chat_id = data["chat_id"] == 2 + user_id = data["user_id"] == 32 + custom_title = data["custom_title"] == "custom_title" return chat_id and user_id and custom_title - monkeypatch.setattr(bot.request, 'post', test) - assert bot.set_chat_administrator_custom_title(2, 32, 'custom_title') + monkeypatch.setattr(bot.request, "post", test) + assert bot.set_chat_administrator_custom_title(2, 32, "custom_title") # TODO: Needs improvement. Need an incoming callbackquery to test def test_answer_callback_query(self, monkeypatch, bot): # For now just test that our internals pass the correct data def test(url, data, *args, **kwargs): return data == { - 'callback_query_id': 23, - 'show_alert': True, - 'url': 'no_url', - 'cache_time': 1, - 'text': 'answer', + "callback_query_id": 23, + "show_alert": True, + "url": "no_url", + "cache_time": 1, + "text": "answer", } - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.answer_callback_query( - 23, text='answer', show_alert=True, url='no_url', cache_time=1 + 23, text="answer", show_alert=True, url="no_url", cache_time=1 ) @flaky(3, 1) def test_edit_message_text(self, bot, message): message = bot.edit_message_text( - text='new_text', + text="new_text", chat_id=message.chat_id, message_id=message.message_id, - parse_mode='HTML', + parse_mode="HTML", disable_web_page_preview=True, ) - assert message.text == 'new_text' + assert message.text == "new_text" @flaky(3, 1) def test_edit_message_text_entities(self, bot, message): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -1097,10 +1069,10 @@ def test_edit_message_text_entities(self, bot, message): assert message.entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_edit_message_text_default_parse_mode(self, default_bot, message): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.edit_message_text( text=test_markdown_string, @@ -1131,29 +1103,29 @@ def test_edit_message_text_default_parse_mode(self, default_bot, message): text=test_markdown_string, chat_id=message.chat_id, message_id=message.message_id, - parse_mode='HTML', + parse_mode="HTML", disable_web_page_preview=True, ) assert message.text == test_markdown_string assert message.text_markdown == escape_markdown(test_markdown_string) - @pytest.mark.skip(reason='need reference to an inline message') + @pytest.mark.skip(reason="need reference to an inline message") def test_edit_message_text_inline(self): pass @flaky(3, 1) def test_edit_message_caption(self, bot, media_message): message = bot.edit_message_caption( - caption='new_caption', + caption="new_caption", chat_id=media_message.chat_id, message_id=media_message.message_id, ) - assert message.caption == 'new_caption' + assert message.caption == "new_caption" @flaky(3, 1) def test_edit_message_caption_entities(self, bot, media_message): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -1172,10 +1144,10 @@ def test_edit_message_caption_entities(self, bot, media_message): # edit_message_media is tested in test_inputmedia @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_edit_message_caption_default_parse_mode(self, default_bot, media_message): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.edit_message_caption( caption=test_markdown_string, @@ -1203,7 +1175,7 @@ def test_edit_message_caption_default_parse_mode(self, default_bot, media_messag caption=test_markdown_string, chat_id=media_message.chat_id, message_id=media_message.message_id, - parse_mode='HTML', + parse_mode="HTML", ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @@ -1211,25 +1183,25 @@ def test_edit_message_caption_default_parse_mode(self, default_bot, media_messag @flaky(3, 1) def test_edit_message_caption_with_parse_mode(self, bot, media_message): message = bot.edit_message_caption( - caption='new *caption*', - parse_mode='Markdown', + caption="new *caption*", + parse_mode="Markdown", chat_id=media_message.chat_id, message_id=media_message.message_id, ) - assert message.caption == 'new caption' + assert message.caption == "new caption" def test_edit_message_caption_without_required(self, bot): - with pytest.raises(ValueError, match='Both chat_id and message_id are required when'): - bot.edit_message_caption(caption='new_caption') + with pytest.raises(ValueError, match="Both chat_id and message_id are required when"): + bot.edit_message_caption(caption="new_caption") - @pytest.mark.skip(reason='need reference to an inline message') + @pytest.mark.skip(reason="need reference to an inline message") def test_edit_message_caption_inline(self): pass @flaky(3, 1) def test_edit_reply_markup(self, bot, message): - new_markup = InlineKeyboardMarkup([[InlineKeyboardButton(text='test', callback_data='1')]]) + new_markup = InlineKeyboardMarkup([[InlineKeyboardButton(text="test", callback_data="1")]]) message = bot.edit_message_reply_markup( chat_id=message.chat_id, message_id=message.message_id, reply_markup=new_markup ) @@ -1237,11 +1209,11 @@ def test_edit_reply_markup(self, bot, message): assert message is not True def test_edit_message_reply_markup_without_required(self, bot): - new_markup = InlineKeyboardMarkup([[InlineKeyboardButton(text='test', callback_data='1')]]) - with pytest.raises(ValueError, match='Both chat_id and message_id are required when'): + new_markup = InlineKeyboardMarkup([[InlineKeyboardButton(text="test", callback_data="1")]]) + with pytest.raises(ValueError, match="Both chat_id and message_id are required when"): bot.edit_message_reply_markup(reply_markup=new_markup) - @pytest.mark.skip(reason='need reference to an inline message') + @pytest.mark.skip(reason="need reference to an inline message") def test_edit_reply_markup_inline(self): pass @@ -1264,13 +1236,13 @@ def post(*args, **kwargs): id=1, from_user=None, chat_instance=123, - data='invalid data', + data="invalid data", message=Message( 1, - from_user=User(1, '', False), + from_user=User(1, "", False), date=None, - chat=Chat(1, ''), - text='Webhook', + chat=Chat(1, ""), + text="Webhook", ), ), ).to_dict() @@ -1278,7 +1250,7 @@ def post(*args, **kwargs): bot.arbitrary_callback_data = True try: - monkeypatch.setattr(bot.request, 'post', post) + monkeypatch.setattr(bot.request, "post", post) bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = bot.get_updates(timeout=1) @@ -1293,14 +1265,14 @@ def post(*args, **kwargs): @flaky(3, 1) @pytest.mark.xfail def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot): - url = 'https://python-telegram-bot.org/test/webhook' + url = "https://python-telegram-bot.org/test/webhook" max_connections = 7 - allowed_updates = ['message'] + allowed_updates = ["message"] bot.set_webhook( url, max_connections=max_connections, allowed_updates=allowed_updates, - ip_address='127.0.0.1', + ip_address="127.0.0.1", ) time.sleep(2) live_info = bot.get_webhook_info() @@ -1308,38 +1280,38 @@ def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot): bot.delete_webhook() time.sleep(2) info = bot.get_webhook_info() - assert info.url == '' + assert info.url == "" assert live_info.url == url assert live_info.max_connections == max_connections assert live_info.allowed_updates == allowed_updates - assert live_info.ip_address == '127.0.0.1' + assert live_info.ip_address == "127.0.0.1" - @pytest.mark.parametrize('drop_pending_updates', [True, False]) + @pytest.mark.parametrize("drop_pending_updates", [True, False]) def test_set_webhook_delete_webhook_drop_pending_updates( self, bot, drop_pending_updates, monkeypatch ): def assertion(url, data, *args, **kwargs): - return bool(data.get('drop_pending_updates')) == drop_pending_updates + return bool(data.get("drop_pending_updates")) == drop_pending_updates - monkeypatch.setattr(bot.request, 'post', assertion) + monkeypatch.setattr(bot.request, "post", assertion) - assert bot.set_webhook(drop_pending_updates=drop_pending_updates) + assert bot.set_webhook("", drop_pending_updates=drop_pending_updates) assert bot.delete_webhook(drop_pending_updates=drop_pending_updates) @flaky(3, 1) def test_leave_chat(self, bot): - with pytest.raises(BadRequest, match='Chat not found'): + with pytest.raises(BadRequest, match="Chat not found"): bot.leave_chat(-123456) - with pytest.raises(NetworkError, match='Chat not found'): + with pytest.raises(NetworkError, match="Chat not found"): bot.leave_chat(-123456) @flaky(3, 1) def test_get_chat(self, bot, super_group_id): chat = bot.get_chat(super_group_id) - assert chat.type == 'supergroup' - assert chat.title == f'>>> telegram.Bot(test) @{bot.username}' + assert chat.type == "supergroup" + assert chat.title == f">>> telegram.Bot(test) @{bot.username}" assert chat.id == int(super_group_id) @flaky(3, 1) @@ -1348,7 +1320,7 @@ def test_get_chat_administrators(self, bot, channel_id): assert isinstance(admins, list) for a in admins: - assert a.status in ('administrator', 'creator') + assert a.status in ("administrator", "creator") @flaky(3, 1) def test_get_chat_member_count(self, bot, channel_id): @@ -1356,23 +1328,13 @@ def test_get_chat_member_count(self, bot, channel_id): assert isinstance(count, int) assert count > 3 - def test_get_chat_members_count_warning(self, bot, channel_id, recwarn): - bot.get_chat_members_count(channel_id) - assert len(recwarn) == 1 - assert '`bot.get_chat_members_count` is deprecated' in str(recwarn[0].message) - - def test_bot_command_property_warning(self, bot, recwarn): - _ = bot.commands - assert len(recwarn) == 1 - assert 'Bot.commands has been deprecated since there can' in str(recwarn[0].message) - @flaky(3, 1) def test_get_chat_member(self, bot, channel_id, chat_id): chat_member = bot.get_chat_member(channel_id, chat_id) - assert chat_member.status == 'administrator' - assert chat_member.user.first_name == 'PTB' - assert chat_member.user.last_name == 'Test user' + assert chat_member.status == "administrator" + assert chat_member.user.first_name == "PTB" + assert chat_member.user.last_name == "Test user" @pytest.mark.skip(reason="Not implemented yet.") def test_set_chat_sticker_set(self): @@ -1384,31 +1346,31 @@ def test_delete_chat_sticker_set(self): @flaky(3, 1) def test_send_game(self, bot, chat_id): - game_short_name = 'test_game' + game_short_name = "test_game" message = bot.send_game(chat_id, game_short_name) assert message.game assert message.game.description == ( - 'A no-op test game, for python-telegram-bot bot framework testing.' + "A no-op test game, for python-telegram-bot bot framework testing." ) - assert message.game.animation.file_id != '' + assert message.game.animation.file_id != "" # We added some test bots later and for some reason the file size is not the same for them # so we accept three different sizes here. Shouldn't be too much of assert message.game.photo[0].file_size in [851, 4928, 850] @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_game_default_allow_sending_without_reply(self, default_bot, chat_id, custom): - game_short_name = 'test_game' - reply_to_message = default_bot.send_message(chat_id, 'test') + game_short_name = "test_game" + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_game( @@ -1426,7 +1388,7 @@ def test_send_game_default_allow_sending_without_reply(self, default_bot, chat_i ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_game( chat_id, game_short_name, reply_to_message_id=reply_to_message.message_id ) @@ -1435,7 +1397,7 @@ def test_send_game_default_allow_sending_without_reply(self, default_bot, chat_i def test_set_game_score_1(self, bot, chat_id): # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods # First, test setting a score. - game_short_name = 'test_game' + game_short_name = "test_game" game = bot.send_game(chat_id, game_short_name) message = bot.set_game_score( @@ -1454,7 +1416,7 @@ def test_set_game_score_1(self, bot, chat_id): def test_set_game_score_2(self, bot, chat_id): # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods # Test setting a score higher than previous - game_short_name = 'test_game' + game_short_name = "test_game" game = bot.send_game(chat_id, game_short_name) score = BASE_GAME_SCORE + 1 @@ -1476,12 +1438,12 @@ def test_set_game_score_2(self, bot, chat_id): def test_set_game_score_3(self, bot, chat_id): # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods # Test setting a score lower than previous (should raise error) - game_short_name = 'test_game' + game_short_name = "test_game" game = bot.send_game(chat_id, game_short_name) score = BASE_GAME_SCORE # Even a score equal to previous raises an error. - with pytest.raises(BadRequest, match='Bot_score_not_modified'): + with pytest.raises(BadRequest, match="Bot_score_not_modified"): bot.set_game_score( user_id=chat_id, score=score, chat_id=game.chat_id, message_id=game.message_id ) @@ -1490,7 +1452,7 @@ def test_set_game_score_3(self, bot, chat_id): def test_set_game_score_4(self, bot, chat_id): # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods # Test force setting a lower score - game_short_name = 'test_game' + game_short_name = "test_game" game = bot.send_game(chat_id, game_short_name) time.sleep(2) @@ -1516,7 +1478,7 @@ def test_set_game_score_4(self, bot, chat_id): @xfail def test_get_game_high_scores(self, bot, chat_id): # We need a game to get the scores for - game_short_name = 'test_game' + game_short_name = "test_game" game = bot.send_game(chat_id, game_short_name) high_scores = bot.get_game_high_scores(chat_id, game.chat_id, game.message_id) # We assume that the other game score tests ran within 20 sec @@ -1529,42 +1491,42 @@ def test_answer_shipping_query_ok(self, monkeypatch, bot): # For now just test that our internals pass the correct data def test(url, data, *args, **kwargs): return data == { - 'shipping_query_id': 1, - 'ok': True, - 'shipping_options': [ - {'title': 'option1', 'prices': [{'label': 'price', 'amount': 100}], 'id': 1} + "shipping_query_id": 1, + "ok": True, + "shipping_options": [ + {"title": "option1", "prices": [{"label": "price", "amount": 100}], "id": 1} ], } - monkeypatch.setattr(bot.request, 'post', test) - shipping_options = ShippingOption(1, 'option1', [LabeledPrice('price', 100)]) + monkeypatch.setattr(bot.request, "post", test) + shipping_options = ShippingOption(1, "option1", [LabeledPrice("price", 100)]) assert bot.answer_shipping_query(1, True, shipping_options=[shipping_options]) def test_answer_shipping_query_error_message(self, monkeypatch, bot): # For now just test that our internals pass the correct data def test(url, data, *args, **kwargs): return data == { - 'shipping_query_id': 1, - 'error_message': 'Not enough fish', - 'ok': False, + "shipping_query_id": 1, + "error_message": "Not enough fish", + "ok": False, } - monkeypatch.setattr(bot.request, 'post', test) - assert bot.answer_shipping_query(1, False, error_message='Not enough fish') + monkeypatch.setattr(bot.request, "post", test) + assert bot.answer_shipping_query(1, False, error_message="Not enough fish") def test_answer_shipping_query_errors(self, monkeypatch, bot): - shipping_options = ShippingOption(1, 'option1', [LabeledPrice('price', 100)]) + shipping_options = ShippingOption(1, "option1", [LabeledPrice("price", 100)]) - with pytest.raises(TelegramError, match='should not be empty and there should not be'): - bot.answer_shipping_query(1, True, error_message='Not enough fish') + with pytest.raises(TelegramError, match="should not be empty and there should not be"): + bot.answer_shipping_query(1, True, error_message="Not enough fish") - with pytest.raises(TelegramError, match='should not be empty and there should not be'): + with pytest.raises(TelegramError, match="should not be empty and there should not be"): bot.answer_shipping_query(1, False) - with pytest.raises(TelegramError, match='should not be empty and there should not be'): + with pytest.raises(TelegramError, match="should not be empty and there should not be"): bot.answer_shipping_query(1, False, shipping_options=shipping_options) - with pytest.raises(TelegramError, match='should not be empty and there should not be'): + with pytest.raises(TelegramError, match="should not be empty and there should not be"): bot.answer_shipping_query(1, True) with pytest.raises(AssertionError): @@ -1574,34 +1536,34 @@ def test_answer_shipping_query_errors(self, monkeypatch, bot): def test_answer_pre_checkout_query_ok(self, monkeypatch, bot): # For now just test that our internals pass the correct data def test(url, data, *args, **kwargs): - return data == {'pre_checkout_query_id': 1, 'ok': True} + return data == {"pre_checkout_query_id": 1, "ok": True} - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.answer_pre_checkout_query(1, True) def test_answer_pre_checkout_query_error_message(self, monkeypatch, bot): # For now just test that our internals pass the correct data def test(url, data, *args, **kwargs): return data == { - 'pre_checkout_query_id': 1, - 'error_message': 'Not enough fish', - 'ok': False, + "pre_checkout_query_id": 1, + "error_message": "Not enough fish", + "ok": False, } - monkeypatch.setattr(bot.request, 'post', test) - assert bot.answer_pre_checkout_query(1, False, error_message='Not enough fish') + monkeypatch.setattr(bot.request, "post", test) + assert bot.answer_pre_checkout_query(1, False, error_message="Not enough fish") def test_answer_pre_checkout_query_errors(self, monkeypatch, bot): - with pytest.raises(TelegramError, match='should not be'): - bot.answer_pre_checkout_query(1, True, error_message='Not enough fish') + with pytest.raises(TelegramError, match="should not be"): + bot.answer_pre_checkout_query(1, True, error_message="Not enough fish") - with pytest.raises(TelegramError, match='should not be empty'): + with pytest.raises(TelegramError, match="should not be empty"): bot.answer_pre_checkout_query(1, False) @flaky(3, 1) def test_restrict_chat_member(self, bot, channel_id, chat_permissions): # TODO: Add bot to supergroup so this can be tested properly - with pytest.raises(BadRequest, match='Method is available only for supergroups'): + with pytest.raises(BadRequest, match="Method is available only for supergroups"): assert bot.restrict_chat_member( channel_id, 95205500, chat_permissions, until_date=dtm.datetime.utcnow() ) @@ -1613,9 +1575,9 @@ def test_restrict_chat_member_default_tz( until_timestamp = to_timestamp(until, tzinfo=tz_bot.defaults.tzinfo) def test(url, data, *args, **kwargs): - return data.get('until_date', until_timestamp) == until_timestamp + return data.get("until_date", until_timestamp) == until_timestamp - monkeypatch.setattr(tz_bot.request, 'post', test) + monkeypatch.setattr(tz_bot.request, "post", test) assert tz_bot.restrict_chat_member(channel_id, 95205500, chat_permissions) assert tz_bot.restrict_chat_member( @@ -1628,7 +1590,7 @@ def test(url, data, *args, **kwargs): @flaky(3, 1) def test_promote_chat_member(self, bot, channel_id, monkeypatch): # TODO: Add bot to supergroup so this can be tested properly / give bot perms - with pytest.raises(BadRequest, match='Not enough rights'): + with pytest.raises(BadRequest, match="Not enough rights"): assert bot.promote_chat_member( channel_id, 95205500, @@ -1649,22 +1611,22 @@ def test_promote_chat_member(self, bot, channel_id, monkeypatch): def make_assertion(*args, **_): data = args[1] return ( - data.get('chat_id') == channel_id - and data.get('user_id') == 95205500 - and data.get('is_anonymous') == 1 - and data.get('can_change_info') == 2 - and data.get('can_post_messages') == 3 - and data.get('can_edit_messages') == 4 - and data.get('can_delete_messages') == 5 - and data.get('can_invite_users') == 6 - and data.get('can_restrict_members') == 7 - and data.get('can_pin_messages') == 8 - and data.get('can_promote_members') == 9 - and data.get('can_manage_chat') == 10 - and data.get('can_manage_voice_chats') == 11 + data.get("chat_id") == channel_id + and data.get("user_id") == 95205500 + and data.get("is_anonymous") == 1 + and data.get("can_change_info") == 2 + and data.get("can_post_messages") == 3 + and data.get("can_edit_messages") == 4 + and data.get("can_delete_messages") == 5 + and data.get("can_invite_users") == 6 + and data.get("can_restrict_members") == 7 + and data.get("can_pin_messages") == 8 + and data.get("can_promote_members") == 9 + and data.get("can_manage_chat") == 10 + and data.get("can_manage_voice_chats") == 11 ) - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) assert bot.promote_chat_member( channel_id, 95205500, @@ -1686,10 +1648,10 @@ def test_export_chat_invite_link(self, bot, channel_id): # Each link is unique apparently invite_link = bot.export_chat_invite_link(channel_id) assert isinstance(invite_link, str) - assert invite_link != '' + assert invite_link != "" @flaky(3, 1) - @pytest.mark.parametrize('datetime', argvalues=[True, False], ids=['datetime', 'integer']) + @pytest.mark.parametrize("datetime", argvalues=[True, False], ids=["datetime", "integer"]) def test_advanced_chat_invite_links(self, bot, channel_id, datetime): # we are testing this all in one function in order to save api calls timestamp = dtm.datetime.utcnow() @@ -1701,8 +1663,8 @@ def test_advanced_chat_invite_links(self, bot, channel_id, datetime): invite_link = bot.create_chat_invite_link( channel_id, expire_date=expire_time, member_limit=10 ) - assert invite_link.invite_link != '' - assert not invite_link.invite_link.endswith('...') + assert invite_link.invite_link != "" + assert not invite_link.invite_link.endswith("...") assert pytest.approx(invite_link.expire_date == aware_time_in_future) assert invite_link.member_limit == 10 @@ -1732,8 +1694,8 @@ def test_advanced_chat_invite_links_default_tzinfo(self, tz_bot, channel_id): invite_link = tz_bot.create_chat_invite_link( channel_id, expire_date=time_in_future, member_limit=10 ) - assert invite_link.invite_link != '' - assert not invite_link.invite_link.endswith('...') + assert invite_link.invite_link != "" + assert not invite_link.invite_link.endswith("...") assert pytest.approx(invite_link.expire_date == aware_expire_date) assert invite_link.member_limit == 10 @@ -1757,20 +1719,20 @@ def test_set_chat_photo(self, bot, channel_id): def func(): assert bot.set_chat_photo(channel_id, f) - with open('tests/data/telegram_test_channel.jpg', 'rb') as f: - expect_bad_request(func, 'Type of file mismatch', 'Telegram did not accept the file.') + with open("tests/data/telegram_test_channel.jpg", "rb") as f: + expect_bad_request(func, "Type of file mismatch", "Telegram did not accept the file.") def test_set_chat_photo_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('photo') == expected + test_flag = data.get("photo") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.set_chat_photo(chat_id, file) assert test_flag @@ -1779,17 +1741,16 @@ def test_delete_chat_photo(self, bot, channel_id): def func(): assert bot.delete_chat_photo(channel_id) - expect_bad_request(func, 'Chat_not_modified', 'Chat photo was not set.') + expect_bad_request(func, "Chat_not_modified", "Chat photo was not set.") @flaky(3, 1) def test_set_chat_title(self, bot, channel_id): - assert bot.set_chat_title(channel_id, '>>> telegram.Bot() - Tests') + assert bot.set_chat_title(channel_id, ">>> telegram.Bot() - Tests") @flaky(3, 1) def test_set_chat_description(self, bot, channel_id): - assert bot.set_chat_description(channel_id, 'Time: ' + str(time.time())) + assert bot.set_chat_description(channel_id, "Time: " + str(time.time())) - # TODO: Add bot to group to test there too @flaky(3, 1) def test_pin_and_unpin_message(self, bot, super_group_id): message1 = bot.send_message(super_group_id, text="test_pin_message_1") @@ -1832,17 +1793,17 @@ class OkException(Exception): TIMEOUT = 500 def request_wrapper(*args, **kwargs): - obj = kwargs.get('timeout') + obj = kwargs.get("timeout") if isinstance(obj, Timeout) and obj._read == TIMEOUT: raise OkException return b'{"ok": true, "result": []}' - monkeypatch.setattr('telegram.utils.request.Request._request_wrapper', request_wrapper) + monkeypatch.setattr("telegram.utils.request.Request._request_wrapper", request_wrapper) # Test file uploading with pytest.raises(OkException): - bot.send_photo(chat_id, open('tests/data/telegram.jpg', 'rb'), timeout=TIMEOUT) + bot.send_photo(chat_id, open("tests/data/telegram.jpg", "rb"), timeout=TIMEOUT) # Test JSON submission with pytest.raises(OkException): @@ -1856,21 +1817,21 @@ class OkException(Exception): pass def request_wrapper(*args, **kwargs): - obj = kwargs.get('timeout') + obj = kwargs.get("timeout") if isinstance(obj, Timeout) and obj._read == 20: raise OkException return b'{"ok": true, "result": []}' - monkeypatch.setattr('telegram.utils.request.Request._request_wrapper', request_wrapper) + monkeypatch.setattr("telegram.utils.request.Request._request_wrapper", request_wrapper) # Test file uploading with pytest.raises(OkException): - bot.send_photo(chat_id, open('tests/data/telegram.jpg', 'rb')) + bot.send_photo(chat_id, open("tests/data/telegram.jpg", "rb")) @flaky(3, 1) def test_send_message_entities(self, bot, chat_id): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -1881,10 +1842,10 @@ def test_send_message_entities(self, bot, chat_id): assert message.entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_message_default_parse_mode(self, default_bot, chat_id): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_message(chat_id, test_markdown_string) assert message.text_markdown == test_markdown_string @@ -1894,88 +1855,63 @@ def test_send_message_default_parse_mode(self, default_bot, chat_id): assert message.text == test_markdown_string assert message.text_markdown == escape_markdown(test_markdown_string) - message = default_bot.send_message(chat_id, test_markdown_string, parse_mode='HTML') + message = default_bot.send_message(chat_id, test_markdown_string, parse_mode="HTML") assert message.text == test_markdown_string assert message.text_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_message_default_allow_sending_without_reply(self, default_bot, chat_id, custom): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_message( chat_id, - 'test', + "test", allow_sending_without_reply=custom, reply_to_message_id=reply_to_message.message_id, ) assert message.reply_to_message is None elif default_bot.defaults.allow_sending_without_reply: message = default_bot.send_message( - chat_id, 'test', reply_to_message_id=reply_to_message.message_id + chat_id, "test", reply_to_message_id=reply_to_message.message_id ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_message( - chat_id, 'test', reply_to_message_id=reply_to_message.message_id + chat_id, "test", reply_to_message_id=reply_to_message.message_id ) @flaky(3, 1) def test_set_and_get_my_commands(self, bot): - commands = [ - BotCommand('cmd1', 'descr1'), - BotCommand('cmd2', 'descr2'), - ] + commands = [BotCommand("cmd1", "descr1"), ["cmd2", "descr2"]] bot.set_my_commands([]) assert bot.get_my_commands() == [] - assert bot.commands == [] assert bot.set_my_commands(commands) - for bc in [bot.get_my_commands(), bot.commands]: - assert len(bc) == 2 - assert bc[0].command == 'cmd1' - assert bc[0].description == 'descr1' - assert bc[1].command == 'cmd2' - assert bc[1].description == 'descr2' - - @flaky(3, 1) - def test_set_and_get_my_commands_strings(self, bot): - commands = [ - ['cmd1', 'descr1'], - ['cmd2', 'descr2'], - ] - bot.set_my_commands([]) - assert bot.get_my_commands() == [] - assert bot.commands == [] - assert bot.set_my_commands(commands) - - for bc in [bot.get_my_commands(), bot.commands]: - assert len(bc) == 2 - assert bc[0].command == 'cmd1' - assert bc[0].description == 'descr1' - assert bc[1].command == 'cmd2' - assert bc[1].description == 'descr2' + for i, bc in enumerate(bot.get_my_commands()): + assert bc.command == f"cmd{i+1}" + assert bc.description == f"descr{i+1}" @flaky(3, 1) def test_get_set_delete_my_commands_with_scope(self, bot, super_group_id, chat_id): - group_cmds = [BotCommand('group_cmd', 'visible to this supergroup only')] - private_cmds = [BotCommand('private_cmd', 'visible to this private chat only')] + group_cmds = [BotCommand("group_cmd", "visible to this supergroup only")] + private_cmds = [BotCommand("private_cmd", "visible to this private chat only")] group_scope = BotCommandScopeChat(super_group_id) private_scope = BotCommandScopeChat(chat_id) # Set supergroup command list with lang code and check if the same can be returned from api - bot.set_my_commands(group_cmds, scope=group_scope, language_code='en') - gotten_group_cmds = bot.get_my_commands(scope=group_scope, language_code='en') + bot.set_my_commands(group_cmds, scope=group_scope, language_code="en") + gotten_group_cmds = bot.get_my_commands(scope=group_scope, language_code="en") assert len(gotten_group_cmds) == len(group_cmds) assert gotten_group_cmds[0].command == group_cmds[0].command @@ -1987,44 +1923,41 @@ def test_get_set_delete_my_commands_with_scope(self, bot, super_group_id, chat_i assert len(gotten_private_cmd) == len(private_cmds) assert gotten_private_cmd[0].command == private_cmds[0].command - assert len(bot.commands) == 2 # set from previous test. Makes sure this hasn't changed. - assert bot.commands[0].command == 'cmd1' - # Delete command list from that supergroup and private chat- bot.delete_my_commands(private_scope) - bot.delete_my_commands(group_scope, 'en') + bot.delete_my_commands(group_scope, "en") # Check if its been deleted- deleted_priv_cmds = bot.get_my_commands(scope=private_scope) - deleted_grp_cmds = bot.get_my_commands(scope=group_scope, language_code='en') + deleted_grp_cmds = bot.get_my_commands(scope=group_scope, language_code="en") assert len(deleted_grp_cmds) == 0 == len(group_cmds) - 1 assert len(deleted_priv_cmds) == 0 == len(private_cmds) - 1 bot.delete_my_commands() # Delete commands from default scope - assert not bot.commands # Check if this has been updated to reflect the deletion. + assert len(bot.get_my_commands()) == 0 def test_log_out(self, monkeypatch, bot): # We don't actually make a request as to not break the test setup def assertion(url, data, *args, **kwargs): - return data == {} and url.split('/')[-1] == 'logOut' + return data == {} and url.split("/")[-1] == "logOut" - monkeypatch.setattr(bot.request, 'post', assertion) + monkeypatch.setattr(bot.request, "post", assertion) assert bot.log_out() def test_close(self, monkeypatch, bot): # We don't actually make a request as to not break the test setup def assertion(url, data, *args, **kwargs): - return data == {} and url.split('/')[-1] == 'close' + return data == {} and url.split("/")[-1] == "close" - monkeypatch.setattr(bot.request, 'post', assertion) + monkeypatch.setattr(bot.request, "post", assertion) assert bot.close() @flaky(3, 1) - @pytest.mark.parametrize('json_keyboard', [True, False]) - @pytest.mark.parametrize('caption', ["Test", '', None]) + @pytest.mark.parametrize("json_keyboard", [True, False]) + @pytest.mark.parametrize("caption", ["Test", "", None]) def test_copy_message(self, monkeypatch, bot, chat_id, media_message, json_keyboard, caption): keyboard = InlineKeyboardMarkup( [[InlineKeyboardButton(text="test", callback_data="test2")]] @@ -2042,7 +1975,7 @@ def post(url, data, timeout): assert data["caption_entities"] == [MessageEntity(MessageEntity.BOLD, 0, 4)] return data - monkeypatch.setattr(bot.request, 'post', post) + monkeypatch.setattr(bot.request, "post", post) bot.copy_message( chat_id, from_chat_id=chat_id, @@ -2081,19 +2014,19 @@ def test_copy_message_without_reply(self, bot, chat_id, media_message): @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot', + "default_bot", [ - ({'parse_mode': ParseMode.HTML, 'allow_sending_without_reply': True}), - ({'parse_mode': None, 'allow_sending_without_reply': True}), - ({'parse_mode': None, 'allow_sending_without_reply': False}), + ({"parse_mode": ParseMode.HTML, "allow_sending_without_reply": True}), + ({"parse_mode": None, "allow_sending_without_reply": True}), + ({"parse_mode": None, "allow_sending_without_reply": False}), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_copy_message_with_default(self, default_bot, chat_id, media_message): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if not default_bot.defaults.allow_sending_without_reply: - with pytest.raises(BadRequest, match='not found'): + with pytest.raises(BadRequest, match="not found"): default_bot.copy_message( chat_id, from_chat_id=chat_id, @@ -2123,9 +2056,9 @@ def test_copy_message_with_default(self, default_bot, chat_id, media_message): def test_replace_callback_data_send_message(self, bot, chat_id): try: bot.arbitrary_callback_data = True - replace_button = InlineKeyboardButton(text='replace', callback_data='replace_test') + replace_button = InlineKeyboardButton(text="replace", callback_data="replace_test") no_replace_button = InlineKeyboardButton( - text='no_replace', url='http://python-telegram-bot.org/' + text="no_replace", url="http://python-telegram-bot.org/" ) reply_markup = InlineKeyboardMarkup.from_row( [ @@ -2133,26 +2066,26 @@ def test_replace_callback_data_send_message(self, bot, chat_id): no_replace_button, ] ) - message = bot.send_message(chat_id=chat_id, text='test', reply_markup=reply_markup) + message = bot.send_message(chat_id=chat_id, text="test", reply_markup=reply_markup) inline_keyboard = message.reply_markup.inline_keyboard assert inline_keyboard[0][1] == no_replace_button assert inline_keyboard[0][0] == replace_button keyboard = list(bot.callback_data_cache._keyboard_data)[0] data = list(bot.callback_data_cache._keyboard_data[keyboard].button_data.values())[0] - assert data == 'replace_test' + assert data == "replace_test" finally: bot.arbitrary_callback_data = False bot.callback_data_cache.clear_callback_data() bot.callback_data_cache.clear_callback_queries() def test_replace_callback_data_stop_poll_and_repl_to_message(self, bot, chat_id): - poll_message = bot.send_poll(chat_id=chat_id, question='test', options=['1', '2']) + poll_message = bot.send_poll(chat_id=chat_id, question="test", options=["1", "2"]) try: bot.arbitrary_callback_data = True - replace_button = InlineKeyboardButton(text='replace', callback_data='replace_test') + replace_button = InlineKeyboardButton(text="replace", callback_data="replace_test") no_replace_button = InlineKeyboardButton( - text='no_replace', url='http://python-telegram-bot.org/' + text="no_replace", url="http://python-telegram-bot.org/" ) reply_markup = InlineKeyboardMarkup.from_row( [ @@ -2161,7 +2094,7 @@ def test_replace_callback_data_stop_poll_and_repl_to_message(self, bot, chat_id) ] ) poll_message.stop_poll(reply_markup=reply_markup) - helper_message = poll_message.reply_text('temp', quote=True) + helper_message = poll_message.reply_text("temp", quote=True) message = helper_message.reply_to_message inline_keyboard = message.reply_markup.inline_keyboard @@ -2169,7 +2102,7 @@ def test_replace_callback_data_stop_poll_and_repl_to_message(self, bot, chat_id) assert inline_keyboard[0][0] == replace_button keyboard = list(bot.callback_data_cache._keyboard_data)[0] data = list(bot.callback_data_cache._keyboard_data[keyboard].button_data.values())[0] - assert data == 'replace_test' + assert data == "replace_test" finally: bot.arbitrary_callback_data = False bot.callback_data_cache.clear_callback_data() @@ -2178,12 +2111,12 @@ def test_replace_callback_data_stop_poll_and_repl_to_message(self, bot, chat_id) def test_replace_callback_data_copy_message(self, bot, chat_id): """This also tests that data is inserted into the buttons of message.reply_to_message where message is the return value of a bot method""" - original_message = bot.send_message(chat_id=chat_id, text='original') + original_message = bot.send_message(chat_id=chat_id, text="original") try: bot.arbitrary_callback_data = True - replace_button = InlineKeyboardButton(text='replace', callback_data='replace_test') + replace_button = InlineKeyboardButton(text="replace", callback_data="replace_test") no_replace_button = InlineKeyboardButton( - text='no_replace', url='http://python-telegram-bot.org/' + text="no_replace", url="http://python-telegram-bot.org/" ) reply_markup = InlineKeyboardMarkup.from_row( [ @@ -2193,7 +2126,7 @@ def test_replace_callback_data_copy_message(self, bot, chat_id): ) message_id = original_message.copy(chat_id=chat_id, reply_markup=reply_markup) helper_message = bot.send_message( - chat_id=chat_id, reply_to_message_id=message_id.message_id, text='temp' + chat_id=chat_id, reply_to_message_id=message_id.message_id, text="temp" ) message = helper_message.reply_to_message inline_keyboard = message.reply_markup.inline_keyboard @@ -2202,7 +2135,7 @@ def test_replace_callback_data_copy_message(self, bot, chat_id): assert inline_keyboard[0][0] == replace_button keyboard = list(bot.callback_data_cache._keyboard_data)[0] data = list(bot.callback_data_cache._keyboard_data[keyboard].button_data.values())[0] - assert data == 'replace_test' + assert data == "replace_test" finally: bot.arbitrary_callback_data = False bot.callback_data_cache.clear_callback_data() @@ -2218,7 +2151,7 @@ def make_assertion( api_kwargs=None, ): inline_keyboard = InlineKeyboardMarkup.de_json( - data['results'][0]['reply_markup'], bot + data["results"][0]["reply_markup"], bot ).inline_keyboard assertion_1 = inline_keyboard[0][1] == no_replace_button assertion_2 = inline_keyboard[0][0] != replace_button @@ -2228,16 +2161,16 @@ def make_assertion( ) assertion_3 = ( bot.callback_data_cache._keyboard_data[keyboard].button_data[button] - == 'replace_test' + == "replace_test" ) - assertion_4 = 'reply_markup' not in data['results'][1] + assertion_4 = "reply_markup" not in data["results"][1] return assertion_1 and assertion_2 and assertion_3 and assertion_4 try: bot.arbitrary_callback_data = True - replace_button = InlineKeyboardButton(text='replace', callback_data='replace_test') + replace_button = InlineKeyboardButton(text="replace", callback_data="replace_test") no_replace_button = InlineKeyboardButton( - text='no_replace', url='http://python-telegram-bot.org/' + text="no_replace", url="http://python-telegram-bot.org/" ) reply_markup = InlineKeyboardMarkup.from_row( [ @@ -2247,15 +2180,15 @@ def make_assertion( ) bot.username # call this here so `bot.get_me()` won't be called after mocking - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) results = [ InlineQueryResultArticle( - '11', 'first', InputTextMessageContent('first'), reply_markup=reply_markup + "11", "first", InputTextMessageContent("first"), reply_markup=reply_markup ), InlineQueryResultVoice( - '22', - 'https://python-telegram-bot.org/static/testfiles/telegram.ogg', - title='second', + "22", + "https://python-telegram-bot.org/static/testfiles/telegram.ogg", + title="second", ), ] @@ -2270,17 +2203,17 @@ def test_get_chat_arbitrary_callback_data(self, super_group_id, bot): try: bot.arbitrary_callback_data = True reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='callback_data') + InlineKeyboardButton(text="text", callback_data="callback_data") ) message = bot.send_message( - super_group_id, text='get_chat_arbitrary_callback_data', reply_markup=reply_markup + super_group_id, text="get_chat_arbitrary_callback_data", reply_markup=reply_markup ) message.pin() keyboard = list(bot.callback_data_cache._keyboard_data)[0] data = list(bot.callback_data_cache._keyboard_data[keyboard].button_data.values())[0] - assert data == 'callback_data' + assert data == "callback_data" chat = bot.get_chat(super_group_id) assert chat.pinned_message == message @@ -2302,9 +2235,9 @@ def post(*args, **kwargs): update = Update( 17, poll=Poll( - '42', - 'question', - options=[PollOption('option', 0)], + "42", + "question", + options=[PollOption("option", 0)], total_voter_count=0, is_closed=False, is_anonymous=True, @@ -2316,25 +2249,25 @@ def post(*args, **kwargs): try: bot.arbitrary_callback_data = True - monkeypatch.setattr(bot.request, 'post', post) + monkeypatch.setattr(bot.request, "post", post) bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = bot.get_updates(timeout=1) assert len(updates) == 1 assert updates[0].update_id == 17 - assert updates[0].poll.id == '42' + assert updates[0].poll.id == "42" finally: bot.arbitrary_callback_data = False @pytest.mark.parametrize( - 'message_type', ['channel_post', 'edited_channel_post', 'message', 'edited_message'] + "message_type", ["channel_post", "edited_channel_post", "message", "edited_message"] ) def test_arbitrary_callback_data_pinned_message_reply_to_message( self, super_group_id, bot, monkeypatch, message_type ): bot.arbitrary_callback_data = True reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='callback_data') + InlineKeyboardButton(text="text", callback_data="callback_data") ) message = Message( @@ -2359,7 +2292,7 @@ def post(*args, **kwargs): return [update.to_dict()] try: - monkeypatch.setattr(bot.request, 'post', post) + monkeypatch.setattr(bot.request, "post", post) bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = bot.get_updates(timeout=1) @@ -2369,16 +2302,16 @@ def post(*args, **kwargs): effective_message = updates[0][message_type] assert ( effective_message.reply_to_message.reply_markup.inline_keyboard[0][0].callback_data - == 'callback_data' + == "callback_data" ) assert ( effective_message.pinned_message.reply_markup.inline_keyboard[0][0].callback_data - == 'callback_data' + == "callback_data" ) pinned_message = effective_message.reply_to_message.pinned_message assert ( - pinned_message.reply_markup.inline_keyboard[0][0].callback_data == 'callback_data' + pinned_message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data" ) finally: @@ -2400,15 +2333,15 @@ def test_arbitrary_callback_data_get_chat_no_pinned_message(self, super_group_id bot.arbitrary_callback_data = False @pytest.mark.parametrize( - 'message_type', ['channel_post', 'edited_channel_post', 'message', 'edited_message'] + "message_type", ["channel_post", "edited_channel_post", "message", "edited_message"] ) - @pytest.mark.parametrize('self_sender', [True, False]) + @pytest.mark.parametrize("self_sender", [True, False]) def test_arbitrary_callback_data_via_bot( self, super_group_id, bot, monkeypatch, self_sender, message_type ): bot.arbitrary_callback_data = True reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='callback_data') + InlineKeyboardButton(text="text", callback_data="callback_data") ) reply_markup = bot.callback_data_cache.process_keyboard(reply_markup) @@ -2417,14 +2350,14 @@ def test_arbitrary_callback_data_via_bot( None, None, reply_markup=reply_markup, - via_bot=bot.bot if self_sender else User(1, 'first', False), + via_bot=bot.bot if self_sender else User(1, "first", False), ) def post(*args, **kwargs): return [Update(17, **{message_type: message}).to_dict()] try: - monkeypatch.setattr(bot.request, 'post', post) + monkeypatch.setattr(bot.request, "post", post) bot.delete_webhook() # make sure there is no webhook set if webhook tests failed updates = bot.get_updates(timeout=1) @@ -2433,7 +2366,7 @@ def post(*args, **kwargs): message = updates[0][message_type] if self_sender: - assert message.reply_markup.inline_keyboard[0][0].callback_data == 'callback_data' + assert message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data" else: assert ( message.reply_markup.inline_keyboard[0][0].callback_data @@ -2445,13 +2378,13 @@ def post(*args, **kwargs): bot.callback_data_cache.clear_callback_queries() @pytest.mark.parametrize( - 'cls,warn', [(Bot, True), (BotSubClass, True), (ExtBot, False), (ExtBotSubClass, False)] + "cls,warn", [(Bot, True), (BotSubClass, True), (ExtBot, False), (ExtBotSubClass, False)] ) def test_defaults_warning(self, bot, recwarn, cls, warn): defaults = Defaults() cls(bot.token, defaults=defaults) if warn: assert len(recwarn) == 1 - assert 'Passing Defaults to telegram.Bot is deprecated.' in str(recwarn[-1].message) + assert "Passing Defaults to telegram.Bot is deprecated." in str(recwarn[-1].message) else: assert len(recwarn) == 0 diff --git a/tests/test_botcommand.py b/tests/test_botcommand.py index 1b750d99601..ec80734bfcd 100644 --- a/tests/test_botcommand.py +++ b/tests/test_botcommand.py @@ -24,23 +24,20 @@ @pytest.fixture(scope="class") def bot_command(): - return BotCommand(command='start', description='A command') + return BotCommand(command="start", description="A command") class TestBotCommand: - command = 'start' - description = 'A command' + command = "start" + description = "A command" - def test_slot_behaviour(self, bot_command, recwarn, mro_slots): + def test_slot_behaviour(self, bot_command, mro_slots): for attr in bot_command.__slots__: - assert getattr(bot_command, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not bot_command.__dict__, f"got missing slot(s): {bot_command.__dict__}" + assert getattr(bot_command, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(bot_command)) == len(set(mro_slots(bot_command))), "duplicate slot" - bot_command.custom, bot_command.command = 'should give warning', self.command - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): - json_dict = {'command': self.command, 'description': self.description} + json_dict = {"command": self.command, "description": self.description} bot_command = BotCommand.de_json(json_dict, bot) assert bot_command.command == self.command @@ -52,15 +49,15 @@ def test_to_dict(self, bot_command): bot_command_dict = bot_command.to_dict() assert isinstance(bot_command_dict, dict) - assert bot_command_dict['command'] == bot_command.command - assert bot_command_dict['description'] == bot_command.description + assert bot_command_dict["command"] == bot_command.command + assert bot_command_dict["description"] == bot_command.description def test_equality(self): - a = BotCommand('start', 'some description') - b = BotCommand('start', 'some description') - c = BotCommand('start', 'some other description') - d = BotCommand('hepl', 'some description') - e = Dice(4, 'emoji') + a = BotCommand("start", "some description") + b = BotCommand("start", "some description") + c = BotCommand("start", "some other description") + d = BotCommand("hepl", "some description") + e = Dice(4, "emoji") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_botcommandscope.py b/tests/test_botcommandscope.py index 25e5d5877b6..f2b6541a00e 100644 --- a/tests/test_botcommandscope.py +++ b/tests/test_botcommandscope.py @@ -33,10 +33,10 @@ ) -@pytest.fixture(scope="class", params=['str', 'int']) +@pytest.fixture(scope="class", params=["str", "int"]) def chat_id(request): - if request.param == 'str': - return '@supergroupusername' + if request.param == "str": + return "@supergroupusername" return 43 @@ -106,22 +106,19 @@ def scope_class_and_type(request): return request.param -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def bot_command_scope(scope_class_and_type, chat_id): return scope_class_and_type[0](type=scope_class_and_type[1], chat_id=chat_id, user_id=42) # All the scope types are very similar, so we test everything via parametrization class TestBotCommandScope: - def test_slot_behaviour(self, bot_command_scope, mro_slots, recwarn): + def test_slot_behaviour(self, bot_command_scope, mro_slots): for attr in bot_command_scope.__slots__: - assert getattr(bot_command_scope, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not bot_command_scope.__dict__, f"got missing slot(s): {bot_command_scope.__dict__}" + assert getattr(bot_command_scope, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(bot_command_scope)) == len( set(mro_slots(bot_command_scope)) ), "duplicate slot" - bot_command_scope.custom, bot_command_scope.type = 'warning!', bot_command_scope.type - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot, scope_class_and_type, chat_id): cls = scope_class_and_type[0] @@ -129,46 +126,46 @@ def test_de_json(self, bot, scope_class_and_type, chat_id): assert cls.de_json({}, bot) is None - json_dict = {'type': type_, 'chat_id': chat_id, 'user_id': 42} + json_dict = {"type": type_, "chat_id": chat_id, "user_id": 42} bot_command_scope = BotCommandScope.de_json(json_dict, bot) assert isinstance(bot_command_scope, BotCommandScope) assert isinstance(bot_command_scope, cls) assert bot_command_scope.type == type_ - if 'chat_id' in cls.__slots__: + if "chat_id" in cls.__slots__: assert bot_command_scope.chat_id == chat_id - if 'user_id' in cls.__slots__: + if "user_id" in cls.__slots__: assert bot_command_scope.user_id == 42 def test_de_json_invalid_type(self, bot): - json_dict = {'type': 'invalid', 'chat_id': chat_id, 'user_id': 42} + json_dict = {"type": "invalid", "chat_id": chat_id, "user_id": 42} bot_command_scope = BotCommandScope.de_json(json_dict, bot) assert type(bot_command_scope) is BotCommandScope - assert bot_command_scope.type == 'invalid' + assert bot_command_scope.type == "invalid" def test_de_json_subclass(self, scope_class, bot, chat_id): """This makes sure that e.g. BotCommandScopeDefault(data) never returns a BotCommandScopeChat instance.""" - json_dict = {'type': 'invalid', 'chat_id': chat_id, 'user_id': 42} + json_dict = {"type": "invalid", "chat_id": chat_id, "user_id": 42} assert type(scope_class.de_json(json_dict, bot)) is scope_class def test_to_dict(self, bot_command_scope): bot_command_scope_dict = bot_command_scope.to_dict() assert isinstance(bot_command_scope_dict, dict) - assert bot_command_scope['type'] == bot_command_scope.type - if hasattr(bot_command_scope, 'chat_id'): - assert bot_command_scope['chat_id'] == bot_command_scope.chat_id - if hasattr(bot_command_scope, 'user_id'): - assert bot_command_scope['user_id'] == bot_command_scope.user_id + assert bot_command_scope["type"] == bot_command_scope.type + if hasattr(bot_command_scope, "chat_id"): + assert bot_command_scope["chat_id"] == bot_command_scope.chat_id + if hasattr(bot_command_scope, "user_id"): + assert bot_command_scope["user_id"] == bot_command_scope.user_id def test_equality(self, bot_command_scope, bot): - a = BotCommandScope('base_type') - b = BotCommandScope('base_type') + a = BotCommandScope("base_type") + b = BotCommandScope("base_type") c = bot_command_scope d = deepcopy(bot_command_scope) - e = Dice(4, 'emoji') + e = Dice(4, "emoji") assert a == b assert hash(a) == hash(b) @@ -188,17 +185,17 @@ def test_equality(self, bot_command_scope, bot): assert c != e assert hash(c) != hash(e) - if hasattr(c, 'chat_id'): + if hasattr(c, "chat_id"): json_dict = c.to_dict() - json_dict['chat_id'] = 0 + json_dict["chat_id"] = 0 f = c.__class__.de_json(json_dict, bot) assert c != f assert hash(c) != hash(f) - if hasattr(c, 'user_id'): + if hasattr(c, "user_id"): json_dict = c.to_dict() - json_dict['user_id'] = 0 + json_dict["user_id"] = 0 g = c.__class__.de_json(json_dict, bot) assert c != g diff --git a/tests/test_callbackcontext.py b/tests/test_callbackcontext.py index 7e6b73b78f2..0a54fc5cb87 100644 --- a/tests/test_callbackcontext.py +++ b/tests/test_callbackcontext.py @@ -38,133 +38,129 @@ class TestCallbackContext: - def test_slot_behaviour(self, cdp, recwarn, mro_slots): - c = CallbackContext(cdp) + def test_slot_behaviour(self, dp, mro_slots, recwarn): + c = CallbackContext(dp) for attr in c.__slots__: - assert getattr(c, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert getattr(c, attr, "err") != "err", f"got extra slot '{attr}'" assert not c.__dict__, f"got missing slot(s): {c.__dict__}" assert len(mro_slots(c)) == len(set(mro_slots(c))), "duplicate slot" c.args = c.args assert len(recwarn) == 0, recwarn.list - def test_non_context_dp(self, dp): - with pytest.raises(ValueError): - CallbackContext(dp) + def test_from_job(self, dp): + job = dp.job_queue.run_once(lambda x: x, 10) - def test_from_job(self, cdp): - job = cdp.job_queue.run_once(lambda x: x, 10) - - callback_context = CallbackContext.from_job(job, cdp) + callback_context = CallbackContext.from_job(job, dp) assert callback_context.job is job assert callback_context.chat_data is None assert callback_context.user_data is None - assert callback_context.bot_data is cdp.bot_data - assert callback_context.bot is cdp.bot - assert callback_context.job_queue is cdp.job_queue - assert callback_context.update_queue is cdp.update_queue + assert callback_context.bot_data is dp.bot_data + assert callback_context.bot is dp.bot + assert callback_context.job_queue is dp.job_queue + assert callback_context.update_queue is dp.update_queue - def test_from_update(self, cdp): + def test_from_update(self, dp): update = Update( - 0, message=Message(0, None, Chat(1, 'chat'), from_user=User(1, 'user', False)) + 0, message=Message(0, None, Chat(1, "chat"), from_user=User(1, "user", False)) ) - callback_context = CallbackContext.from_update(update, cdp) + callback_context = CallbackContext.from_update(update, dp) assert callback_context.chat_data == {} assert callback_context.user_data == {} - assert callback_context.bot_data is cdp.bot_data - assert callback_context.bot is cdp.bot - assert callback_context.job_queue is cdp.job_queue - assert callback_context.update_queue is cdp.update_queue + assert callback_context.bot_data is dp.bot_data + assert callback_context.bot is dp.bot + assert callback_context.job_queue is dp.job_queue + assert callback_context.update_queue is dp.update_queue - callback_context_same_user_chat = CallbackContext.from_update(update, cdp) + callback_context_same_user_chat = CallbackContext.from_update(update, dp) - callback_context.bot_data['test'] = 'bot' - callback_context.chat_data['test'] = 'chat' - callback_context.user_data['test'] = 'user' + callback_context.bot_data["test"] = "bot" + callback_context.chat_data["test"] = "chat" + callback_context.user_data["test"] = "user" assert callback_context_same_user_chat.bot_data is callback_context.bot_data assert callback_context_same_user_chat.chat_data is callback_context.chat_data assert callback_context_same_user_chat.user_data is callback_context.user_data update_other_user_chat = Update( - 0, message=Message(0, None, Chat(2, 'chat'), from_user=User(2, 'user', False)) + 0, message=Message(0, None, Chat(2, "chat"), from_user=User(2, "user", False)) ) - callback_context_other_user_chat = CallbackContext.from_update(update_other_user_chat, cdp) + callback_context_other_user_chat = CallbackContext.from_update(update_other_user_chat, dp) assert callback_context_other_user_chat.bot_data is callback_context.bot_data assert callback_context_other_user_chat.chat_data is not callback_context.chat_data assert callback_context_other_user_chat.user_data is not callback_context.user_data - def test_from_update_not_update(self, cdp): - callback_context = CallbackContext.from_update(None, cdp) + def test_from_update_not_update(self, dp): + callback_context = CallbackContext.from_update(None, dp) assert callback_context.chat_data is None assert callback_context.user_data is None - assert callback_context.bot_data is cdp.bot_data - assert callback_context.bot is cdp.bot - assert callback_context.job_queue is cdp.job_queue - assert callback_context.update_queue is cdp.update_queue + assert callback_context.bot_data is dp.bot_data + assert callback_context.bot is dp.bot + assert callback_context.job_queue is dp.job_queue + assert callback_context.update_queue is dp.update_queue - callback_context = CallbackContext.from_update('', cdp) + callback_context = CallbackContext.from_update("", dp) assert callback_context.chat_data is None assert callback_context.user_data is None - assert callback_context.bot_data is cdp.bot_data - assert callback_context.bot is cdp.bot - assert callback_context.job_queue is cdp.job_queue - assert callback_context.update_queue is cdp.update_queue + assert callback_context.bot_data is dp.bot_data + assert callback_context.bot is dp.bot + assert callback_context.job_queue is dp.job_queue + assert callback_context.update_queue is dp.update_queue - def test_from_error(self, cdp): - error = TelegramError('test') + def test_from_error(self, dp): + error = TelegramError("test") update = Update( - 0, message=Message(0, None, Chat(1, 'chat'), from_user=User(1, 'user', False)) + 0, message=Message(0, None, Chat(1, "chat"), from_user=User(1, "user", False)) ) - callback_context = CallbackContext.from_error(update, error, cdp) + callback_context = CallbackContext.from_error(update, error, dp) assert callback_context.error is error assert callback_context.chat_data == {} assert callback_context.user_data == {} - assert callback_context.bot_data is cdp.bot_data - assert callback_context.bot is cdp.bot - assert callback_context.job_queue is cdp.job_queue - assert callback_context.update_queue is cdp.update_queue + assert callback_context.bot_data is dp.bot_data + assert callback_context.bot is dp.bot + assert callback_context.job_queue is dp.job_queue + assert callback_context.update_queue is dp.update_queue assert callback_context.async_args is None assert callback_context.async_kwargs is None - def test_from_error_async_params(self, cdp): - error = TelegramError('test') + def test_from_error_async_params(self, dp): + error = TelegramError("test") - args = [1, '2'] - kwargs = {'one': 1, 2: 'two'} + args = [1, "2"] + kwargs = {"one": 1, 2: "two"} callback_context = CallbackContext.from_error( - None, error, cdp, async_args=args, async_kwargs=kwargs + None, error, dp, async_args=args, async_kwargs=kwargs ) assert callback_context.error is error assert callback_context.async_args is args assert callback_context.async_kwargs is kwargs - def test_match(self, cdp): - callback_context = CallbackContext(cdp) + def test_match(self, dp): + callback_context = CallbackContext(dp) assert callback_context.match is None - callback_context.matches = ['test', 'blah'] + callback_context.matches = ["test", "blah"] - assert callback_context.match == 'test' + assert callback_context.match == "test" - def test_data_assignment(self, cdp): + def test_data_assignment(self, dp): update = Update( - 0, message=Message(0, None, Chat(1, 'chat'), from_user=User(1, 'user', False)) + 0, message=Message(0, None, Chat(1, "chat"), from_user=User(1, "user", False)) ) - callback_context = CallbackContext.from_update(update, cdp) + callback_context = CallbackContext.from_update(update, dp) with pytest.raises(AttributeError): callback_context.bot_data = {"test": 123} @@ -173,60 +169,60 @@ def test_data_assignment(self, cdp): with pytest.raises(AttributeError): callback_context.chat_data = "test" - def test_dispatcher_attribute(self, cdp): - callback_context = CallbackContext(cdp) - assert callback_context.dispatcher == cdp + def test_dispatcher_attribute(self, dp): + callback_context = CallbackContext(dp) + assert callback_context.dispatcher == dp - def test_drop_callback_data_exception(self, bot, cdp): + def test_drop_callback_data_exception(self, bot, dp): non_ext_bot = Bot(bot.token) update = Update( - 0, message=Message(0, None, Chat(1, 'chat'), from_user=User(1, 'user', False)) + 0, message=Message(0, None, Chat(1, "chat"), from_user=User(1, "user", False)) ) - callback_context = CallbackContext.from_update(update, cdp) + callback_context = CallbackContext.from_update(update, dp) - with pytest.raises(RuntimeError, match='This telegram.ext.ExtBot instance does not'): + with pytest.raises(RuntimeError, match="This telegram.ext.ExtBot instance does not"): callback_context.drop_callback_data(None) try: - cdp.bot = non_ext_bot - with pytest.raises(RuntimeError, match='telegram.Bot does not allow for'): + dp.bot = non_ext_bot + with pytest.raises(RuntimeError, match="telegram.Bot does not allow for"): callback_context.drop_callback_data(None) finally: - cdp.bot = bot + dp.bot = bot - def test_drop_callback_data(self, cdp, monkeypatch, chat_id): - monkeypatch.setattr(cdp.bot, 'arbitrary_callback_data', True) + def test_drop_callback_data(self, dp, monkeypatch, chat_id): + monkeypatch.setattr(dp.bot, "arbitrary_callback_data", True) update = Update( - 0, message=Message(0, None, Chat(1, 'chat'), from_user=User(1, 'user', False)) + 0, message=Message(0, None, Chat(1, "chat"), from_user=User(1, "user", False)) ) - callback_context = CallbackContext.from_update(update, cdp) - cdp.bot.send_message( + callback_context = CallbackContext.from_update(update, dp) + dp.bot.send_message( chat_id=chat_id, - text='test', + text="test", reply_markup=InlineKeyboardMarkup.from_button( - InlineKeyboardButton('test', callback_data='callback_data') + InlineKeyboardButton("test", callback_data="callback_data") ), ) - keyboard_uuid = cdp.bot.callback_data_cache.persistence_data[0][0][0] - button_uuid = list(cdp.bot.callback_data_cache.persistence_data[0][0][2])[0] + keyboard_uuid = dp.bot.callback_data_cache.persistence_data[0][0][0] + button_uuid = list(dp.bot.callback_data_cache.persistence_data[0][0][2])[0] callback_data = keyboard_uuid + button_uuid callback_query = CallbackQuery( - id='1', + id="1", from_user=None, chat_instance=None, data=callback_data, ) - cdp.bot.callback_data_cache.process_callback_query(callback_query) + dp.bot.callback_data_cache.process_callback_query(callback_query) try: - assert len(cdp.bot.callback_data_cache.persistence_data[0]) == 1 - assert list(cdp.bot.callback_data_cache.persistence_data[1]) == ['1'] + assert len(dp.bot.callback_data_cache.persistence_data[0]) == 1 + assert list(dp.bot.callback_data_cache.persistence_data[1]) == ["1"] callback_context.drop_callback_data(callback_query) - assert cdp.bot.callback_data_cache.persistence_data == ([], {}) + assert dp.bot.callback_data_cache.persistence_data == ([], {}) finally: - cdp.bot.callback_data_cache.clear_callback_data() - cdp.bot.callback_data_cache.clear_callback_queries() + dp.bot.callback_data_cache.clear_callback_data() + dp.bot.callback_data_cache.clear_callback_queries() diff --git a/tests/test_callbackdatacache.py b/tests/test_callbackdatacache.py index 318071328d0..a5edcf0bf11 100644 --- a/tests/test_callbackdatacache.py +++ b/tests/test_callbackdatacache.py @@ -32,33 +32,29 @@ ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def callback_data_cache(bot): return CallbackDataCache(bot) class TestInvalidCallbackData: - def test_slot_behaviour(self, mro_slots, recwarn): + def test_slot_behaviour(self, mro_slots): invalid_callback_data = InvalidCallbackData() for attr in invalid_callback_data.__slots__: - assert getattr(invalid_callback_data, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert getattr(invalid_callback_data, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(invalid_callback_data)) == len( set(mro_slots(invalid_callback_data)) ), "duplicate slot" - with pytest.raises(AttributeError): - invalid_callback_data.custom class TestKeyboardData: def test_slot_behaviour(self, mro_slots): - keyboard_data = _KeyboardData('uuid') + keyboard_data = _KeyboardData("uuid") for attr in keyboard_data.__slots__: - assert getattr(keyboard_data, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert getattr(keyboard_data, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(keyboard_data)) == len( set(mro_slots(keyboard_data)) ), "duplicate slot" - with pytest.raises(AttributeError): - keyboard_data.custom = 42 class TestCallbackDataCache: @@ -66,17 +62,15 @@ def test_slot_behaviour(self, callback_data_cache, mro_slots): for attr in callback_data_cache.__slots__: attr = ( f"_CallbackDataCache{attr}" - if attr.startswith('__') and not attr.endswith('__') + if attr.startswith("__") and not attr.endswith("__") else attr ) - assert getattr(callback_data_cache, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert getattr(callback_data_cache, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(callback_data_cache)) == len( set(mro_slots(callback_data_cache)) ), "duplicate slot" - with pytest.raises(AttributeError): - callback_data_cache.custom = 42 - @pytest.mark.parametrize('maxsize', [1, 5, 2048]) + @pytest.mark.parametrize("maxsize", [1, 5, 2048]) def test_init_maxsize(self, maxsize, bot): assert CallbackDataCache(bot).maxsize == 1024 cdc = CallbackDataCache(bot, maxsize=maxsize) @@ -84,23 +78,23 @@ def test_init_maxsize(self, maxsize, bot): assert cdc.bot is bot def test_init_and_access__persistent_data(self, bot): - keyboard_data = _KeyboardData('123', 456, {'button': 678}) - persistent_data = ([keyboard_data.to_tuple()], {'id': '123'}) + keyboard_data = _KeyboardData("123", 456, {"button": 678}) + persistent_data = ([keyboard_data.to_tuple()], {"id": "123"}) cdc = CallbackDataCache(bot, persistent_data=persistent_data) assert cdc.maxsize == 1024 - assert dict(cdc._callback_queries) == {'id': '123'} - assert list(cdc._keyboard_data.keys()) == ['123'] - assert cdc._keyboard_data['123'].keyboard_uuid == '123' - assert cdc._keyboard_data['123'].access_time == 456 - assert cdc._keyboard_data['123'].button_data == {'button': 678} + assert dict(cdc._callback_queries) == {"id": "123"} + assert list(cdc._keyboard_data.keys()) == ["123"] + assert cdc._keyboard_data["123"].keyboard_uuid == "123" + assert cdc._keyboard_data["123"].access_time == 456 + assert cdc._keyboard_data["123"].button_data == {"button": 678} assert cdc.persistence_data == persistent_data def test_process_keyboard(self, callback_data_cache): - changing_button_1 = InlineKeyboardButton('changing', callback_data='some data 1') - changing_button_2 = InlineKeyboardButton('changing', callback_data='some data 2') - non_changing_button = InlineKeyboardButton('non-changing', url='https://ptb.org') + changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") + changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") + non_changing_button = InlineKeyboardButton("non-changing", url="https://ptb.org") reply_markup = InlineKeyboardMarkup.from_row( [non_changing_button, changing_button_1, changing_button_2] ) @@ -118,23 +112,23 @@ def test_process_keyboard(self, callback_data_cache): ) assert keyboard_1 == keyboard_2 assert ( - callback_data_cache._keyboard_data[keyboard_1].button_data[button_1] == 'some data 1' + callback_data_cache._keyboard_data[keyboard_1].button_data[button_1] == "some data 1" ) assert ( - callback_data_cache._keyboard_data[keyboard_2].button_data[button_2] == 'some data 2' + callback_data_cache._keyboard_data[keyboard_2].button_data[button_2] == "some data 2" ) def test_process_keyboard_no_changing_button(self, callback_data_cache): reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton('non-changing', url='https://ptb.org') + InlineKeyboardButton("non-changing", url="https://ptb.org") ) assert callback_data_cache.process_keyboard(reply_markup) is reply_markup def test_process_keyboard_full(self, bot): cdc = CallbackDataCache(bot, maxsize=1) - changing_button_1 = InlineKeyboardButton('changing', callback_data='some data 1') - changing_button_2 = InlineKeyboardButton('changing', callback_data='some data 2') - non_changing_button = InlineKeyboardButton('non-changing', url='https://ptb.org') + changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") + changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") + non_changing_button = InlineKeyboardButton("non-changing", url="https://ptb.org") reply_markup = InlineKeyboardMarkup.from_row( [non_changing_button, changing_button_1, changing_button_2] ) @@ -149,14 +143,14 @@ def test_process_keyboard_full(self, bot): assert cdc.persistence_data[0][0][0] != keyboard_1 assert cdc.persistence_data[0][0][0] == keyboard_2 - @pytest.mark.parametrize('data', [True, False]) - @pytest.mark.parametrize('message', [True, False]) - @pytest.mark.parametrize('invalid', [True, False]) + @pytest.mark.parametrize("data", [True, False]) + @pytest.mark.parametrize("message", [True, False]) + @pytest.mark.parametrize("invalid", [True, False]) def test_process_callback_query(self, callback_data_cache, data, message, invalid): """This also tests large parts of process_message""" - changing_button_1 = InlineKeyboardButton('changing', callback_data='some data 1') - changing_button_2 = InlineKeyboardButton('changing', callback_data='some data 2') - non_changing_button = InlineKeyboardButton('non-changing', url='https://ptb.org') + changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") + changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") + non_changing_button = InlineKeyboardButton("non-changing", url="https://ptb.org") reply_markup = InlineKeyboardMarkup.from_row( [non_changing_button, changing_button_1, changing_button_2] ) @@ -182,7 +176,7 @@ def test_process_callback_query(self, callback_data_cache, data, message, invali if not invalid: if data: - assert callback_query.data == 'some data 1' + assert callback_query.data == "some data 1" # make sure that we stored the mapping CallbackQuery.id -> keyboard_uuid correctly assert len(callback_data_cache._keyboard_data) == 1 assert ( @@ -218,13 +212,13 @@ def test_process_callback_query(self, callback_data_cache, data, message, invali InvalidCallbackData, ) - @pytest.mark.parametrize('pass_from_user', [True, False]) - @pytest.mark.parametrize('pass_via_bot', [True, False]) + @pytest.mark.parametrize("pass_from_user", [True, False]) + @pytest.mark.parametrize("pass_via_bot", [True, False]) def test_process_message_wrong_sender(self, pass_from_user, pass_via_bot, callback_data_cache): reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton('test', callback_data='callback_data') + InlineKeyboardButton("test", callback_data="callback_data") ) - user = User(1, 'first', False) + user = User(1, "first", False) message = Message( 1, None, @@ -236,21 +230,21 @@ def test_process_message_wrong_sender(self, pass_from_user, pass_via_bot, callba callback_data_cache.process_message(message) if pass_from_user or pass_via_bot: # Here we can determine that the message is not from our bot, so no replacing - assert message.reply_markup.inline_keyboard[0][0].callback_data == 'callback_data' + assert message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data" else: # Here we have no chance to know, so InvalidCallbackData assert isinstance( message.reply_markup.inline_keyboard[0][0].callback_data, InvalidCallbackData ) - @pytest.mark.parametrize('pass_from_user', [True, False]) + @pytest.mark.parametrize("pass_from_user", [True, False]) def test_process_message_inline_mode(self, pass_from_user, callback_data_cache): """Check that via_bot tells us correctly that our bot sent the message, even if from_user is not our bot.""" reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton('test', callback_data='callback_data') + InlineKeyboardButton("test", callback_data="callback_data") ) - user = User(1, 'first', False) + user = User(1, "first", False) message = Message( 1, None, @@ -261,7 +255,7 @@ def test_process_message_inline_mode(self, pass_from_user, callback_data_cache): ) callback_data_cache.process_message(message) # Here we can determine that the message is not from our bot, so no replacing - assert message.reply_markup.inline_keyboard[0][0].callback_data == 'callback_data' + assert message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data" def test_process_message_no_reply_markup(self, callback_data_cache): message = Message(1, None, None) @@ -269,13 +263,13 @@ def test_process_message_no_reply_markup(self, callback_data_cache): assert message.reply_markup is None def test_drop_data(self, callback_data_cache): - changing_button_1 = InlineKeyboardButton('changing', callback_data='some data 1') - changing_button_2 = InlineKeyboardButton('changing', callback_data='some data 2') + changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") + changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") reply_markup = InlineKeyboardMarkup.from_row([changing_button_1, changing_button_2]) out = callback_data_cache.process_keyboard(reply_markup) callback_query = CallbackQuery( - '1', + "1", from_user=None, chat_instance=None, data=out.inline_keyboard[0][1].callback_data, @@ -290,19 +284,19 @@ def test_drop_data(self, callback_data_cache): assert len(callback_data_cache.persistence_data[0]) == 0 def test_drop_data_missing_data(self, callback_data_cache): - changing_button_1 = InlineKeyboardButton('changing', callback_data='some data 1') - changing_button_2 = InlineKeyboardButton('changing', callback_data='some data 2') + changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") + changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") reply_markup = InlineKeyboardMarkup.from_row([changing_button_1, changing_button_2]) out = callback_data_cache.process_keyboard(reply_markup) callback_query = CallbackQuery( - '1', + "1", from_user=None, chat_instance=None, data=out.inline_keyboard[0][1].callback_data, ) - with pytest.raises(KeyError, match='CallbackQuery was not found in cache.'): + with pytest.raises(KeyError, match="CallbackQuery was not found in cache."): callback_data_cache.drop_data(callback_query) callback_data_cache.process_callback_query(callback_query) @@ -310,10 +304,10 @@ def test_drop_data_missing_data(self, callback_data_cache): callback_data_cache.drop_data(callback_query) assert callback_data_cache.persistence_data == ([], {}) - @pytest.mark.parametrize('method', ('callback_data', 'callback_queries')) + @pytest.mark.parametrize("method", ("callback_data", "callback_queries")) def test_clear_all(self, callback_data_cache, method): - changing_button_1 = InlineKeyboardButton('changing', callback_data='some data 1') - changing_button_2 = InlineKeyboardButton('changing', callback_data='some data 2') + changing_button_1 = InlineKeyboardButton("changing", callback_data="some data 1") + changing_button_2 = InlineKeyboardButton("changing", callback_data="some data 2") reply_markup = InlineKeyboardMarkup.from_row([changing_button_1, changing_button_2]) for i in range(100): @@ -326,7 +320,7 @@ def test_clear_all(self, callback_data_cache, method): ) callback_data_cache.process_callback_query(callback_query) - if method == 'callback_data': + if method == "callback_data": callback_data_cache.clear_callback_data() # callback_data was cleared, callback_queries weren't assert len(callback_data_cache.persistence_data[0]) == 0 @@ -337,12 +331,12 @@ def test_clear_all(self, callback_data_cache, method): assert len(callback_data_cache.persistence_data[0]) == 100 assert len(callback_data_cache.persistence_data[1]) == 0 - @pytest.mark.parametrize('time_method', ['time', 'datetime', 'defaults']) + @pytest.mark.parametrize("time_method", ["time", "datetime", "defaults"]) def test_clear_cutoff(self, callback_data_cache, time_method, tz_bot): # Fill the cache with some fake data for i in range(50): reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton('changing', callback_data=str(i)) + InlineKeyboardButton("changing", callback_data=str(i)) ) out = callback_data_cache.process_keyboard(reply_markup) callback_query = CallbackQuery( @@ -355,9 +349,9 @@ def test_clear_cutoff(self, callback_data_cache, time_method, tz_bot): # sleep a bit before saving the time cutoff, to make test more reliable time.sleep(0.1) - if time_method == 'time': + if time_method == "time": cutoff = time.time() - elif time_method == 'datetime': + elif time_method == "datetime": cutoff = datetime.now(pytz.utc) else: cutoff = datetime.now(tz_bot.defaults.tzinfo).replace(tzinfo=None) @@ -367,7 +361,7 @@ def test_clear_cutoff(self, callback_data_cache, time_method, tz_bot): # more fake data after the time cutoff for i in range(50, 100): reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton('changing', callback_data=str(i)) + InlineKeyboardButton("changing", callback_data=str(i)) ) out = callback_data_cache.process_keyboard(reply_markup) callback_query = CallbackQuery( diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py index 56aede6708b..712472cc70a 100644 --- a/tests/test_callbackquery.py +++ b/tests/test_callbackquery.py @@ -23,7 +23,7 @@ from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling -@pytest.fixture(scope='function', params=['message', 'inline']) +@pytest.fixture(scope="function", params=["message", "inline"]) def callback_query(bot, request): cbq = CallbackQuery( TestCallbackQuery.id_, @@ -33,7 +33,7 @@ def callback_query(bot, request): game_short_name=TestCallbackQuery.game_short_name, bot=bot, ) - if request.param == 'message': + if request.param == "message": cbq.message = TestCallbackQuery.message cbq.message.bot = bot else: @@ -42,55 +42,52 @@ def callback_query(bot, request): class TestCallbackQuery: - id_ = 'id' - from_user = User(1, 'test_user', False) - chat_instance = 'chat_instance' - message = Message(3, None, Chat(4, 'private'), from_user=User(5, 'bot', False)) - data = 'data' - inline_message_id = 'inline_message_id' - game_short_name = 'the_game' - - def test_slot_behaviour(self, callback_query, recwarn, mro_slots): + id_ = "id" + from_user = User(1, "test_user", False) + chat_instance = "chat_instance" + message = Message(3, None, Chat(4, "private"), from_user=User(5, "bot", False)) + data = "data" + inline_message_id = "inline_message_id" + game_short_name = "the_game" + + def test_slot_behaviour(self, callback_query, mro_slots): for attr in callback_query.__slots__: - assert getattr(callback_query, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not callback_query.__dict__, f"got missing slot(s): {callback_query.__dict__}" + assert getattr(callback_query, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(callback_query)) == len(set(mro_slots(callback_query))), "same slot" - callback_query.custom, callback_query.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @staticmethod def skip_params(callback_query: CallbackQuery): if callback_query.inline_message_id: - return {'message_id', 'chat_id'} - return {'inline_message_id'} + return {"message_id", "chat_id"} + return {"inline_message_id"} @staticmethod def shortcut_kwargs(callback_query: CallbackQuery): if not callback_query.inline_message_id: - return {'message_id', 'chat_id'} - return {'inline_message_id'} + return {"message_id", "chat_id"} + return {"inline_message_id"} @staticmethod def check_passed_ids(callback_query: CallbackQuery, kwargs): if callback_query.inline_message_id: - id_ = kwargs['inline_message_id'] == callback_query.inline_message_id - chat_id = kwargs['chat_id'] is None - message_id = kwargs['message_id'] is None + id_ = kwargs["inline_message_id"] == callback_query.inline_message_id + chat_id = kwargs["chat_id"] is None + message_id = kwargs["message_id"] is None else: - id_ = kwargs['inline_message_id'] is None - chat_id = kwargs['chat_id'] == callback_query.message.chat_id - message_id = kwargs['message_id'] == callback_query.message.message_id + id_ = kwargs["inline_message_id"] is None + chat_id = kwargs["chat_id"] == callback_query.message.chat_id + message_id = kwargs["message_id"] == callback_query.message.message_id return id_ and chat_id and message_id def test_de_json(self, bot): json_dict = { - 'id': self.id_, - 'from': self.from_user.to_dict(), - 'chat_instance': self.chat_instance, - 'message': self.message.to_dict(), - 'data': self.data, - 'inline_message_id': self.inline_message_id, - 'game_short_name': self.game_short_name, + "id": self.id_, + "from": self.from_user.to_dict(), + "chat_instance": self.chat_instance, + "message": self.message.to_dict(), + "data": self.data, + "inline_message_id": self.inline_message_id, + "game_short_name": self.game_short_name, } callback_query = CallbackQuery.de_json(json_dict, bot) @@ -106,98 +103,98 @@ def test_to_dict(self, callback_query): callback_query_dict = callback_query.to_dict() assert isinstance(callback_query_dict, dict) - assert callback_query_dict['id'] == callback_query.id - assert callback_query_dict['from'] == callback_query.from_user.to_dict() - assert callback_query_dict['chat_instance'] == callback_query.chat_instance + assert callback_query_dict["id"] == callback_query.id + assert callback_query_dict["from"] == callback_query.from_user.to_dict() + assert callback_query_dict["chat_instance"] == callback_query.chat_instance if callback_query.message: - assert callback_query_dict['message'] == callback_query.message.to_dict() + assert callback_query_dict["message"] == callback_query.message.to_dict() else: - assert callback_query_dict['inline_message_id'] == callback_query.inline_message_id - assert callback_query_dict['data'] == callback_query.data - assert callback_query_dict['game_short_name'] == callback_query.game_short_name + assert callback_query_dict["inline_message_id"] == callback_query.inline_message_id + assert callback_query_dict["data"] == callback_query.data + assert callback_query_dict["game_short_name"] == callback_query.game_short_name def test_answer(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - return kwargs['callback_query_id'] == callback_query.id + return kwargs["callback_query_id"] == callback_query.id assert check_shortcut_signature( - CallbackQuery.answer, Bot.answer_callback_query, ['callback_query_id'], [] + CallbackQuery.answer, Bot.answer_callback_query, ["callback_query_id"], [] ) assert check_shortcut_call( - callback_query.answer, callback_query.bot, 'answer_callback_query' + callback_query.answer, callback_query.bot, "answer_callback_query" ) assert check_defaults_handling(callback_query.answer, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'answer_callback_query', make_assertion) + monkeypatch.setattr(callback_query.bot, "answer_callback_query", make_assertion) # TODO: PEP8 assert callback_query.answer() def test_edit_message_text(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - text = kwargs['text'] == 'test' + text = kwargs["text"] == "test" ids = self.check_passed_ids(callback_query, kwargs) return ids and text assert check_shortcut_signature( CallbackQuery.edit_message_text, Bot.edit_message_text, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.edit_message_text, callback_query.bot, - 'edit_message_text', + "edit_message_text", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) assert check_defaults_handling(callback_query.edit_message_text, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'edit_message_text', make_assertion) - assert callback_query.edit_message_text(text='test') - assert callback_query.edit_message_text('test') + monkeypatch.setattr(callback_query.bot, "edit_message_text", make_assertion) + assert callback_query.edit_message_text(text="test") + assert callback_query.edit_message_text("test") def test_edit_message_caption(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - caption = kwargs['caption'] == 'new caption' + caption = kwargs["caption"] == "new caption" ids = self.check_passed_ids(callback_query, kwargs) return ids and caption assert check_shortcut_signature( CallbackQuery.edit_message_caption, Bot.edit_message_caption, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.edit_message_caption, callback_query.bot, - 'edit_message_caption', + "edit_message_caption", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) assert check_defaults_handling(callback_query.edit_message_caption, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'edit_message_caption', make_assertion) - assert callback_query.edit_message_caption(caption='new caption') - assert callback_query.edit_message_caption('new caption') + monkeypatch.setattr(callback_query.bot, "edit_message_caption", make_assertion) + assert callback_query.edit_message_caption(caption="new caption") + assert callback_query.edit_message_caption("new caption") def test_edit_message_reply_markup(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - reply_markup = kwargs['reply_markup'] == [['1', '2']] + reply_markup = kwargs["reply_markup"] == [["1", "2"]] ids = self.check_passed_ids(callback_query, kwargs) return ids and reply_markup assert check_shortcut_signature( CallbackQuery.edit_message_reply_markup, Bot.edit_message_reply_markup, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.edit_message_reply_markup, callback_query.bot, - 'edit_message_reply_markup', + "edit_message_reply_markup", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) @@ -205,52 +202,52 @@ def make_assertion(*_, **kwargs): callback_query.edit_message_reply_markup, callback_query.bot ) - monkeypatch.setattr(callback_query.bot, 'edit_message_reply_markup', make_assertion) - assert callback_query.edit_message_reply_markup(reply_markup=[['1', '2']]) - assert callback_query.edit_message_reply_markup([['1', '2']]) + monkeypatch.setattr(callback_query.bot, "edit_message_reply_markup", make_assertion) + assert callback_query.edit_message_reply_markup(reply_markup=[["1", "2"]]) + assert callback_query.edit_message_reply_markup([["1", "2"]]) def test_edit_message_media(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - message_media = kwargs.get('media') == [['1', '2']] + message_media = kwargs.get("media") == [["1", "2"]] ids = self.check_passed_ids(callback_query, kwargs) return ids and message_media assert check_shortcut_signature( CallbackQuery.edit_message_media, Bot.edit_message_media, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.edit_message_media, callback_query.bot, - 'edit_message_media', + "edit_message_media", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) assert check_defaults_handling(callback_query.edit_message_media, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'edit_message_media', make_assertion) - assert callback_query.edit_message_media(media=[['1', '2']]) - assert callback_query.edit_message_media([['1', '2']]) + monkeypatch.setattr(callback_query.bot, "edit_message_media", make_assertion) + assert callback_query.edit_message_media(media=[["1", "2"]]) + assert callback_query.edit_message_media([["1", "2"]]) def test_edit_message_live_location(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - latitude = kwargs.get('latitude') == 1 - longitude = kwargs.get('longitude') == 2 + latitude = kwargs.get("latitude") == 1 + longitude = kwargs.get("longitude") == 2 ids = self.check_passed_ids(callback_query, kwargs) return ids and latitude and longitude assert check_shortcut_signature( CallbackQuery.edit_message_live_location, Bot.edit_message_live_location, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.edit_message_live_location, callback_query.bot, - 'edit_message_live_location', + "edit_message_live_location", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) @@ -258,7 +255,7 @@ def make_assertion(*_, **kwargs): callback_query.edit_message_live_location, callback_query.bot ) - monkeypatch.setattr(callback_query.bot, 'edit_message_live_location', make_assertion) + monkeypatch.setattr(callback_query.bot, "edit_message_live_location", make_assertion) assert callback_query.edit_message_live_location(latitude=1, longitude=2) assert callback_query.edit_message_live_location(1, 2) @@ -270,13 +267,13 @@ def make_assertion(*_, **kwargs): assert check_shortcut_signature( CallbackQuery.stop_message_live_location, Bot.stop_message_live_location, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.stop_message_live_location, callback_query.bot, - 'stop_message_live_location', + "stop_message_live_location", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) @@ -284,57 +281,57 @@ def make_assertion(*_, **kwargs): callback_query.stop_message_live_location, callback_query.bot ) - monkeypatch.setattr(callback_query.bot, 'stop_message_live_location', make_assertion) + monkeypatch.setattr(callback_query.bot, "stop_message_live_location", make_assertion) assert callback_query.stop_message_live_location() def test_set_game_score(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - user_id = kwargs.get('user_id') == 1 - score = kwargs.get('score') == 2 + user_id = kwargs.get("user_id") == 1 + score = kwargs.get("score") == 2 ids = self.check_passed_ids(callback_query, kwargs) return ids and user_id and score assert check_shortcut_signature( CallbackQuery.set_game_score, Bot.set_game_score, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.set_game_score, callback_query.bot, - 'set_game_score', + "set_game_score", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) assert check_defaults_handling(callback_query.set_game_score, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'set_game_score', make_assertion) + monkeypatch.setattr(callback_query.bot, "set_game_score", make_assertion) assert callback_query.set_game_score(user_id=1, score=2) assert callback_query.set_game_score(1, 2) def test_get_game_high_scores(self, monkeypatch, callback_query): def make_assertion(*_, **kwargs): - user_id = kwargs.get('user_id') == 1 + user_id = kwargs.get("user_id") == 1 ids = self.check_passed_ids(callback_query, kwargs) return ids and user_id assert check_shortcut_signature( CallbackQuery.get_game_high_scores, Bot.get_game_high_scores, - ['inline_message_id', 'message_id', 'chat_id'], + ["inline_message_id", "message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.get_game_high_scores, callback_query.bot, - 'get_game_high_scores', + "get_game_high_scores", skip_params=self.skip_params(callback_query), shortcut_kwargs=self.shortcut_kwargs(callback_query), ) assert check_defaults_handling(callback_query.get_game_high_scores, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'get_game_high_scores', make_assertion) + monkeypatch.setattr(callback_query.bot, "get_game_high_scores", make_assertion) assert callback_query.get_game_high_scores(user_id=1) assert callback_query.get_game_high_scores(1) @@ -343,22 +340,22 @@ def test_delete_message(self, monkeypatch, callback_query): pytest.skip("Can't delete inline messages") def make_assertion(*args, **kwargs): - id_ = kwargs['chat_id'] == callback_query.message.chat_id - message = kwargs['message_id'] == callback_query.message.message_id + id_ = kwargs["chat_id"] == callback_query.message.chat_id + message = kwargs["message_id"] == callback_query.message.message_id return id_ and message assert check_shortcut_signature( CallbackQuery.delete_message, Bot.delete_message, - ['message_id', 'chat_id'], + ["message_id", "chat_id"], [], ) assert check_shortcut_call( - callback_query.delete_message, callback_query.bot, 'delete_message' + callback_query.delete_message, callback_query.bot, "delete_message" ) assert check_defaults_handling(callback_query.delete_message, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'delete_message', make_assertion) + monkeypatch.setattr(callback_query.bot, "delete_message", make_assertion) assert callback_query.delete_message() def test_pin_message(self, monkeypatch, callback_query): @@ -366,20 +363,20 @@ def test_pin_message(self, monkeypatch, callback_query): pytest.skip("Can't pin inline messages") def make_assertion(*args, **kwargs): - return kwargs['chat_id'] == callback_query.message.chat_id + return kwargs["chat_id"] == callback_query.message.chat_id assert check_shortcut_signature( CallbackQuery.pin_message, Bot.pin_chat_message, - ['message_id', 'chat_id'], + ["message_id", "chat_id"], [], ) assert check_shortcut_call( - callback_query.pin_message, callback_query.bot, 'pin_chat_message' + callback_query.pin_message, callback_query.bot, "pin_chat_message" ) assert check_defaults_handling(callback_query.pin_message, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'pin_chat_message', make_assertion) + monkeypatch.setattr(callback_query.bot, "pin_chat_message", make_assertion) assert callback_query.pin_message() def test_unpin_message(self, monkeypatch, callback_query): @@ -387,23 +384,23 @@ def test_unpin_message(self, monkeypatch, callback_query): pytest.skip("Can't unpin inline messages") def make_assertion(*args, **kwargs): - return kwargs['chat_id'] == callback_query.message.chat_id + return kwargs["chat_id"] == callback_query.message.chat_id assert check_shortcut_signature( CallbackQuery.unpin_message, Bot.unpin_chat_message, - ['message_id', 'chat_id'], + ["message_id", "chat_id"], [], ) assert check_shortcut_call( callback_query.unpin_message, callback_query.bot, - 'unpin_chat_message', - shortcut_kwargs=['message_id', 'chat_id'], + "unpin_chat_message", + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(callback_query.unpin_message, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'unpin_chat_message', make_assertion) + monkeypatch.setattr(callback_query.bot, "unpin_chat_message", make_assertion) assert callback_query.unpin_message() def test_copy_message(self, monkeypatch, callback_query): @@ -411,29 +408,29 @@ def test_copy_message(self, monkeypatch, callback_query): pytest.skip("Can't copy inline messages") def make_assertion(*args, **kwargs): - id_ = kwargs['from_chat_id'] == callback_query.message.chat_id - chat_id = kwargs['chat_id'] == 1 - message = kwargs['message_id'] == callback_query.message.message_id + id_ = kwargs["from_chat_id"] == callback_query.message.chat_id + chat_id = kwargs["chat_id"] == 1 + message = kwargs["message_id"] == callback_query.message.message_id return id_ and message and chat_id assert check_shortcut_signature( CallbackQuery.copy_message, Bot.copy_message, - ['message_id', 'from_chat_id'], + ["message_id", "from_chat_id"], [], ) - assert check_shortcut_call(callback_query.copy_message, callback_query.bot, 'copy_message') + assert check_shortcut_call(callback_query.copy_message, callback_query.bot, "copy_message") assert check_defaults_handling(callback_query.copy_message, callback_query.bot) - monkeypatch.setattr(callback_query.bot, 'copy_message', make_assertion) + monkeypatch.setattr(callback_query.bot, "copy_message", make_assertion) assert callback_query.copy_message(1) def test_equality(self): - a = CallbackQuery(self.id_, self.from_user, 'chat') - b = CallbackQuery(self.id_, self.from_user, 'chat') - c = CallbackQuery(self.id_, None, '') - d = CallbackQuery('', None, 'chat') - e = Audio(self.id_, 'unique_id', 1) + a = CallbackQuery(self.id_, self.from_user, "chat") + b = CallbackQuery(self.id_, self.from_user, "chat") + c = CallbackQuery(self.id_, None, "") + d = CallbackQuery("", None, "chat") + e = Audio(self.id_, "unique_id", 1) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_callbackqueryhandler.py b/tests/test_callbackqueryhandler.py index 1f65ffd0ca0..41bbaf2a578 100644 --- a/tests/test_callbackqueryhandler.py +++ b/tests/test_callbackqueryhandler.py @@ -34,59 +34,56 @@ ) from telegram.ext import CallbackQueryHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, + {"message": message}, + {"edited_message": message}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, ] ids = ( - 'message', - 'edited_message', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', + "message", + "edited_message", + "channel_post", + "edited_channel_post", + "inline_query", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def callback_query(bot): - return Update(0, callback_query=CallbackQuery(2, User(1, '', False), None, data='test data')) + return Update(0, callback_query=CallbackQuery(2, User(1, "", False), None, data="test data")) class TestCallbackQueryHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - handler = CallbackQueryHandler(self.callback_data_1, pass_user_data=True) + def test_slot_behaviour(self, mro_slots): + handler = CallbackQueryHandler(self.callback_data_1) for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) + def callback_basic(self, update, context): + test_bot = isinstance(context.bot, Bot) test_update = isinstance(update, Update) self.test_flag = test_bot and test_update @@ -104,9 +101,9 @@ def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): def callback_group(self, bot, update, groups=None, groupdict=None): if groups is not None: - self.test_flag = groups == ('t', ' data') + self.test_flag = groups == ("t", " data") if groupdict is not None: - self.test_flag = groupdict == {'begin': 't', 'end': ' data'} + self.test_flag = groupdict == {"begin": "t", "end": " data"} def callback_context(self, update, context): self.test_flag = ( @@ -123,25 +120,16 @@ def callback_context(self, update, context): def callback_context_pattern(self, update, context): if context.matches[0].groups(): - self.test_flag = context.matches[0].groups() == ('t', ' data') + self.test_flag = context.matches[0].groups() == ("t", " data") if context.matches[0].groupdict(): - self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' data'} - - def test_basic(self, dp, callback_query): - handler = CallbackQueryHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(callback_query) - - dp.process_update(callback_query) - assert self.test_flag + self.test_flag = context.matches[0].groupdict() == {"begin": "t", "end": " data"} def test_with_pattern(self, callback_query): - handler = CallbackQueryHandler(self.callback_basic, pattern='.*est.*') + handler = CallbackQueryHandler(self.callback_basic, pattern=".*est.*") assert handler.check_update(callback_query) - callback_query.callback_query.data = 'nothing here' + callback_query.callback_query.data = "nothing here" assert not handler.check_update(callback_query) callback_query.callback_query.data = None @@ -159,7 +147,7 @@ def pattern(callback_data): callback_query.callback_query.data = CallbackData() assert handler.check_update(callback_query) - callback_query.callback_query.data = 'callback_data' + callback_query.callback_query.data = "callback_data" assert not handler.check_update(callback_query) def test_with_type_pattern(self, callback_query): @@ -170,113 +158,44 @@ class CallbackData: callback_query.callback_query.data = CallbackData() assert handler.check_update(callback_query) - callback_query.callback_query.data = 'callback_data' + callback_query.callback_query.data = "callback_data" assert not handler.check_update(callback_query) handler = CallbackQueryHandler(self.callback_basic, pattern=bool) callback_query.callback_query.data = False assert handler.check_update(callback_query) - callback_query.callback_query.data = 'callback_data' + callback_query.callback_query.data = "callback_data" assert not handler.check_update(callback_query) - def test_with_passing_group_dict(self, dp, callback_query): - handler = CallbackQueryHandler( - self.callback_group, pattern='(?P.*)est(?P.*)', pass_groups=True - ) - dp.add_handler(handler) - - dp.process_update(callback_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = CallbackQueryHandler( - self.callback_group, pattern='(?P.*)est(?P.*)', pass_groupdict=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(callback_query) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, callback_query): - handler = CallbackQueryHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(callback_query) - assert self.test_flag + def test_other_update_types(self, false_update): + handler = CallbackQueryHandler(self.callback_basic) + assert not handler.check_update(false_update) - dp.remove_handler(handler) - handler = CallbackQueryHandler(self.callback_data_1, pass_chat_data=True) + def test_context(self, dp, callback_query): + handler = CallbackQueryHandler(self.callback_context) dp.add_handler(handler) - self.test_flag = False dp.process_update(callback_query) assert self.test_flag - dp.remove_handler(handler) + def test_context_pattern(self, dp, callback_query): handler = CallbackQueryHandler( - self.callback_data_2, pass_chat_data=True, pass_user_data=True + self.callback_context_pattern, pattern=r"(?P.*)est(?P.*)" ) dp.add_handler(handler) - self.test_flag = False - dp.process_update(callback_query) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, callback_query): - handler = CallbackQueryHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - dp.process_update(callback_query) assert self.test_flag dp.remove_handler(handler) - handler = CallbackQueryHandler(self.callback_queue_1, pass_update_queue=True) + handler = CallbackQueryHandler(self.callback_context_pattern, pattern=r"(t)est(.*)") dp.add_handler(handler) - self.test_flag = False dp.process_update(callback_query) assert self.test_flag - dp.remove_handler(handler) - handler = CallbackQueryHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(callback_query) - assert self.test_flag - - def test_other_update_types(self, false_update): - handler = CallbackQueryHandler(self.callback_basic) - assert not handler.check_update(false_update) - - def test_context(self, cdp, callback_query): - handler = CallbackQueryHandler(self.callback_context) - cdp.add_handler(handler) - - cdp.process_update(callback_query) - assert self.test_flag - - def test_context_pattern(self, cdp, callback_query): - handler = CallbackQueryHandler( - self.callback_context_pattern, pattern=r'(?P.*)est(?P.*)' - ) - cdp.add_handler(handler) - - cdp.process_update(callback_query) - assert self.test_flag - - cdp.remove_handler(handler) - handler = CallbackQueryHandler(self.callback_context_pattern, pattern=r'(t)est(.*)') - cdp.add_handler(handler) - - cdp.process_update(callback_query) - assert self.test_flag - - def test_context_callable_pattern(self, cdp, callback_query): + def test_context_callable_pattern(self, dp, callback_query): class CallbackData: pass @@ -287,6 +206,6 @@ def callback(update, context): assert context.matches is None handler = CallbackQueryHandler(callback, pattern=pattern) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(callback_query) + dp.process_update(callback_query) diff --git a/tests/test_chat.py b/tests/test_chat.py index a60956c485e..2e685ef9d42 100644 --- a/tests/test_chat.py +++ b/tests/test_chat.py @@ -24,7 +24,7 @@ from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chat(bot): return Chat( TestChat.id_, @@ -46,11 +46,11 @@ def chat(bot): class TestChat: id_ = -28767330 - title = 'ToledosPalaceBot - Group' - type_ = 'group' - username = 'username' + title = "ToledosPalaceBot - Group" + type_ = "group" + username = "username" all_members_are_administrators = False - sticker_set_name = 'stickers' + sticker_set_name = "stickers" can_set_sticker_set = False permissions = ChatPermissions( can_send_messages=True, @@ -61,31 +61,28 @@ class TestChat: message_auto_delete_time = 42 bio = "I'm a Barbie Girl in a Barbie World" linked_chat_id = 11880 - location = ChatLocation(Location(123, 456), 'Barbie World') + location = ChatLocation(Location(123, 456), "Barbie World") - def test_slot_behaviour(self, chat, recwarn, mro_slots): + def test_slot_behaviour(self, chat, mro_slots): for attr in chat.__slots__: - assert getattr(chat, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not chat.__dict__, f"got missing slot(s): {chat.__dict__}" + assert getattr(chat, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(chat)) == len(set(mro_slots(chat))), "duplicate slot" - chat.custom, chat.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'id': self.id_, - 'title': self.title, - 'type': self.type_, - 'username': self.username, - 'all_members_are_administrators': self.all_members_are_administrators, - 'sticker_set_name': self.sticker_set_name, - 'can_set_sticker_set': self.can_set_sticker_set, - 'permissions': self.permissions.to_dict(), - 'slow_mode_delay': self.slow_mode_delay, - 'message_auto_delete_time': self.message_auto_delete_time, - 'bio': self.bio, - 'linked_chat_id': self.linked_chat_id, - 'location': self.location.to_dict(), + "id": self.id_, + "title": self.title, + "type": self.type_, + "username": self.username, + "all_members_are_administrators": self.all_members_are_administrators, + "sticker_set_name": self.sticker_set_name, + "can_set_sticker_set": self.can_set_sticker_set, + "permissions": self.permissions.to_dict(), + "slow_mode_delay": self.slow_mode_delay, + "message_auto_delete_time": self.message_auto_delete_time, + "bio": self.bio, + "linked_chat_id": self.linked_chat_id, + "location": self.location.to_dict(), } chat = Chat.de_json(json_dict, bot) @@ -108,30 +105,30 @@ def test_to_dict(self, chat): chat_dict = chat.to_dict() assert isinstance(chat_dict, dict) - assert chat_dict['id'] == chat.id - assert chat_dict['title'] == chat.title - assert chat_dict['type'] == chat.type - assert chat_dict['username'] == chat.username - assert chat_dict['all_members_are_administrators'] == chat.all_members_are_administrators - assert chat_dict['permissions'] == chat.permissions.to_dict() - assert chat_dict['slow_mode_delay'] == chat.slow_mode_delay - assert chat_dict['message_auto_delete_time'] == chat.message_auto_delete_time - assert chat_dict['bio'] == chat.bio - assert chat_dict['linked_chat_id'] == chat.linked_chat_id - assert chat_dict['location'] == chat.location.to_dict() + assert chat_dict["id"] == chat.id + assert chat_dict["title"] == chat.title + assert chat_dict["type"] == chat.type + assert chat_dict["username"] == chat.username + assert chat_dict["all_members_are_administrators"] == chat.all_members_are_administrators + assert chat_dict["permissions"] == chat.permissions.to_dict() + assert chat_dict["slow_mode_delay"] == chat.slow_mode_delay + assert chat_dict["message_auto_delete_time"] == chat.message_auto_delete_time + assert chat_dict["bio"] == chat.bio + assert chat_dict["linked_chat_id"] == chat.linked_chat_id + assert chat_dict["location"] == chat.location.to_dict() def test_link(self, chat): - assert chat.link == f'https://t.me/{chat.username}' + assert chat.link == f"https://t.me/{chat.username}" chat.username = None assert chat.link is None def test_full_name(self): chat = Chat( - id=1, type=Chat.PRIVATE, first_name='first\u2022name', last_name='last\u2022name' + id=1, type=Chat.PRIVATE, first_name="first\u2022name", last_name="last\u2022name" ) - assert chat.full_name == 'first\u2022name last\u2022name' - chat = Chat(id=1, type=Chat.PRIVATE, first_name='first\u2022name') - assert chat.full_name == 'first\u2022name' + assert chat.full_name == "first\u2022name last\u2022name" + chat = Chat(id=1, type=Chat.PRIVATE, first_name="first\u2022name") + assert chat.full_name == "first\u2022name" chat = Chat( id=1, type=Chat.PRIVATE, @@ -140,508 +137,487 @@ def test_full_name(self): def test_send_action(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == chat.id - action = kwargs['action'] == ChatAction.TYPING + id_ = kwargs["chat_id"] == chat.id + action = kwargs["action"] == ChatAction.TYPING return id_ and action - assert check_shortcut_signature(chat.send_action, Bot.send_chat_action, ['chat_id'], []) - assert check_shortcut_call(chat.send_action, chat.bot, 'send_chat_action') + assert check_shortcut_signature(chat.send_action, Bot.send_chat_action, ["chat_id"], []) + assert check_shortcut_call(chat.send_action, chat.bot, "send_chat_action") assert check_defaults_handling(chat.send_action, chat.bot) - monkeypatch.setattr(chat.bot, 'send_chat_action', make_assertion) + monkeypatch.setattr(chat.bot, "send_chat_action", make_assertion) assert chat.send_action(action=ChatAction.TYPING) assert chat.send_action(action=ChatAction.TYPING) def test_leave(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id - assert check_shortcut_signature(Chat.leave, Bot.leave_chat, ['chat_id'], []) - assert check_shortcut_call(chat.leave, chat.bot, 'leave_chat') + assert check_shortcut_signature(Chat.leave, Bot.leave_chat, ["chat_id"], []) + assert check_shortcut_call(chat.leave, chat.bot, "leave_chat") assert check_defaults_handling(chat.leave, chat.bot) - monkeypatch.setattr(chat.bot, 'leave_chat', make_assertion) + monkeypatch.setattr(chat.bot, "leave_chat", make_assertion) assert chat.leave() def test_get_administrators(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature( - Chat.get_administrators, Bot.get_chat_administrators, ['chat_id'], [] + Chat.get_administrators, Bot.get_chat_administrators, ["chat_id"], [] ) - assert check_shortcut_call(chat.get_administrators, chat.bot, 'get_chat_administrators') + assert check_shortcut_call(chat.get_administrators, chat.bot, "get_chat_administrators") assert check_defaults_handling(chat.get_administrators, chat.bot) - monkeypatch.setattr(chat.bot, 'get_chat_administrators', make_assertion) + monkeypatch.setattr(chat.bot, "get_chat_administrators", make_assertion) assert chat.get_administrators() def test_get_member_count(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature( - Chat.get_member_count, Bot.get_chat_member_count, ['chat_id'], [] + Chat.get_member_count, Bot.get_chat_member_count, ["chat_id"], [] ) - assert check_shortcut_call(chat.get_member_count, chat.bot, 'get_chat_member_count') + assert check_shortcut_call(chat.get_member_count, chat.bot, "get_chat_member_count") assert check_defaults_handling(chat.get_member_count, chat.bot) - monkeypatch.setattr(chat.bot, 'get_chat_member_count', make_assertion) + monkeypatch.setattr(chat.bot, "get_chat_member_count", make_assertion) assert chat.get_member_count() - def test_get_members_count_warning(self, chat, monkeypatch, recwarn): - def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id - - monkeypatch.setattr(chat.bot, 'get_chat_member_count', make_assertion) - assert chat.get_members_count() - assert len(recwarn) == 1 - assert '`Chat.get_members_count` is deprecated' in str(recwarn[0].message) - def test_get_member(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 + chat_id = kwargs["chat_id"] == chat.id + user_id = kwargs["user_id"] == 42 return chat_id and user_id - assert check_shortcut_signature(Chat.get_member, Bot.get_chat_member, ['chat_id'], []) - assert check_shortcut_call(chat.get_member, chat.bot, 'get_chat_member') + assert check_shortcut_signature(Chat.get_member, Bot.get_chat_member, ["chat_id"], []) + assert check_shortcut_call(chat.get_member, chat.bot, "get_chat_member") assert check_defaults_handling(chat.get_member, chat.bot) - monkeypatch.setattr(chat.bot, 'get_chat_member', make_assertion) + monkeypatch.setattr(chat.bot, "get_chat_member", make_assertion) assert chat.get_member(user_id=42) def test_ban_member(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - until = kwargs['until_date'] == 43 + chat_id = kwargs["chat_id"] == chat.id + user_id = kwargs["user_id"] == 42 + until = kwargs["until_date"] == 43 return chat_id and user_id and until - assert check_shortcut_signature(Chat.ban_member, Bot.ban_chat_member, ['chat_id'], []) - assert check_shortcut_call(chat.ban_member, chat.bot, 'ban_chat_member') + assert check_shortcut_signature(Chat.ban_member, Bot.ban_chat_member, ["chat_id"], []) + assert check_shortcut_call(chat.ban_member, chat.bot, "ban_chat_member") assert check_defaults_handling(chat.ban_member, chat.bot) - monkeypatch.setattr(chat.bot, 'ban_chat_member', make_assertion) + monkeypatch.setattr(chat.bot, "ban_chat_member", make_assertion) assert chat.ban_member(user_id=42, until_date=43) - def test_kick_member_warning(self, chat, monkeypatch, recwarn): - def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - until = kwargs['until_date'] == 43 - return chat_id and user_id and until - - monkeypatch.setattr(chat.bot, 'ban_chat_member', make_assertion) - assert chat.kick_member(user_id=42, until_date=43) - assert len(recwarn) == 1 - assert '`Chat.kick_member` is deprecated' in str(recwarn[0].message) - - @pytest.mark.parametrize('only_if_banned', [True, False, None]) + @pytest.mark.parametrize("only_if_banned", [True, False, None]) def test_unban_member(self, monkeypatch, chat, only_if_banned): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - o_i_b = kwargs.get('only_if_banned') == only_if_banned + chat_id = kwargs["chat_id"] == chat.id + user_id = kwargs["user_id"] == 42 + o_i_b = kwargs.get("only_if_banned") == only_if_banned return chat_id and user_id and o_i_b - assert check_shortcut_signature(Chat.unban_member, Bot.unban_chat_member, ['chat_id'], []) - assert check_shortcut_call(chat.unban_member, chat.bot, 'unban_chat_member') + assert check_shortcut_signature(Chat.unban_member, Bot.unban_chat_member, ["chat_id"], []) + assert check_shortcut_call(chat.unban_member, chat.bot, "unban_chat_member") assert check_defaults_handling(chat.unban_member, chat.bot) - monkeypatch.setattr(chat.bot, 'unban_chat_member', make_assertion) + monkeypatch.setattr(chat.bot, "unban_chat_member", make_assertion) assert chat.unban_member(user_id=42, only_if_banned=only_if_banned) - @pytest.mark.parametrize('is_anonymous', [True, False, None]) + @pytest.mark.parametrize("is_anonymous", [True, False, None]) def test_promote_member(self, monkeypatch, chat, is_anonymous): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - o_i_b = kwargs.get('is_anonymous') == is_anonymous + chat_id = kwargs["chat_id"] == chat.id + user_id = kwargs["user_id"] == 42 + o_i_b = kwargs.get("is_anonymous") == is_anonymous return chat_id and user_id and o_i_b assert check_shortcut_signature( - Chat.promote_member, Bot.promote_chat_member, ['chat_id'], [] + Chat.promote_member, Bot.promote_chat_member, ["chat_id"], [] ) - assert check_shortcut_call(chat.promote_member, chat.bot, 'promote_chat_member') + assert check_shortcut_call(chat.promote_member, chat.bot, "promote_chat_member") assert check_defaults_handling(chat.promote_member, chat.bot) - monkeypatch.setattr(chat.bot, 'promote_chat_member', make_assertion) + monkeypatch.setattr(chat.bot, "promote_chat_member", make_assertion) assert chat.promote_member(user_id=42, is_anonymous=is_anonymous) def test_restrict_member(self, monkeypatch, chat): permissions = ChatPermissions(True, False, True, False, True, False, True, False) def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - o_i_b = kwargs.get('permissions') == permissions + chat_id = kwargs["chat_id"] == chat.id + user_id = kwargs["user_id"] == 42 + o_i_b = kwargs.get("permissions") == permissions return chat_id and user_id and o_i_b assert check_shortcut_signature( - Chat.restrict_member, Bot.restrict_chat_member, ['chat_id'], [] + Chat.restrict_member, Bot.restrict_chat_member, ["chat_id"], [] ) - assert check_shortcut_call(chat.restrict_member, chat.bot, 'restrict_chat_member') + assert check_shortcut_call(chat.restrict_member, chat.bot, "restrict_chat_member") assert check_defaults_handling(chat.restrict_member, chat.bot) - monkeypatch.setattr(chat.bot, 'restrict_chat_member', make_assertion) + monkeypatch.setattr(chat.bot, "restrict_chat_member", make_assertion) assert chat.restrict_member(user_id=42, permissions=permissions) def test_set_permissions(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - permissions = kwargs['permissions'] == self.permissions + chat_id = kwargs["chat_id"] == chat.id + permissions = kwargs["permissions"] == self.permissions return chat_id and permissions assert check_shortcut_signature( - Chat.set_permissions, Bot.set_chat_permissions, ['chat_id'], [] + Chat.set_permissions, Bot.set_chat_permissions, ["chat_id"], [] ) - assert check_shortcut_call(chat.set_permissions, chat.bot, 'set_chat_permissions') + assert check_shortcut_call(chat.set_permissions, chat.bot, "set_chat_permissions") assert check_defaults_handling(chat.set_permissions, chat.bot) - monkeypatch.setattr(chat.bot, 'set_chat_permissions', make_assertion) + monkeypatch.setattr(chat.bot, "set_chat_permissions", make_assertion) assert chat.set_permissions(permissions=self.permissions) def test_set_administrator_custom_title(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == chat.id - user_id = kwargs['user_id'] == 42 - custom_title = kwargs['custom_title'] == 'custom_title' + chat_id = kwargs["chat_id"] == chat.id + user_id = kwargs["user_id"] == 42 + custom_title = kwargs["custom_title"] == "custom_title" return chat_id and user_id and custom_title - monkeypatch.setattr('telegram.Bot.set_chat_administrator_custom_title', make_assertion) - assert chat.set_administrator_custom_title(user_id=42, custom_title='custom_title') + monkeypatch.setattr("telegram.Bot.set_chat_administrator_custom_title", make_assertion) + assert chat.set_administrator_custom_title(user_id=42, custom_title="custom_title") def test_pin_message(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['message_id'] == 42 + return kwargs["chat_id"] == chat.id and kwargs["message_id"] == 42 - assert check_shortcut_signature(Chat.pin_message, Bot.pin_chat_message, ['chat_id'], []) - assert check_shortcut_call(chat.pin_message, chat.bot, 'pin_chat_message') + assert check_shortcut_signature(Chat.pin_message, Bot.pin_chat_message, ["chat_id"], []) + assert check_shortcut_call(chat.pin_message, chat.bot, "pin_chat_message") assert check_defaults_handling(chat.pin_message, chat.bot) - monkeypatch.setattr(chat.bot, 'pin_chat_message', make_assertion) + monkeypatch.setattr(chat.bot, "pin_chat_message", make_assertion) assert chat.pin_message(message_id=42) def test_unpin_message(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature( - Chat.unpin_message, Bot.unpin_chat_message, ['chat_id'], [] + Chat.unpin_message, Bot.unpin_chat_message, ["chat_id"], [] ) - assert check_shortcut_call(chat.unpin_message, chat.bot, 'unpin_chat_message') + assert check_shortcut_call(chat.unpin_message, chat.bot, "unpin_chat_message") assert check_defaults_handling(chat.unpin_message, chat.bot) - monkeypatch.setattr(chat.bot, 'unpin_chat_message', make_assertion) + monkeypatch.setattr(chat.bot, "unpin_chat_message", make_assertion) assert chat.unpin_message() def test_unpin_all_messages(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature( - Chat.unpin_all_messages, Bot.unpin_all_chat_messages, ['chat_id'], [] + Chat.unpin_all_messages, Bot.unpin_all_chat_messages, ["chat_id"], [] ) - assert check_shortcut_call(chat.unpin_all_messages, chat.bot, 'unpin_all_chat_messages') + assert check_shortcut_call(chat.unpin_all_messages, chat.bot, "unpin_all_chat_messages") assert check_defaults_handling(chat.unpin_all_messages, chat.bot) - monkeypatch.setattr(chat.bot, 'unpin_all_chat_messages', make_assertion) + monkeypatch.setattr(chat.bot, "unpin_all_chat_messages", make_assertion) assert chat.unpin_all_messages() def test_instance_method_send_message(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['text'] == 'test' + return kwargs["chat_id"] == chat.id and kwargs["text"] == "test" - assert check_shortcut_signature(Chat.send_message, Bot.send_message, ['chat_id'], []) - assert check_shortcut_call(chat.send_message, chat.bot, 'send_message') + assert check_shortcut_signature(Chat.send_message, Bot.send_message, ["chat_id"], []) + assert check_shortcut_call(chat.send_message, chat.bot, "send_message") assert check_defaults_handling(chat.send_message, chat.bot) - monkeypatch.setattr(chat.bot, 'send_message', make_assertion) - assert chat.send_message(text='test') + monkeypatch.setattr(chat.bot, "send_message", make_assertion) + assert chat.send_message(text="test") def test_instance_method_send_media_group(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['media'] == 'test_media_group' + return kwargs["chat_id"] == chat.id and kwargs["media"] == "test_media_group" assert check_shortcut_signature( - Chat.send_media_group, Bot.send_media_group, ['chat_id'], [] + Chat.send_media_group, Bot.send_media_group, ["chat_id"], [] ) - assert check_shortcut_call(chat.send_media_group, chat.bot, 'send_media_group') + assert check_shortcut_call(chat.send_media_group, chat.bot, "send_media_group") assert check_defaults_handling(chat.send_media_group, chat.bot) - monkeypatch.setattr(chat.bot, 'send_media_group', make_assertion) - assert chat.send_media_group(media='test_media_group') + monkeypatch.setattr(chat.bot, "send_media_group", make_assertion) + assert chat.send_media_group(media="test_media_group") def test_instance_method_send_photo(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['photo'] == 'test_photo' + return kwargs["chat_id"] == chat.id and kwargs["photo"] == "test_photo" - assert check_shortcut_signature(Chat.send_photo, Bot.send_photo, ['chat_id'], []) - assert check_shortcut_call(chat.send_photo, chat.bot, 'send_photo') + assert check_shortcut_signature(Chat.send_photo, Bot.send_photo, ["chat_id"], []) + assert check_shortcut_call(chat.send_photo, chat.bot, "send_photo") assert check_defaults_handling(chat.send_photo, chat.bot) - monkeypatch.setattr(chat.bot, 'send_photo', make_assertion) - assert chat.send_photo(photo='test_photo') + monkeypatch.setattr(chat.bot, "send_photo", make_assertion) + assert chat.send_photo(photo="test_photo") def test_instance_method_send_contact(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['phone_number'] == 'test_contact' + return kwargs["chat_id"] == chat.id and kwargs["phone_number"] == "test_contact" - assert check_shortcut_signature(Chat.send_contact, Bot.send_contact, ['chat_id'], []) - assert check_shortcut_call(chat.send_contact, chat.bot, 'send_contact') + assert check_shortcut_signature(Chat.send_contact, Bot.send_contact, ["chat_id"], []) + assert check_shortcut_call(chat.send_contact, chat.bot, "send_contact") assert check_defaults_handling(chat.send_contact, chat.bot) - monkeypatch.setattr(chat.bot, 'send_contact', make_assertion) - assert chat.send_contact(phone_number='test_contact') + monkeypatch.setattr(chat.bot, "send_contact", make_assertion) + assert chat.send_contact(phone_number="test_contact") def test_instance_method_send_audio(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['audio'] == 'test_audio' + return kwargs["chat_id"] == chat.id and kwargs["audio"] == "test_audio" - assert check_shortcut_signature(Chat.send_audio, Bot.send_audio, ['chat_id'], []) - assert check_shortcut_call(chat.send_audio, chat.bot, 'send_audio') + assert check_shortcut_signature(Chat.send_audio, Bot.send_audio, ["chat_id"], []) + assert check_shortcut_call(chat.send_audio, chat.bot, "send_audio") assert check_defaults_handling(chat.send_audio, chat.bot) - monkeypatch.setattr(chat.bot, 'send_audio', make_assertion) - assert chat.send_audio(audio='test_audio') + monkeypatch.setattr(chat.bot, "send_audio", make_assertion) + assert chat.send_audio(audio="test_audio") def test_instance_method_send_document(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['document'] == 'test_document' + return kwargs["chat_id"] == chat.id and kwargs["document"] == "test_document" - assert check_shortcut_signature(Chat.send_document, Bot.send_document, ['chat_id'], []) - assert check_shortcut_call(chat.send_document, chat.bot, 'send_document') + assert check_shortcut_signature(Chat.send_document, Bot.send_document, ["chat_id"], []) + assert check_shortcut_call(chat.send_document, chat.bot, "send_document") assert check_defaults_handling(chat.send_document, chat.bot) - monkeypatch.setattr(chat.bot, 'send_document', make_assertion) - assert chat.send_document(document='test_document') + monkeypatch.setattr(chat.bot, "send_document", make_assertion) + assert chat.send_document(document="test_document") def test_instance_method_send_dice(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['emoji'] == 'test_dice' + return kwargs["chat_id"] == chat.id and kwargs["emoji"] == "test_dice" - assert check_shortcut_signature(Chat.send_dice, Bot.send_dice, ['chat_id'], []) - assert check_shortcut_call(chat.send_dice, chat.bot, 'send_dice') + assert check_shortcut_signature(Chat.send_dice, Bot.send_dice, ["chat_id"], []) + assert check_shortcut_call(chat.send_dice, chat.bot, "send_dice") assert check_defaults_handling(chat.send_dice, chat.bot) - monkeypatch.setattr(chat.bot, 'send_dice', make_assertion) - assert chat.send_dice(emoji='test_dice') + monkeypatch.setattr(chat.bot, "send_dice", make_assertion) + assert chat.send_dice(emoji="test_dice") def test_instance_method_send_game(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['game_short_name'] == 'test_game' + return kwargs["chat_id"] == chat.id and kwargs["game_short_name"] == "test_game" - assert check_shortcut_signature(Chat.send_game, Bot.send_game, ['chat_id'], []) - assert check_shortcut_call(chat.send_game, chat.bot, 'send_game') + assert check_shortcut_signature(Chat.send_game, Bot.send_game, ["chat_id"], []) + assert check_shortcut_call(chat.send_game, chat.bot, "send_game") assert check_defaults_handling(chat.send_game, chat.bot) - monkeypatch.setattr(chat.bot, 'send_game', make_assertion) - assert chat.send_game(game_short_name='test_game') + monkeypatch.setattr(chat.bot, "send_game", make_assertion) + assert chat.send_game(game_short_name="test_game") def test_instance_method_send_invoice(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - title = kwargs['title'] == 'title' - description = kwargs['description'] == 'description' - payload = kwargs['payload'] == 'payload' - provider_token = kwargs['provider_token'] == 'provider_token' - currency = kwargs['currency'] == 'currency' - prices = kwargs['prices'] == 'prices' + title = kwargs["title"] == "title" + description = kwargs["description"] == "description" + payload = kwargs["payload"] == "payload" + provider_token = kwargs["provider_token"] == "provider_token" + currency = kwargs["currency"] == "currency" + prices = kwargs["prices"] == "prices" args = title and description and payload and provider_token and currency and prices - return kwargs['chat_id'] == chat.id and args + return kwargs["chat_id"] == chat.id and args - assert check_shortcut_signature(Chat.send_invoice, Bot.send_invoice, ['chat_id'], []) - assert check_shortcut_call(chat.send_invoice, chat.bot, 'send_invoice') + assert check_shortcut_signature(Chat.send_invoice, Bot.send_invoice, ["chat_id"], []) + assert check_shortcut_call(chat.send_invoice, chat.bot, "send_invoice") assert check_defaults_handling(chat.send_invoice, chat.bot) - monkeypatch.setattr(chat.bot, 'send_invoice', make_assertion) + monkeypatch.setattr(chat.bot, "send_invoice", make_assertion) assert chat.send_invoice( - 'title', - 'description', - 'payload', - 'provider_token', - 'currency', - 'prices', + "title", + "description", + "payload", + "provider_token", + "currency", + "prices", ) def test_instance_method_send_location(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['latitude'] == 'test_location' + return kwargs["chat_id"] == chat.id and kwargs["latitude"] == "test_location" - assert check_shortcut_signature(Chat.send_location, Bot.send_location, ['chat_id'], []) - assert check_shortcut_call(chat.send_location, chat.bot, 'send_location') + assert check_shortcut_signature(Chat.send_location, Bot.send_location, ["chat_id"], []) + assert check_shortcut_call(chat.send_location, chat.bot, "send_location") assert check_defaults_handling(chat.send_location, chat.bot) - monkeypatch.setattr(chat.bot, 'send_location', make_assertion) - assert chat.send_location(latitude='test_location') + monkeypatch.setattr(chat.bot, "send_location", make_assertion) + assert chat.send_location(latitude="test_location") def test_instance_method_send_sticker(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['sticker'] == 'test_sticker' + return kwargs["chat_id"] == chat.id and kwargs["sticker"] == "test_sticker" - assert check_shortcut_signature(Chat.send_sticker, Bot.send_sticker, ['chat_id'], []) - assert check_shortcut_call(chat.send_sticker, chat.bot, 'send_sticker') + assert check_shortcut_signature(Chat.send_sticker, Bot.send_sticker, ["chat_id"], []) + assert check_shortcut_call(chat.send_sticker, chat.bot, "send_sticker") assert check_defaults_handling(chat.send_sticker, chat.bot) - monkeypatch.setattr(chat.bot, 'send_sticker', make_assertion) - assert chat.send_sticker(sticker='test_sticker') + monkeypatch.setattr(chat.bot, "send_sticker", make_assertion) + assert chat.send_sticker(sticker="test_sticker") def test_instance_method_send_venue(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['title'] == 'test_venue' + return kwargs["chat_id"] == chat.id and kwargs["title"] == "test_venue" - assert check_shortcut_signature(Chat.send_venue, Bot.send_venue, ['chat_id'], []) - assert check_shortcut_call(chat.send_venue, chat.bot, 'send_venue') + assert check_shortcut_signature(Chat.send_venue, Bot.send_venue, ["chat_id"], []) + assert check_shortcut_call(chat.send_venue, chat.bot, "send_venue") assert check_defaults_handling(chat.send_venue, chat.bot) - monkeypatch.setattr(chat.bot, 'send_venue', make_assertion) - assert chat.send_venue(title='test_venue') + monkeypatch.setattr(chat.bot, "send_venue", make_assertion) + assert chat.send_venue(title="test_venue") def test_instance_method_send_video(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['video'] == 'test_video' + return kwargs["chat_id"] == chat.id and kwargs["video"] == "test_video" - assert check_shortcut_signature(Chat.send_video, Bot.send_video, ['chat_id'], []) - assert check_shortcut_call(chat.send_video, chat.bot, 'send_video') + assert check_shortcut_signature(Chat.send_video, Bot.send_video, ["chat_id"], []) + assert check_shortcut_call(chat.send_video, chat.bot, "send_video") assert check_defaults_handling(chat.send_video, chat.bot) - monkeypatch.setattr(chat.bot, 'send_video', make_assertion) - assert chat.send_video(video='test_video') + monkeypatch.setattr(chat.bot, "send_video", make_assertion) + assert chat.send_video(video="test_video") def test_instance_method_send_video_note(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['video_note'] == 'test_video_note' + return kwargs["chat_id"] == chat.id and kwargs["video_note"] == "test_video_note" - assert check_shortcut_signature(Chat.send_video_note, Bot.send_video_note, ['chat_id'], []) - assert check_shortcut_call(chat.send_video_note, chat.bot, 'send_video_note') + assert check_shortcut_signature(Chat.send_video_note, Bot.send_video_note, ["chat_id"], []) + assert check_shortcut_call(chat.send_video_note, chat.bot, "send_video_note") assert check_defaults_handling(chat.send_video_note, chat.bot) - monkeypatch.setattr(chat.bot, 'send_video_note', make_assertion) - assert chat.send_video_note(video_note='test_video_note') + monkeypatch.setattr(chat.bot, "send_video_note", make_assertion) + assert chat.send_video_note(video_note="test_video_note") def test_instance_method_send_voice(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['voice'] == 'test_voice' + return kwargs["chat_id"] == chat.id and kwargs["voice"] == "test_voice" - assert check_shortcut_signature(Chat.send_voice, Bot.send_voice, ['chat_id'], []) - assert check_shortcut_call(chat.send_voice, chat.bot, 'send_voice') + assert check_shortcut_signature(Chat.send_voice, Bot.send_voice, ["chat_id"], []) + assert check_shortcut_call(chat.send_voice, chat.bot, "send_voice") assert check_defaults_handling(chat.send_voice, chat.bot) - monkeypatch.setattr(chat.bot, 'send_voice', make_assertion) - assert chat.send_voice(voice='test_voice') + monkeypatch.setattr(chat.bot, "send_voice", make_assertion) + assert chat.send_voice(voice="test_voice") def test_instance_method_send_animation(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['animation'] == 'test_animation' + return kwargs["chat_id"] == chat.id and kwargs["animation"] == "test_animation" - assert check_shortcut_signature(Chat.send_animation, Bot.send_animation, ['chat_id'], []) - assert check_shortcut_call(chat.send_animation, chat.bot, 'send_animation') + assert check_shortcut_signature(Chat.send_animation, Bot.send_animation, ["chat_id"], []) + assert check_shortcut_call(chat.send_animation, chat.bot, "send_animation") assert check_defaults_handling(chat.send_animation, chat.bot) - monkeypatch.setattr(chat.bot, 'send_animation', make_assertion) - assert chat.send_animation(animation='test_animation') + monkeypatch.setattr(chat.bot, "send_animation", make_assertion) + assert chat.send_animation(animation="test_animation") def test_instance_method_send_poll(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['question'] == 'test_poll' + return kwargs["chat_id"] == chat.id and kwargs["question"] == "test_poll" - assert check_shortcut_signature(Chat.send_poll, Bot.send_poll, ['chat_id'], []) - assert check_shortcut_call(chat.send_poll, chat.bot, 'send_poll') + assert check_shortcut_signature(Chat.send_poll, Bot.send_poll, ["chat_id"], []) + assert check_shortcut_call(chat.send_poll, chat.bot, "send_poll") assert check_defaults_handling(chat.send_poll, chat.bot) - monkeypatch.setattr(chat.bot, 'send_poll', make_assertion) - assert chat.send_poll(question='test_poll', options=[1, 2]) + monkeypatch.setattr(chat.bot, "send_poll", make_assertion) + assert chat.send_poll(question="test_poll", options=[1, 2]) def test_instance_method_send_copy(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - from_chat_id = kwargs['from_chat_id'] == 'test_copy' - message_id = kwargs['message_id'] == 42 - chat_id = kwargs['chat_id'] == chat.id + from_chat_id = kwargs["from_chat_id"] == "test_copy" + message_id = kwargs["message_id"] == 42 + chat_id = kwargs["chat_id"] == chat.id return from_chat_id and message_id and chat_id - assert check_shortcut_signature(Chat.send_copy, Bot.copy_message, ['chat_id'], []) - assert check_shortcut_call(chat.copy_message, chat.bot, 'copy_message') + assert check_shortcut_signature(Chat.send_copy, Bot.copy_message, ["chat_id"], []) + assert check_shortcut_call(chat.copy_message, chat.bot, "copy_message") assert check_defaults_handling(chat.copy_message, chat.bot) - monkeypatch.setattr(chat.bot, 'copy_message', make_assertion) - assert chat.send_copy(from_chat_id='test_copy', message_id=42) + monkeypatch.setattr(chat.bot, "copy_message", make_assertion) + assert chat.send_copy(from_chat_id="test_copy", message_id=42) def test_instance_method_copy_message(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - from_chat_id = kwargs['from_chat_id'] == chat.id - message_id = kwargs['message_id'] == 42 - chat_id = kwargs['chat_id'] == 'test_copy' + from_chat_id = kwargs["from_chat_id"] == chat.id + message_id = kwargs["message_id"] == 42 + chat_id = kwargs["chat_id"] == "test_copy" return from_chat_id and message_id and chat_id - assert check_shortcut_signature(Chat.copy_message, Bot.copy_message, ['from_chat_id'], []) - assert check_shortcut_call(chat.copy_message, chat.bot, 'copy_message') + assert check_shortcut_signature(Chat.copy_message, Bot.copy_message, ["from_chat_id"], []) + assert check_shortcut_call(chat.copy_message, chat.bot, "copy_message") assert check_defaults_handling(chat.copy_message, chat.bot) - monkeypatch.setattr(chat.bot, 'copy_message', make_assertion) - assert chat.copy_message(chat_id='test_copy', message_id=42) + monkeypatch.setattr(chat.bot, "copy_message", make_assertion) + assert chat.copy_message(chat_id="test_copy", message_id=42) def test_export_invite_link(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature( - Chat.export_invite_link, Bot.export_chat_invite_link, ['chat_id'], [] + Chat.export_invite_link, Bot.export_chat_invite_link, ["chat_id"], [] ) - assert check_shortcut_call(chat.export_invite_link, chat.bot, 'export_chat_invite_link') + assert check_shortcut_call(chat.export_invite_link, chat.bot, "export_chat_invite_link") assert check_defaults_handling(chat.export_invite_link, chat.bot) - monkeypatch.setattr(chat.bot, 'export_chat_invite_link', make_assertion) + monkeypatch.setattr(chat.bot, "export_chat_invite_link", make_assertion) assert chat.export_invite_link() def test_create_invite_link(self, monkeypatch, chat): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id + return kwargs["chat_id"] == chat.id assert check_shortcut_signature( - Chat.create_invite_link, Bot.create_chat_invite_link, ['chat_id'], [] + Chat.create_invite_link, Bot.create_chat_invite_link, ["chat_id"], [] ) - assert check_shortcut_call(chat.create_invite_link, chat.bot, 'create_chat_invite_link') + assert check_shortcut_call(chat.create_invite_link, chat.bot, "create_chat_invite_link") assert check_defaults_handling(chat.create_invite_link, chat.bot) - monkeypatch.setattr(chat.bot, 'create_chat_invite_link', make_assertion) + monkeypatch.setattr(chat.bot, "create_chat_invite_link", make_assertion) assert chat.create_invite_link() def test_edit_invite_link(self, monkeypatch, chat): link = "ThisIsALink" def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['invite_link'] == link + return kwargs["chat_id"] == chat.id and kwargs["invite_link"] == link assert check_shortcut_signature( - Chat.edit_invite_link, Bot.edit_chat_invite_link, ['chat_id'], [] + Chat.edit_invite_link, Bot.edit_chat_invite_link, ["chat_id"], [] ) - assert check_shortcut_call(chat.edit_invite_link, chat.bot, 'edit_chat_invite_link') + assert check_shortcut_call(chat.edit_invite_link, chat.bot, "edit_chat_invite_link") assert check_defaults_handling(chat.edit_invite_link, chat.bot) - monkeypatch.setattr(chat.bot, 'edit_chat_invite_link', make_assertion) + monkeypatch.setattr(chat.bot, "edit_chat_invite_link", make_assertion) assert chat.edit_invite_link(invite_link=link) def test_revoke_invite_link(self, monkeypatch, chat): link = "ThisIsALink" def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == chat.id and kwargs['invite_link'] == link + return kwargs["chat_id"] == chat.id and kwargs["invite_link"] == link assert check_shortcut_signature( - Chat.revoke_invite_link, Bot.revoke_chat_invite_link, ['chat_id'], [] + Chat.revoke_invite_link, Bot.revoke_chat_invite_link, ["chat_id"], [] ) - assert check_shortcut_call(chat.revoke_invite_link, chat.bot, 'revoke_chat_invite_link') + assert check_shortcut_call(chat.revoke_invite_link, chat.bot, "revoke_chat_invite_link") assert check_defaults_handling(chat.revoke_invite_link, chat.bot) - monkeypatch.setattr(chat.bot, 'revoke_chat_invite_link', make_assertion) + monkeypatch.setattr(chat.bot, "revoke_chat_invite_link", make_assertion) assert chat.revoke_invite_link(invite_link=link) def test_equality(self): a = Chat(self.id_, self.title, self.type_) b = Chat(self.id_, self.title, self.type_) - c = Chat(self.id_, '', '') + c = Chat(self.id_, "", "") d = Chat(0, self.title, self.type_) - e = User(self.id_, '', False) + e = User(self.id_, "", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_chataction.py b/tests/test_chataction.py index 61903992872..f9c1ca3be31 100644 --- a/tests/test_chataction.py +++ b/tests/test_chataction.py @@ -19,11 +19,8 @@ from telegram import ChatAction -def test_slot_behaviour(recwarn, mro_slots): +def test_slot_behaviour(mro_slots): action = ChatAction() for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list diff --git a/tests/test_chatinvitelink.py b/tests/test_chatinvitelink.py index 8b4fcadfd5a..6e989e42b2a 100644 --- a/tests/test_chatinvitelink.py +++ b/tests/test_chatinvitelink.py @@ -24,12 +24,12 @@ from telegram.utils.helpers import to_timestamp -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def creator(): - return User(1, 'First name', False) + return User(1, "First name", False) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def invite_link(creator): return ChatInviteLink( TestChatInviteLink.link, @@ -49,20 +49,17 @@ class TestChatInviteLink: expire_date = datetime.datetime.utcnow() member_limit = 42 - def test_slot_behaviour(self, recwarn, mro_slots, invite_link): + def test_slot_behaviour(self, mro_slots, invite_link): for attr in invite_link.__slots__: - assert getattr(invite_link, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not invite_link.__dict__, f"got missing slot(s): {invite_link.__dict__}" + assert getattr(invite_link, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(invite_link)) == len(set(mro_slots(invite_link))), "duplicate slot" - invite_link.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json_required_args(self, bot, creator): json_dict = { - 'invite_link': self.link, - 'creator': creator.to_dict(), - 'is_primary': self.primary, - 'is_revoked': self.revoked, + "invite_link": self.link, + "creator": creator.to_dict(), + "is_primary": self.primary, + "is_revoked": self.revoked, } invite_link = ChatInviteLink.de_json(json_dict, bot) @@ -74,12 +71,12 @@ def test_de_json_required_args(self, bot, creator): def test_de_json_all_args(self, bot, creator): json_dict = { - 'invite_link': self.link, - 'creator': creator.to_dict(), - 'is_primary': self.primary, - 'is_revoked': self.revoked, - 'expire_date': to_timestamp(self.expire_date), - 'member_limit': self.member_limit, + "invite_link": self.link, + "creator": creator.to_dict(), + "is_primary": self.primary, + "is_revoked": self.revoked, + "expire_date": to_timestamp(self.expire_date), + "member_limit": self.member_limit, } invite_link = ChatInviteLink.de_json(json_dict, bot) @@ -95,20 +92,20 @@ def test_de_json_all_args(self, bot, creator): def test_to_dict(self, invite_link): invite_link_dict = invite_link.to_dict() assert isinstance(invite_link_dict, dict) - assert invite_link_dict['creator'] == invite_link.creator.to_dict() - assert invite_link_dict['invite_link'] == invite_link.invite_link - assert invite_link_dict['is_primary'] == self.primary - assert invite_link_dict['is_revoked'] == self.revoked - assert invite_link_dict['expire_date'] == to_timestamp(self.expire_date) - assert invite_link_dict['member_limit'] == self.member_limit + assert invite_link_dict["creator"] == invite_link.creator.to_dict() + assert invite_link_dict["invite_link"] == invite_link.invite_link + assert invite_link_dict["is_primary"] == self.primary + assert invite_link_dict["is_revoked"] == self.revoked + assert invite_link_dict["expire_date"] == to_timestamp(self.expire_date) + assert invite_link_dict["member_limit"] == self.member_limit def test_equality(self): - a = ChatInviteLink("link", User(1, '', False), True, True) - b = ChatInviteLink("link", User(1, '', False), True, True) - d = ChatInviteLink("link", User(2, '', False), False, True) - d2 = ChatInviteLink("notalink", User(1, '', False), False, True) - d3 = ChatInviteLink("notalink", User(1, '', False), True, True) - e = User(1, '', False) + a = ChatInviteLink("link", User(1, "", False), True, True) + b = ChatInviteLink("link", User(1, "", False), True, True) + d = ChatInviteLink("link", User(2, "", False), False, True) + d2 = ChatInviteLink("notalink", User(1, "", False), False, True) + d3 = ChatInviteLink("notalink", User(1, "", False), True, True) + e = User(1, "", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_chatlocation.py b/tests/test_chatlocation.py index 1facfde2e63..6dabc5bdb25 100644 --- a/tests/test_chatlocation.py +++ b/tests/test_chatlocation.py @@ -22,28 +22,25 @@ from telegram import Location, ChatLocation, User -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chat_location(bot): return ChatLocation(TestChatLocation.location, TestChatLocation.address) class TestChatLocation: location = Location(123, 456) - address = 'The Shire' + address = "The Shire" - def test_slot_behaviour(self, chat_location, recwarn, mro_slots): + def test_slot_behaviour(self, chat_location, mro_slots): inst = chat_location for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.address = 'should give warning', self.address - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'location': self.location.to_dict(), - 'address': self.address, + "location": self.location.to_dict(), + "address": self.address, } chat_location = ChatLocation.de_json(json_dict, bot) @@ -54,15 +51,15 @@ def test_to_dict(self, chat_location): chat_location_dict = chat_location.to_dict() assert isinstance(chat_location_dict, dict) - assert chat_location_dict['location'] == chat_location.location.to_dict() - assert chat_location_dict['address'] == chat_location.address + assert chat_location_dict["location"] == chat_location.location.to_dict() + assert chat_location_dict["address"] == chat_location.address def test_equality(self, chat_location): a = chat_location b = ChatLocation(self.location, self.address) - c = ChatLocation(self.location, 'Mordor') + c = ChatLocation(self.location, "Mordor") d = ChatLocation(Location(456, 132), self.address) - e = User(456, '', False) + e = User(456, "", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py index ce4f0757c61..129eb826bea 100644 --- a/tests/test_chatmember.py +++ b/tests/test_chatmember.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. import datetime +import inspect from copy import deepcopy import pytest @@ -34,206 +35,198 @@ Dice, ) - -@pytest.fixture(scope='class') -def user(): - return User(1, 'First name', False) - - -@pytest.fixture( - scope="class", - params=[ - (ChatMemberOwner, ChatMember.CREATOR), - (ChatMemberAdministrator, ChatMember.ADMINISTRATOR), - (ChatMemberMember, ChatMember.MEMBER), - (ChatMemberRestricted, ChatMember.RESTRICTED), - (ChatMemberLeft, ChatMember.LEFT), - (ChatMemberBanned, ChatMember.KICKED), - ], - ids=[ - ChatMember.CREATOR, - ChatMember.ADMINISTRATOR, - ChatMember.MEMBER, - ChatMember.RESTRICTED, - ChatMember.LEFT, - ChatMember.KICKED, +ignored = ["self", "_kwargs"] + + +class CMDefaults: + user = User(1, "First name", False) + custom_title: str = "PTB" + is_anonymous: bool = True + until_date: datetime.datetime = to_timestamp(datetime.datetime.utcnow()) + can_be_edited: bool = False + can_change_info: bool = True + can_post_messages: bool = True + can_edit_messages: bool = True + can_delete_messages: bool = True + can_invite_users: bool = True + can_restrict_members: bool = True + can_pin_messages: bool = True + can_promote_members: bool = True + can_send_messages: bool = True + can_send_media_messages: bool = True + can_send_polls: bool = True + can_send_other_messages: bool = True + can_add_web_page_previews: bool = True + is_member: bool = True + can_manage_chat: bool = True + can_manage_voice_chats: bool = True + + +def chat_member_owner(): + return ChatMemberOwner(CMDefaults.user, CMDefaults.is_anonymous, CMDefaults.custom_title) + + +def chat_member_administrator(): + return ChatMemberAdministrator( + CMDefaults.user, + CMDefaults.can_be_edited, + CMDefaults.is_anonymous, + CMDefaults.can_manage_chat, + CMDefaults.can_delete_messages, + CMDefaults.can_manage_voice_chats, + CMDefaults.can_restrict_members, + CMDefaults.can_promote_members, + CMDefaults.can_change_info, + CMDefaults.can_invite_users, + CMDefaults.can_post_messages, + CMDefaults.can_edit_messages, + CMDefaults.can_pin_messages, + CMDefaults.custom_title, + ) + + +def chat_member_member(): + return ChatMemberMember(CMDefaults.user) + + +def chat_member_restricted(): + return ChatMemberRestricted( + CMDefaults.user, + CMDefaults.is_member, + CMDefaults.can_change_info, + CMDefaults.can_invite_users, + CMDefaults.can_pin_messages, + CMDefaults.can_send_messages, + CMDefaults.can_send_media_messages, + CMDefaults.can_send_polls, + CMDefaults.can_send_other_messages, + CMDefaults.can_add_web_page_previews, + CMDefaults.until_date, + ) + + +def chat_member_left(): + return ChatMemberLeft(CMDefaults.user) + + +def chat_member_banned(): + return ChatMemberBanned(CMDefaults.user, CMDefaults.until_date) + + +def make_json_dict(instance: ChatMember, include_optional_args: bool = False) -> dict: + """Used to make the json dict which we use for testing de_json. Similar to iter_args()""" + json_dict = {"status": instance.status} + sig = inspect.signature(instance.__class__.__init__) + + for param in sig.parameters.values(): + if param.name in ignored: # ignore irrelevant params + continue + + val = getattr(instance, param.name) + # Compulsory args- + if param.default is inspect.Parameter.empty: + if hasattr(val, "to_dict"): # convert the user object or any future ones to dict. + val = val.to_dict() + json_dict[param.name] = val + + # If we want to test all args (for de_json)- + elif param.default is not inspect.Parameter.empty and include_optional_args: + json_dict[param.name] = val + return json_dict + + +def iter_args(instance: ChatMember, de_json_inst: ChatMember, include_optional: bool = False): + """ + We accept both the regular instance and de_json created instance and iterate over them for + easy one line testing later one. + """ + yield instance.status, de_json_inst.status # yield this here cause it's not available in sig. + + sig = inspect.signature(instance.__class__.__init__) + for param in sig.parameters.values(): + if param.name in ignored: + continue + inst_at, json_at = getattr(instance, param.name), getattr(de_json_inst, param.name) + if isinstance(json_at, datetime.datetime): # Convert datetime to int + json_at = to_timestamp(json_at) + if param.default is not inspect.Parameter.empty and include_optional: + yield inst_at, json_at + elif param.default is inspect.Parameter.empty: + yield inst_at, json_at + + +@pytest.fixture +def chat_member_type(request): + return request.param() + + +@pytest.mark.parametrize( + "chat_member_type", + [ + chat_member_owner, + chat_member_administrator, + chat_member_member, + chat_member_restricted, + chat_member_left, + chat_member_banned, ], + indirect=True, ) -def chat_member_class_and_status(request): - return request.param - - -@pytest.fixture(scope='class') -def chat_member_types(chat_member_class_and_status, user): - return chat_member_class_and_status[0](status=chat_member_class_and_status[1], user=user) - - -class TestChatMember: - def test_slot_behaviour(self, chat_member_types, mro_slots, recwarn): - for attr in chat_member_types.__slots__: - assert getattr(chat_member_types, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not chat_member_types.__dict__, f"got missing slot(s): {chat_member_types.__dict__}" - assert len(mro_slots(chat_member_types)) == len( - set(mro_slots(chat_member_types)) - ), "duplicate slot" - chat_member_types.custom, chat_member_types.status = 'warning!', chat_member_types.status - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list +class TestChatMemberTypes: + def test_slot_behaviour(self, chat_member_type, mro_slots): + inst = chat_member_type + for attr in inst.__slots__: + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" + + def test_de_json_required_args(self, bot, chat_member_type): + cls = chat_member_type.__class__ + assert cls.de_json({}, bot) is None - def test_de_json_required_args(self, bot, chat_member_class_and_status, user): - cls = chat_member_class_and_status[0] - status = chat_member_class_and_status[1] + json_dict = make_json_dict(chat_member_type) + const_chat_member = ChatMember.de_json(json_dict, bot) - assert cls.de_json({}, bot) is None + assert isinstance(const_chat_member, ChatMember) + assert isinstance(const_chat_member, cls) + for chat_mem_type_at, const_chat_mem_at in iter_args(chat_member_type, const_chat_member): + assert chat_mem_type_at == const_chat_mem_at - json_dict = {'status': status, 'user': user.to_dict()} - chat_member_type = ChatMember.de_json(json_dict, bot) + def test_de_json_all_args(self, bot, chat_member_type): + json_dict = make_json_dict(chat_member_type, include_optional_args=True) + const_chat_member = ChatMember.de_json(json_dict, bot) - assert isinstance(chat_member_type, ChatMember) - assert isinstance(chat_member_type, cls) - assert chat_member_type.status == status - assert chat_member_type.user == user - - def test_de_json_all_args(self, bot, chat_member_class_and_status, user): - cls = chat_member_class_and_status[0] - status = chat_member_class_and_status[1] - time = datetime.datetime.utcnow() - - json_dict = { - 'user': user.to_dict(), - 'status': status, - 'custom_title': 'PTB', - 'is_anonymous': True, - 'until_date': to_timestamp(time), - 'can_be_edited': False, - 'can_change_info': True, - 'can_post_messages': False, - 'can_edit_messages': True, - 'can_delete_messages': True, - 'can_invite_users': False, - 'can_restrict_members': True, - 'can_pin_messages': False, - 'can_promote_members': True, - 'can_send_messages': False, - 'can_send_media_messages': True, - 'can_send_polls': False, - 'can_send_other_messages': True, - 'can_add_web_page_previews': False, - 'can_manage_chat': True, - 'can_manage_voice_chats': True, - } - chat_member_type = ChatMember.de_json(json_dict, bot) + assert isinstance(const_chat_member, ChatMember) + assert isinstance(const_chat_member, chat_member_type.__class__) + for c_mem_type_at, const_c_mem_at in iter_args(chat_member_type, const_chat_member, True): + assert c_mem_type_at == const_c_mem_at - assert isinstance(chat_member_type, ChatMember) - assert isinstance(chat_member_type, cls) - assert chat_member_type.user == user - assert chat_member_type.status == status - if chat_member_type.custom_title is not None: - assert chat_member_type.custom_title == 'PTB' - assert type(chat_member_type) in {ChatMemberOwner, ChatMemberAdministrator} - if chat_member_type.is_anonymous is not None: - assert chat_member_type.is_anonymous is True - assert type(chat_member_type) in {ChatMemberOwner, ChatMemberAdministrator} - if chat_member_type.until_date is not None: - assert type(chat_member_type) in {ChatMemberBanned, ChatMemberRestricted} - if chat_member_type.can_be_edited is not None: - assert chat_member_type.can_be_edited is False - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_change_info is not None: - assert chat_member_type.can_change_info is True - assert type(chat_member_type) in {ChatMemberAdministrator, ChatMemberRestricted} - if chat_member_type.can_post_messages is not None: - assert chat_member_type.can_post_messages is False - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_edit_messages is not None: - assert chat_member_type.can_edit_messages is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_delete_messages is not None: - assert chat_member_type.can_delete_messages is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_invite_users is not None: - assert chat_member_type.can_invite_users is False - assert type(chat_member_type) in {ChatMemberAdministrator, ChatMemberRestricted} - if chat_member_type.can_restrict_members is not None: - assert chat_member_type.can_restrict_members is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_pin_messages is not None: - assert chat_member_type.can_pin_messages is False - assert type(chat_member_type) in {ChatMemberAdministrator, ChatMemberRestricted} - if chat_member_type.can_promote_members is not None: - assert chat_member_type.can_promote_members is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_send_messages is not None: - assert chat_member_type.can_send_messages is False - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_send_media_messages is not None: - assert chat_member_type.can_send_media_messages is True - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_send_polls is not None: - assert chat_member_type.can_send_polls is False - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_send_other_messages is not None: - assert chat_member_type.can_send_other_messages is True - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_add_web_page_previews is not None: - assert chat_member_type.can_add_web_page_previews is False - assert type(chat_member_type) == ChatMemberRestricted - if chat_member_type.can_manage_chat is not None: - assert chat_member_type.can_manage_chat is True - assert type(chat_member_type) == ChatMemberAdministrator - if chat_member_type.can_manage_voice_chats is not None: - assert chat_member_type.can_manage_voice_chats is True - assert type(chat_member_type) == ChatMemberAdministrator - - def test_de_json_invalid_status(self, bot, user): - json_dict = {'status': 'invalid', 'user': user.to_dict()} + def test_de_json_invalid_status(self, chat_member_type, bot): + json_dict = {"status": "invalid", "user": CMDefaults.user.to_dict()} chat_member_type = ChatMember.de_json(json_dict, bot) assert type(chat_member_type) is ChatMember - assert chat_member_type.status == 'invalid' + assert chat_member_type.status == "invalid" - def test_de_json_subclass(self, chat_member_class_and_status, bot, chat_id, user): + def test_de_json_subclass(self, chat_member_type, bot, chat_id): """This makes sure that e.g. ChatMemberAdministrator(data, bot) never returns a - ChatMemberKicked instance.""" - cls = chat_member_class_and_status[0] - time = datetime.datetime.utcnow() - json_dict = { - 'user': user.to_dict(), - 'status': 'status', - 'custom_title': 'PTB', - 'is_anonymous': True, - 'until_date': to_timestamp(time), - 'can_be_edited': False, - 'can_change_info': True, - 'can_post_messages': False, - 'can_edit_messages': True, - 'can_delete_messages': True, - 'can_invite_users': False, - 'can_restrict_members': True, - 'can_pin_messages': False, - 'can_promote_members': True, - 'can_send_messages': False, - 'can_send_media_messages': True, - 'can_send_polls': False, - 'can_send_other_messages': True, - 'can_add_web_page_previews': False, - 'can_manage_chat': True, - 'can_manage_voice_chats': True, - } + ChatMemberBanned instance.""" + cls = chat_member_type.__class__ + json_dict = make_json_dict(chat_member_type, True) assert type(cls.de_json(json_dict, bot)) is cls - def test_to_dict(self, chat_member_types, user): - chat_member_dict = chat_member_types.to_dict() + def test_to_dict(self, chat_member_type): + chat_member_dict = chat_member_type.to_dict() assert isinstance(chat_member_dict, dict) - assert chat_member_dict['status'] == chat_member_types.status - assert chat_member_dict['user'] == user.to_dict() - - def test_equality(self, chat_member_types, user): - a = ChatMember(status='status', user=user) - b = ChatMember(status='status', user=user) - c = chat_member_types - d = deepcopy(chat_member_types) - e = Dice(4, 'emoji') + assert chat_member_dict["status"] == chat_member_type.status + assert chat_member_dict["user"] == chat_member_type.user.to_dict() + + def test_equality(self, chat_member_type): + a = ChatMember(status="status", user=CMDefaults.user) + b = ChatMember(status="status", user=CMDefaults.user) + c = chat_member_type + d = deepcopy(chat_member_type) + e = Dice(4, "emoji") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_chatmemberhandler.py b/tests/test_chatmemberhandler.py index 1fc75c71d61..425dde70204 100644 --- a/tests/test_chatmemberhandler.py +++ b/tests/test_chatmemberhandler.py @@ -37,50 +37,50 @@ from telegram.ext import CallbackContext, JobQueue, ChatMemberHandler from telegram.utils.helpers import from_timestamp -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chat_member_updated(): return ChatMemberUpdated( - Chat(1, 'chat'), - User(1, '', False), + Chat(1, "chat"), + User(1, "", False), from_timestamp(int(time.time())), - ChatMember(User(1, '', False), ChatMember.CREATOR), - ChatMember(User(1, '', False), ChatMember.CREATOR), + ChatMember(User(1, "", False), ChatMember.CREATOR), + ChatMember(User(1, "", False), ChatMember.CREATOR), ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def chat_member(bot, chat_member_updated): return Update(0, my_chat_member=chat_member_updated) @@ -88,36 +88,16 @@ def chat_member(bot, chat_member_updated): class TestChatMemberHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - action = ChatMemberHandler(self.callback_basic) + def test_slot_behaviour(self, mro_slots): + action = ChatMemberHandler(self.callback_context) for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -131,30 +111,21 @@ def callback_context(self, update, context): and isinstance(update.chat_member or update.my_chat_member, ChatMemberUpdated) ) - def test_basic(self, dp, chat_member): - handler = ChatMemberHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(chat_member) - - dp.process_update(chat_member) - assert self.test_flag - @pytest.mark.parametrize( - argnames=['allowed_types', 'expected'], + argnames=["allowed_types", "expected"], argvalues=[ (ChatMemberHandler.MY_CHAT_MEMBER, (True, False)), (ChatMemberHandler.CHAT_MEMBER, (False, True)), (ChatMemberHandler.ANY_CHAT_MEMBER, (True, True)), ], - ids=['MY_CHAT_MEMBER', 'CHAT_MEMBER', 'ANY_CHAT_MEMBER'], + ids=["MY_CHAT_MEMBER", "CHAT_MEMBER", "ANY_CHAT_MEMBER"], ) def test_chat_member_types( self, dp, chat_member_updated, chat_member, expected, allowed_types ): result_1, result_2 = expected - handler = ChatMemberHandler(self.callback_basic, chat_member_types=allowed_types) + handler = ChatMemberHandler(self.callback_context, chat_member_types=allowed_types) dp.add_handler(handler) assert handler.check_update(chat_member) == result_1 @@ -169,62 +140,14 @@ def test_chat_member_types( dp.process_update(chat_member) assert self.test_flag == result_2 - def test_pass_user_or_chat_data(self, dp, chat_member): - handler = ChatMemberHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(chat_member) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChatMemberHandler(self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chat_member) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChatMemberHandler(self.callback_data_2, pass_chat_data=True, pass_user_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chat_member) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, chat_member): - handler = ChatMemberHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(chat_member) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChatMemberHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chat_member) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChatMemberHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chat_member) - assert self.test_flag - def test_other_update_types(self, false_update): - handler = ChatMemberHandler(self.callback_basic) + handler = ChatMemberHandler(self.callback_context) assert not handler.check_update(false_update) assert not handler.check_update(True) - def test_context(self, cdp, chat_member): + def test_context(self, dp, chat_member): handler = ChatMemberHandler(self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(chat_member) + dp.process_update(chat_member) assert self.test_flag diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py index d90e83761f1..2e8e9c7a5b8 100644 --- a/tests/test_chatmemberupdated.py +++ b/tests/test_chatmemberupdated.py @@ -22,41 +22,60 @@ import pytest import pytz -from telegram import User, ChatMember, Chat, ChatMemberUpdated, ChatInviteLink +from telegram import ( + User, + ChatMember, + ChatMemberAdministrator, + Chat, + ChatMemberUpdated, + ChatInviteLink, +) from telegram.utils.helpers import to_timestamp -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def user(): - return User(1, 'First name', False) + return User(1, "First name", False) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chat(): - return Chat(1, Chat.SUPERGROUP, 'Chat') + return Chat(1, Chat.SUPERGROUP, "Chat") -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def old_chat_member(user): return ChatMember(user, TestChatMemberUpdated.old_status) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def new_chat_member(user): - return ChatMember(user, TestChatMemberUpdated.new_status) + return ChatMemberAdministrator( + user, + TestChatMemberUpdated.new_status, + True, + True, + True, + True, + True, + True, + True, + True, + True, + ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def time(): return datetime.datetime.now(tz=pytz.utc) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def invite_link(user): - return ChatInviteLink('link', user, True, True) + return ChatInviteLink("link", user, True, True) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chat_member_updated(user, chat, old_chat_member, new_chat_member, invite_link, time): return ChatMemberUpdated(chat, user, time, old_chat_member, new_chat_member, invite_link) @@ -65,22 +84,19 @@ class TestChatMemberUpdated: old_status = ChatMember.MEMBER new_status = ChatMember.ADMINISTRATOR - def test_slot_behaviour(self, recwarn, mro_slots, chat_member_updated): + def test_slot_behaviour(self, mro_slots, chat_member_updated): action = chat_member_updated for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json_required_args(self, bot, user, chat, old_chat_member, new_chat_member, time): json_dict = { - 'chat': chat.to_dict(), - 'from': user.to_dict(), - 'date': to_timestamp(time), - 'old_chat_member': old_chat_member.to_dict(), - 'new_chat_member': new_chat_member.to_dict(), + "chat": chat.to_dict(), + "from": user.to_dict(), + "date": to_timestamp(time), + "old_chat_member": old_chat_member.to_dict(), + "new_chat_member": new_chat_member.to_dict(), } chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot) @@ -97,12 +113,12 @@ def test_de_json_all_args( self, bot, user, time, invite_link, chat, old_chat_member, new_chat_member ): json_dict = { - 'chat': chat.to_dict(), - 'from': user.to_dict(), - 'date': to_timestamp(time), - 'old_chat_member': old_chat_member.to_dict(), - 'new_chat_member': new_chat_member.to_dict(), - 'invite_link': invite_link.to_dict(), + "chat": chat.to_dict(), + "from": user.to_dict(), + "date": to_timestamp(time), + "old_chat_member": old_chat_member.to_dict(), + "new_chat_member": new_chat_member.to_dict(), + "invite_link": invite_link.to_dict(), } chat_member_updated = ChatMemberUpdated.de_json(json_dict, bot) @@ -118,65 +134,65 @@ def test_de_json_all_args( def test_to_dict(self, chat_member_updated): chat_member_updated_dict = chat_member_updated.to_dict() assert isinstance(chat_member_updated_dict, dict) - assert chat_member_updated_dict['chat'] == chat_member_updated.chat.to_dict() - assert chat_member_updated_dict['from'] == chat_member_updated.from_user.to_dict() - assert chat_member_updated_dict['date'] == to_timestamp(chat_member_updated.date) + assert chat_member_updated_dict["chat"] == chat_member_updated.chat.to_dict() + assert chat_member_updated_dict["from"] == chat_member_updated.from_user.to_dict() + assert chat_member_updated_dict["date"] == to_timestamp(chat_member_updated.date) assert ( - chat_member_updated_dict['old_chat_member'] + chat_member_updated_dict["old_chat_member"] == chat_member_updated.old_chat_member.to_dict() ) assert ( - chat_member_updated_dict['new_chat_member'] + chat_member_updated_dict["new_chat_member"] == chat_member_updated.new_chat_member.to_dict() ) - assert chat_member_updated_dict['invite_link'] == chat_member_updated.invite_link.to_dict() + assert chat_member_updated_dict["invite_link"] == chat_member_updated.invite_link.to_dict() def test_equality(self, time, old_chat_member, new_chat_member, invite_link): a = ChatMemberUpdated( - Chat(1, 'chat'), - User(1, '', False), + Chat(1, "chat"), + User(1, "", False), time, old_chat_member, new_chat_member, invite_link, ) b = ChatMemberUpdated( - Chat(1, 'chat'), User(1, '', False), time, old_chat_member, new_chat_member + Chat(1, "chat"), User(1, "", False), time, old_chat_member, new_chat_member ) # wrong date c = ChatMemberUpdated( - Chat(1, 'chat'), - User(1, '', False), + Chat(1, "chat"), + User(1, "", False), time + datetime.timedelta(hours=1), old_chat_member, new_chat_member, ) # wrong chat & form_user d = ChatMemberUpdated( - Chat(42, 'wrong_chat'), - User(42, 'wrong_user', False), + Chat(42, "wrong_chat"), + User(42, "wrong_user", False), time, old_chat_member, new_chat_member, ) # wrong old_chat_member e = ChatMemberUpdated( - Chat(1, 'chat'), - User(1, '', False), + Chat(1, "chat"), + User(1, "", False), time, - ChatMember(User(1, '', False), ChatMember.CREATOR), + ChatMember(User(1, "", False), ChatMember.CREATOR), new_chat_member, ) # wrong new_chat_member f = ChatMemberUpdated( - Chat(1, 'chat'), - User(1, '', False), + Chat(1, "chat"), + User(1, "", False), time, old_chat_member, - ChatMember(User(1, '', False), ChatMember.CREATOR), + ChatMember(User(1, "", False), ChatMember.CREATOR), ) # wrong type - g = ChatMember(User(1, '', False), ChatMember.CREATOR) + g = ChatMember(User(1, "", False), ChatMember.CREATOR) assert a == b assert hash(a) == hash(b) @@ -187,29 +203,29 @@ def test_equality(self, time, old_chat_member, new_chat_member, invite_link): assert hash(a) != hash(other) def test_difference_required(self, user, chat): - old_chat_member = ChatMember(user, 'old_status') - new_chat_member = ChatMember(user, 'new_status') + old_chat_member = ChatMember(user, "old_status") + new_chat_member = ChatMember(user, "new_status") chat_member_updated = ChatMemberUpdated( chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member ) - assert chat_member_updated.difference() == {'status': ('old_status', 'new_status')} + assert chat_member_updated.difference() == {"status": ("old_status", "new_status")} # We deliberately change an optional argument here to make sure that comparision doesn't # just happens by id/required args - new_user = User(1, 'First name', False, last_name='last name') + new_user = User(1, "First name", False, last_name="last name") new_chat_member.user = new_user assert chat_member_updated.difference() == { - 'status': ('old_status', 'new_status'), - 'user': (user, new_user), + "status": ("old_status", "new_status"), + "user": (user, new_user), } @pytest.mark.parametrize( - 'optional_attribute', + "optional_attribute", # This gives the names of all optional arguments of ChatMember [ name for name, param in inspect.signature(ChatMember).parameters.items() - if name != 'self' and param.default != inspect.Parameter.empty + if name != "self" and param.default != inspect.Parameter.empty ], ) def test_difference_optionals(self, optional_attribute, user, chat): @@ -217,8 +233,8 @@ def test_difference_optionals(self, optional_attribute, user, chat): # the other attributes old_value = datetime.datetime(2020, 1, 1) new_value = datetime.datetime(2021, 1, 1) - old_chat_member = ChatMember(user, 'status', **{optional_attribute: old_value}) - new_chat_member = ChatMember(user, 'status', **{optional_attribute: new_value}) + old_chat_member = ChatMember(user, "status", **{optional_attribute: old_value}) + new_chat_member = ChatMember(user, "status", **{optional_attribute: new_value}) chat_member_updated = ChatMemberUpdated( chat, user, datetime.datetime.utcnow(), old_chat_member, new_chat_member ) diff --git a/tests/test_chatpermissions.py b/tests/test_chatpermissions.py index c47ae6669c3..0060ac00e27 100644 --- a/tests/test_chatpermissions.py +++ b/tests/test_chatpermissions.py @@ -46,25 +46,22 @@ class TestChatPermissions: can_invite_users = None can_pin_messages = None - def test_slot_behaviour(self, chat_permissions, recwarn, mro_slots): + def test_slot_behaviour(self, chat_permissions, mro_slots): inst = chat_permissions for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.can_send_polls = 'should give warning', self.can_send_polls - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'can_send_messages': self.can_send_messages, - 'can_send_media_messages': self.can_send_media_messages, - 'can_send_polls': self.can_send_polls, - 'can_send_other_messages': self.can_send_other_messages, - 'can_add_web_page_previews': self.can_add_web_page_previews, - 'can_change_info': self.can_change_info, - 'can_invite_users': self.can_invite_users, - 'can_pin_messages': self.can_pin_messages, + "can_send_messages": self.can_send_messages, + "can_send_media_messages": self.can_send_media_messages, + "can_send_polls": self.can_send_polls, + "can_send_other_messages": self.can_send_other_messages, + "can_add_web_page_previews": self.can_add_web_page_previews, + "can_change_info": self.can_change_info, + "can_invite_users": self.can_invite_users, + "can_pin_messages": self.can_pin_messages, } permissions = ChatPermissions.de_json(json_dict, bot) @@ -81,21 +78,21 @@ def test_to_dict(self, chat_permissions): permissions_dict = chat_permissions.to_dict() assert isinstance(permissions_dict, dict) - assert permissions_dict['can_send_messages'] == chat_permissions.can_send_messages + assert permissions_dict["can_send_messages"] == chat_permissions.can_send_messages assert ( - permissions_dict['can_send_media_messages'] == chat_permissions.can_send_media_messages + permissions_dict["can_send_media_messages"] == chat_permissions.can_send_media_messages ) - assert permissions_dict['can_send_polls'] == chat_permissions.can_send_polls + assert permissions_dict["can_send_polls"] == chat_permissions.can_send_polls assert ( - permissions_dict['can_send_other_messages'] == chat_permissions.can_send_other_messages + permissions_dict["can_send_other_messages"] == chat_permissions.can_send_other_messages ) assert ( - permissions_dict['can_add_web_page_previews'] + permissions_dict["can_add_web_page_previews"] == chat_permissions.can_add_web_page_previews ) - assert permissions_dict['can_change_info'] == chat_permissions.can_change_info - assert permissions_dict['can_invite_users'] == chat_permissions.can_invite_users - assert permissions_dict['can_pin_messages'] == chat_permissions.can_pin_messages + assert permissions_dict["can_change_info"] == chat_permissions.can_change_info + assert permissions_dict["can_invite_users"] == chat_permissions.can_invite_users + assert permissions_dict["can_pin_messages"] == chat_permissions.can_pin_messages def test_equality(self): a = ChatPermissions( @@ -116,7 +113,7 @@ def test_equality(self): can_send_polls=True, can_send_other_messages=False, ) - d = User(123, '', False) + d = User(123, "", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_chatphoto.py b/tests/test_chatphoto.py index 3676b0e1b81..032faab2692 100644 --- a/tests/test_chatphoto.py +++ b/tests/test_chatphoto.py @@ -29,77 +29,74 @@ ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def chatphoto_file(): - f = open('tests/data/telegram.jpg', 'rb') + f = open("tests/data/telegram.jpg", "rb") yield f f.close() -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def chat_photo(bot, super_group_id): def func(): return bot.get_chat(super_group_id, timeout=50).photo - return expect_bad_request(func, 'Type of file mismatch', 'Telegram did not accept the file.') + return expect_bad_request(func, "Type of file mismatch", "Telegram did not accept the file.") class TestChatPhoto: - chatphoto_small_file_id = 'smallCgADAQADngIAAuyVeEez0xRovKi9VAI' - chatphoto_big_file_id = 'bigCgADAQADngIAAuyVeEez0xRovKi9VAI' - chatphoto_small_file_unique_id = 'smalladc3145fd2e84d95b64d68eaa22aa33e' - chatphoto_big_file_unique_id = 'bigadc3145fd2e84d95b64d68eaa22aa33e' - chatphoto_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.jpg' + chatphoto_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI" + chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI" + chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e" + chatphoto_big_file_unique_id = "bigadc3145fd2e84d95b64d68eaa22aa33e" + chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg" - def test_slot_behaviour(self, chat_photo, recwarn, mro_slots): + def test_slot_behaviour(self, chat_photo, mro_slots): for attr in chat_photo.__slots__: - assert getattr(chat_photo, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not chat_photo.__dict__, f"got missing slot(s): {chat_photo.__dict__}" + assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(chat_photo)) == len(set(mro_slots(chat_photo))), "duplicate slot" - chat_photo.custom, chat_photo.big_file_id = 'gives warning', self.chatphoto_big_file_id - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @flaky(3, 1) def test_send_all_args(self, bot, super_group_id, chatphoto_file, chat_photo, thumb_file): def func(): assert bot.set_chat_photo(super_group_id, chatphoto_file) - expect_bad_request(func, 'Type of file mismatch', 'Telegram did not accept the file.') + expect_bad_request(func, "Type of file mismatch", "Telegram did not accept the file.") @flaky(3, 1) def test_get_and_download(self, bot, chat_photo): new_file = bot.get_file(chat_photo.small_file_id) assert new_file.file_id == chat_photo.small_file_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.jpg') + new_file.download("telegram.jpg") - assert os.path.isfile('telegram.jpg') + assert os.path.isfile("telegram.jpg") new_file = bot.get_file(chat_photo.big_file_id) assert new_file.file_id == chat_photo.big_file_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.jpg') + new_file.download("telegram.jpg") - assert os.path.isfile('telegram.jpg') + assert os.path.isfile("telegram.jpg") def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo): def test(url, data, **kwargs): - return data['photo'] == chat_photo + return data["photo"] == chat_photo - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id) assert message def test_de_json(self, bot, chat_photo): json_dict = { - 'small_file_id': self.chatphoto_small_file_id, - 'big_file_id': self.chatphoto_big_file_id, - 'small_file_unique_id': self.chatphoto_small_file_unique_id, - 'big_file_unique_id': self.chatphoto_big_file_unique_id, + "small_file_id": self.chatphoto_small_file_id, + "big_file_id": self.chatphoto_big_file_id, + "small_file_unique_id": self.chatphoto_small_file_unique_id, + "big_file_unique_id": self.chatphoto_big_file_unique_id, } chat_photo = ChatPhoto.de_json(json_dict, bot) assert chat_photo.small_file_id == self.chatphoto_small_file_id @@ -111,14 +108,14 @@ def test_to_dict(self, chat_photo): chat_photo_dict = chat_photo.to_dict() assert isinstance(chat_photo_dict, dict) - assert chat_photo_dict['small_file_id'] == chat_photo.small_file_id - assert chat_photo_dict['big_file_id'] == chat_photo.big_file_id - assert chat_photo_dict['small_file_unique_id'] == chat_photo.small_file_unique_id - assert chat_photo_dict['big_file_unique_id'] == chat_photo.big_file_unique_id + assert chat_photo_dict["small_file_id"] == chat_photo.small_file_id + assert chat_photo_dict["big_file_id"] == chat_photo.big_file_id + assert chat_photo_dict["small_file_unique_id"] == chat_photo.small_file_unique_id + assert chat_photo_dict["big_file_unique_id"] == chat_photo.big_file_unique_id @flaky(3, 1) def test_error_send_empty_file(self, bot, super_group_id): - chatphoto_file = open(os.devnull, 'rb') + chatphoto_file = open(os.devnull, "rb") with pytest.raises(TelegramError): bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file) @@ -126,7 +123,7 @@ def test_error_send_empty_file(self, bot, super_group_id): @flaky(3, 1) def test_error_send_empty_file_id(self, bot, super_group_id): with pytest.raises(TelegramError): - bot.set_chat_photo(chat_id=super_group_id, photo='') + bot.set_chat_photo(chat_id=super_group_id, photo="") def test_error_send_without_required_args(self, bot, super_group_id): with pytest.raises(TypeError): @@ -134,24 +131,24 @@ def test_error_send_without_required_args(self, bot, super_group_id): def test_get_small_file_instance_method(self, monkeypatch, chat_photo): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == chat_photo.small_file_id + return kwargs["file_id"] == chat_photo.small_file_id - assert check_shortcut_signature(ChatPhoto.get_small_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(chat_photo.get_small_file, chat_photo.bot, 'get_file') + assert check_shortcut_signature(ChatPhoto.get_small_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(chat_photo.get_small_file, chat_photo.bot, "get_file") assert check_defaults_handling(chat_photo.get_small_file, chat_photo.bot) - monkeypatch.setattr(chat_photo.bot, 'get_file', make_assertion) + monkeypatch.setattr(chat_photo.bot, "get_file", make_assertion) assert chat_photo.get_small_file() def test_get_big_file_instance_method(self, monkeypatch, chat_photo): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == chat_photo.big_file_id + return kwargs["file_id"] == chat_photo.big_file_id - assert check_shortcut_signature(ChatPhoto.get_big_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(chat_photo.get_big_file, chat_photo.bot, 'get_file') + assert check_shortcut_signature(ChatPhoto.get_big_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(chat_photo.get_big_file, chat_photo.bot, "get_file") assert check_defaults_handling(chat_photo.get_big_file, chat_photo.bot) - monkeypatch.setattr(chat_photo.bot, 'get_file', make_assertion) + monkeypatch.setattr(chat_photo.bot, "get_file", make_assertion) assert chat_photo.get_big_file() def test_equality(self): @@ -168,9 +165,9 @@ def test_equality(self): self.chatphoto_big_file_unique_id, ) c = ChatPhoto( - '', '', self.chatphoto_small_file_unique_id, self.chatphoto_big_file_unique_id + "", "", self.chatphoto_small_file_unique_id, self.chatphoto_big_file_unique_id ) - d = ChatPhoto('', '', 0, 0) + d = ChatPhoto("", "", 0, 0) e = Voice(self.chatphoto_small_file_id, self.chatphoto_small_file_unique_id, 0) assert a == b diff --git a/tests/test_choseninlineresult.py b/tests/test_choseninlineresult.py index a6a797ce076..66742d08b3d 100644 --- a/tests/test_choseninlineresult.py +++ b/tests/test_choseninlineresult.py @@ -22,31 +22,28 @@ from telegram import User, ChosenInlineResult, Location, Voice -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def user(): - return User(1, 'First name', False) + return User(1, "First name", False) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chosen_inline_result(user): return ChosenInlineResult(TestChosenInlineResult.result_id, user, TestChosenInlineResult.query) class TestChosenInlineResult: - result_id = 'result id' - query = 'query text' + result_id = "result id" + query = "query text" - def test_slot_behaviour(self, chosen_inline_result, recwarn, mro_slots): + def test_slot_behaviour(self, chosen_inline_result, mro_slots): inst = chosen_inline_result for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.result_id = 'should give warning', self.result_id - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json_required(self, bot, user): - json_dict = {'result_id': self.result_id, 'from': user.to_dict(), 'query': self.query} + json_dict = {"result_id": self.result_id, "from": user.to_dict(), "query": self.query} result = ChosenInlineResult.de_json(json_dict, bot) assert result.result_id == self.result_id @@ -56,11 +53,11 @@ def test_de_json_required(self, bot, user): def test_de_json_all(self, bot, user): loc = Location(-42.003, 34.004) json_dict = { - 'result_id': self.result_id, - 'from': user.to_dict(), - 'query': self.query, - 'location': loc.to_dict(), - 'inline_message_id': 'a random id', + "result_id": self.result_id, + "from": user.to_dict(), + "query": self.query, + "location": loc.to_dict(), + "inline_message_id": "a random id", } result = ChosenInlineResult.de_json(json_dict, bot) @@ -68,22 +65,22 @@ def test_de_json_all(self, bot, user): assert result.from_user == user assert result.query == self.query assert result.location == loc - assert result.inline_message_id == 'a random id' + assert result.inline_message_id == "a random id" def test_to_dict(self, chosen_inline_result): chosen_inline_result_dict = chosen_inline_result.to_dict() assert isinstance(chosen_inline_result_dict, dict) - assert chosen_inline_result_dict['result_id'] == chosen_inline_result.result_id - assert chosen_inline_result_dict['from'] == chosen_inline_result.from_user.to_dict() - assert chosen_inline_result_dict['query'] == chosen_inline_result.query + assert chosen_inline_result_dict["result_id"] == chosen_inline_result.result_id + assert chosen_inline_result_dict["from"] == chosen_inline_result.from_user.to_dict() + assert chosen_inline_result_dict["query"] == chosen_inline_result.query def test_equality(self, user): - a = ChosenInlineResult(self.result_id, user, 'Query', '') - b = ChosenInlineResult(self.result_id, user, 'Query', '') - c = ChosenInlineResult(self.result_id, user, '', '') - d = ChosenInlineResult('', user, 'Query', '') - e = Voice(self.result_id, 'unique_id', 0) + a = ChosenInlineResult(self.result_id, user, "Query", "") + b = ChosenInlineResult(self.result_id, user, "Query", "") + c = ChosenInlineResult(self.result_id, user, "", "") + d = ChosenInlineResult("", user, "Query", "") + e = Voice(self.result_id, "unique_id", 0) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_choseninlineresulthandler.py b/tests/test_choseninlineresulthandler.py index 1803a291b9c..240df160a89 100644 --- a/tests/test_choseninlineresulthandler.py +++ b/tests/test_choseninlineresulthandler.py @@ -34,43 +34,43 @@ ) from telegram.ext import ChosenInlineResultHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "inline_query", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=1, **request.param) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def chosen_inline_result(): return Update( 1, - chosen_inline_result=ChosenInlineResult('result_id', User(1, 'test_user', False), 'query'), + chosen_inline_result=ChosenInlineResult("result_id", User(1, "test_user", False), "query"), ) @@ -81,17 +81,14 @@ class TestChosenInlineResultHandler: def reset(self): self.test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): handler = ChosenInlineResultHandler(self.callback_basic) for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) + def callback_basic(self, update, context): + test_bot = isinstance(context.bot, Bot) test_update = isinstance(update, Update) self.test_flag = test_bot and test_update @@ -122,99 +119,41 @@ def callback_context(self, update, context): def callback_context_pattern(self, update, context): if context.matches[0].groups(): - self.test_flag = context.matches[0].groups() == ('res', '_id') + self.test_flag = context.matches[0].groups() == ("res", "_id") if context.matches[0].groupdict(): - self.test_flag = context.matches[0].groupdict() == {'begin': 'res', 'end': '_id'} - - def test_basic(self, dp, chosen_inline_result): - handler = ChosenInlineResultHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(chosen_inline_result) - dp.process_update(chosen_inline_result) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, chosen_inline_result): - handler = ChosenInlineResultHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(chosen_inline_result) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChosenInlineResultHandler(self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chosen_inline_result) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChosenInlineResultHandler( - self.callback_data_2, pass_chat_data=True, pass_user_data=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chosen_inline_result) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, chosen_inline_result): - handler = ChosenInlineResultHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(chosen_inline_result) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChosenInlineResultHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chosen_inline_result) - assert self.test_flag - - dp.remove_handler(handler) - handler = ChosenInlineResultHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(chosen_inline_result) - assert self.test_flag + self.test_flag = context.matches[0].groupdict() == {"begin": "res", "end": "_id"} def test_other_update_types(self, false_update): handler = ChosenInlineResultHandler(self.callback_basic) assert not handler.check_update(false_update) - def test_context(self, cdp, chosen_inline_result): + def test_context(self, dp, chosen_inline_result): handler = ChosenInlineResultHandler(self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(chosen_inline_result) + dp.process_update(chosen_inline_result) assert self.test_flag def test_with_pattern(self, chosen_inline_result): - handler = ChosenInlineResultHandler(self.callback_basic, pattern='.*ult.*') + handler = ChosenInlineResultHandler(self.callback_basic, pattern=".*ult.*") assert handler.check_update(chosen_inline_result) - chosen_inline_result.chosen_inline_result.result_id = 'nothing here' + chosen_inline_result.chosen_inline_result.result_id = "nothing here" assert not handler.check_update(chosen_inline_result) - chosen_inline_result.chosen_inline_result.result_id = 'result_id' + chosen_inline_result.chosen_inline_result.result_id = "result_id" - def test_context_pattern(self, cdp, chosen_inline_result): + def test_context_pattern(self, dp, chosen_inline_result): handler = ChosenInlineResultHandler( - self.callback_context_pattern, pattern=r'(?P.*)ult(?P.*)' + self.callback_context_pattern, pattern=r"(?P.*)ult(?P.*)" ) - cdp.add_handler(handler) - cdp.process_update(chosen_inline_result) + dp.add_handler(handler) + dp.process_update(chosen_inline_result) assert self.test_flag - cdp.remove_handler(handler) - handler = ChosenInlineResultHandler(self.callback_context_pattern, pattern=r'(res)ult(.*)') - cdp.add_handler(handler) + dp.remove_handler(handler) + handler = ChosenInlineResultHandler(self.callback_context_pattern, pattern=r"(res)ult(.*)") + dp.add_handler(handler) - cdp.process_update(chosen_inline_result) + dp.process_update(chosen_inline_result) assert self.test_flag diff --git a/tests/test_commandhandler.py b/tests/test_commandhandler.py index 6c6262545b2..ed9a6964dfb 100644 --- a/tests/test_commandhandler.py +++ b/tests/test_commandhandler.py @@ -20,8 +20,6 @@ from queue import Queue import pytest -import itertools -from telegram.utils.deprecate import TelegramDeprecationWarning from telegram import Message, Update, Chat, Bot from telegram.ext import CommandHandler, Filters, CallbackContext, JobQueue, PrefixHandler @@ -56,12 +54,6 @@ class BaseTest: def reset(self): self.test_flag = False - PASS_KEYWORDS = ('pass_user_data', 'pass_chat_data', 'pass_job_queue', 'pass_update_queue') - - @pytest.fixture(scope='module', params=itertools.combinations(PASS_KEYWORDS, 2)) - def pass_combination(self, request): - return {key: True for key in request.param} - def response(self, dispatcher, update): """ Utility to send an update to a dispatcher and assert @@ -72,8 +64,8 @@ def response(self, dispatcher, update): dispatcher.process_update(update) return self.test_flag - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) + def callback_basic(self, update, context): + test_bot = isinstance(context.bot, Bot) test_update = isinstance(update, Update) self.test_flag = test_bot and test_update @@ -98,7 +90,7 @@ def callback_context(self, update, context): ) def callback_context_args(self, update, context): - self.test_flag = context.args == ['one', 'two'] + self.test_flag = context.args == ["one", "two"] def callback_context_regex1(self, update, context): if context.matches: @@ -112,12 +104,12 @@ def callback_context_regex2(self, update, context): num = len(context.matches) == 2 self.test_flag = types and num - def _test_context_args_or_regex(self, cdp, handler, text): - cdp.add_handler(handler) + def _test_context_args_or_regex(self, dp, handler, text): + dp.add_handler(handler) update = make_command_update(text) - assert not self.response(cdp, update) - update.message.text += ' one two' - assert self.response(cdp, update) + assert not self.response(dp, update) + update.message.text += " one two" + assert self.response(dp, update) def _test_edited(self, message, handler_edited, handler_not_edited): """ @@ -140,37 +132,26 @@ def _test_edited(self, message, handler_edited, handler_not_edited): class TestCommandHandler(BaseTest): - CMD = '/test' + CMD = "/test" - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): handler = self.make_default_handler() for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.command = 'should give warning', self.CMD - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def command(self): return self.CMD - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def command_message(self, command): return make_command_message(command) - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def command_update(self, command_message): return make_command_update(command_message) - def ch_callback_args(self, bot, update, args): - if update.message.text == self.CMD: - self.test_flag = len(args) == 0 - elif update.message.text == f'{self.CMD}@{bot.username}': - self.test_flag = len(args) == 0 - else: - self.test_flag = args == ['one', 'two'] - def make_default_handler(self, callback=None, **kwargs): callback = callback or self.callback_basic return CommandHandler(self.CMD[1:], callback, **kwargs) @@ -183,78 +164,51 @@ def test_basic(self, dp, command): assert self.response(dp, make_command_update(command)) assert not is_match(handler, make_command_update(command[1:])) - assert not is_match(handler, make_command_update(f'/not{command[1:]}')) - assert not is_match(handler, make_command_update(f'not {command} at start')) + assert not is_match(handler, make_command_update(f"/not{command[1:]}")) + assert not is_match(handler, make_command_update(f"not {command} at start")) @pytest.mark.parametrize( - 'cmd', - ['way_too_longcommand1234567yes_way_toooooooLong', 'Γ―Γ±vΓ‘lΓ­dletters', 'invalid #&* chars'], - ids=['too long', 'invalid letter', 'invalid characters'], + "cmd", + ["way_too_longcommand1234567yes_way_toooooooLong", "Γ―Γ±vΓ‘lΓ­dletters", "invalid #&* chars"], + ids=["too long", "invalid letter", "invalid characters"], ) def test_invalid_commands(self, cmd): - with pytest.raises(ValueError, match='not a valid bot command'): + with pytest.raises(ValueError, match="not a valid bot command"): CommandHandler(cmd, self.callback_basic) def test_command_list(self): """A command handler with multiple commands registered should respond to all of them.""" - handler = CommandHandler(['test', 'star'], self.callback_basic) - assert is_match(handler, make_command_update('/test')) - assert is_match(handler, make_command_update('/star')) - assert not is_match(handler, make_command_update('/stop')) - - def test_deprecation_warning(self): - """``allow_edited`` deprecated in favor of filters""" - with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'): - self.make_default_handler(allow_edited=True) + handler = CommandHandler(["test", "star"], self.callback_basic) + assert is_match(handler, make_command_update("/test")) + assert is_match(handler, make_command_update("/star")) + assert not is_match(handler, make_command_update("/stop")) def test_edited(self, command_message): - """Test that a CH responds to an edited message iff its filters allow it""" + """Test that a CH responds to an edited message if its filters allow it""" handler_edited = self.make_default_handler() handler_no_edited = self.make_default_handler(filters=~Filters.update.edited_message) self._test_edited(command_message, handler_edited, handler_no_edited) - def test_edited_deprecated(self, command_message): - """Test that a CH responds to an edited message iff ``allow_edited`` is True""" - handler_edited = self.make_default_handler(allow_edited=True) - handler_no_edited = self.make_default_handler(allow_edited=False) - self._test_edited(command_message, handler_edited, handler_no_edited) - def test_directed_commands(self, bot, command): """Test recognition of commands with a mention to the bot""" handler = self.make_default_handler() - assert is_match(handler, make_command_update(command + '@' + bot.username, bot=bot)) - assert not is_match(handler, make_command_update(command + '@otherbot', bot=bot)) + assert is_match(handler, make_command_update(command + "@" + bot.username, bot=bot)) + assert not is_match(handler, make_command_update(command + "@otherbot", bot=bot)) def test_with_filter(self, command): - """Test that a CH with a (generic) filter responds iff its filters match""" - handler = self.make_default_handler(filters=Filters.group) + """Test that a CH with a (generic) filter responds if its filters match""" + handler = self.make_default_handler(filters=Filters.chat_type.group) assert is_match(handler, make_command_update(command, chat=Chat(-23, Chat.GROUP))) assert not is_match(handler, make_command_update(command, chat=Chat(23, Chat.PRIVATE))) - def test_pass_args(self, dp, bot, command): - """Test the passing of arguments alongside a command""" - handler = self.make_default_handler(self.ch_callback_args, pass_args=True) - dp.add_handler(handler) - at_command = f'{command}@{bot.username}' - assert self.response(dp, make_command_update(command)) - assert self.response(dp, make_command_update(command + ' one two')) - assert self.response(dp, make_command_update(at_command, bot=bot)) - assert self.response(dp, make_command_update(at_command + ' one two', bot=bot)) - def test_newline(self, dp, command): """Assert that newlines don't interfere with a command handler matching a message""" handler = self.make_default_handler() dp.add_handler(handler) - update = make_command_update(command + '\nfoobar') + update = make_command_update(command + "\nfoobar") assert is_match(handler, update) assert self.response(dp, update) - @pytest.mark.parametrize('pass_keyword', BaseTest.PASS_KEYWORDS) - def test_pass_data(self, dp, command_update, pass_combination, pass_keyword): - handler = CommandHandler('test', self.make_callback_for(pass_keyword), **pass_combination) - dp.add_handler(handler) - assert self.response(dp, command_update) == pass_combination.get(pass_keyword, False) - def test_other_update_types(self, false_update): """Test that a command handler doesn't respond to unrelated updates""" handler = self.make_default_handler() @@ -263,33 +217,33 @@ def test_other_update_types(self, false_update): def test_filters_for_wrong_command(self, mock_filter): """Filters should not be executed if the command does not match the handler""" handler = self.make_default_handler(filters=mock_filter) - assert not is_match(handler, make_command_update('/star')) + assert not is_match(handler, make_command_update("/star")) assert not mock_filter.tested - def test_context(self, cdp, command_update): + def test_context(self, dp, command_update): """Test correct behaviour of CHs with context-based callbacks""" handler = self.make_default_handler(self.callback_context) - cdp.add_handler(handler) - assert self.response(cdp, command_update) + dp.add_handler(handler) + assert self.response(dp, command_update) - def test_context_args(self, cdp, command): + def test_context_args(self, dp, command): """Test CHs that pass arguments through ``context``""" handler = self.make_default_handler(self.callback_context_args) - self._test_context_args_or_regex(cdp, handler, command) + self._test_context_args_or_regex(dp, handler, command) - def test_context_regex(self, cdp, command): + def test_context_regex(self, dp, command): """Test CHs with context-based callbacks and a single filter""" handler = self.make_default_handler( - self.callback_context_regex1, filters=Filters.regex('one two') + self.callback_context_regex1, filters=Filters.regex("one two") ) - self._test_context_args_or_regex(cdp, handler, command) + self._test_context_args_or_regex(dp, handler, command) - def test_context_multiple_regex(self, cdp, command): + def test_context_multiple_regex(self, dp, command): """Test CHs with context-based callbacks and filters combined""" handler = self.make_default_handler( - self.callback_context_regex2, filters=Filters.regex('one') & Filters.regex('two') + self.callback_context_regex2, filters=Filters.regex("one") & Filters.regex("two") ) - self._test_context_args_or_regex(cdp, handler, command) + self._test_context_args_or_regex(dp, handler, command) # ----------------------------- PrefixHandler ----------------------------- @@ -301,44 +255,41 @@ def combinations(prefixes, commands): class TestPrefixHandler(BaseTest): # Prefixes and commands with which to test PrefixHandler: - PREFIXES = ['!', '#', 'mytrig-'] - COMMANDS = ['help', 'test'] + PREFIXES = ["!", "#", "mytrig-"] + COMMANDS = ["help", "test"] COMBINATIONS = list(combinations(PREFIXES, COMMANDS)) - def test_slot_behaviour(self, mro_slots, recwarn): + def test_slot_behaviour(self, mro_slots): handler = self.make_default_handler() for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.command = 'should give warning', self.COMMANDS - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - @pytest.fixture(scope='class', params=PREFIXES) + @pytest.fixture(scope="class", params=PREFIXES) def prefix(self, request): return request.param - @pytest.fixture(scope='class', params=[1, 2], ids=['single prefix', 'multiple prefixes']) + @pytest.fixture(scope="class", params=[1, 2], ids=["single prefix", "multiple prefixes"]) def prefixes(self, request): return TestPrefixHandler.PREFIXES[: request.param] - @pytest.fixture(scope='class', params=COMMANDS) + @pytest.fixture(scope="class", params=COMMANDS) def command(self, request): return request.param - @pytest.fixture(scope='class', params=[1, 2], ids=['single command', 'multiple commands']) + @pytest.fixture(scope="class", params=[1, 2], ids=["single command", "multiple commands"]) def commands(self, request): return TestPrefixHandler.COMMANDS[: request.param] - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def prefix_message_text(self, prefix, command): return prefix + command - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def prefix_message(self, prefix_message_text): return make_message(prefix_message_text) - @pytest.fixture(scope='class') + @pytest.fixture(scope="class") def prefix_message_update(self, prefix_message): return make_message_update(prefix_message) @@ -346,12 +297,6 @@ def make_default_handler(self, callback=None, **kwargs): callback = callback or self.callback_basic return PrefixHandler(self.PREFIXES, self.COMMANDS, callback, **kwargs) - def ch_callback_args(self, bot, update, args): - if update.message.text in TestPrefixHandler.COMBINATIONS: - self.test_flag = len(args) == 0 - else: - self.test_flag = args == ['one', 'two'] - def test_basic(self, dp, prefix, command): """Test the basic expected response from a prefix handler""" handler = self.make_default_handler() @@ -360,8 +305,8 @@ def test_basic(self, dp, prefix, command): assert self.response(dp, make_message_update(text)) assert not is_match(handler, make_message_update(command)) - assert not is_match(handler, make_message_update(prefix + 'notacommand')) - assert not is_match(handler, make_command_update(f'not {text} at start')) + assert not is_match(handler, make_message_update(prefix + "notacommand")) + assert not is_match(handler, make_command_update(f"not {text} at start")) def test_single_multi_prefixes_commands(self, prefixes, commands, prefix_message_update): """Test various combinations of prefixes and commands""" @@ -376,30 +321,11 @@ def test_edited(self, prefix_message): self._test_edited(prefix_message, handler_edited, handler_no_edited) def test_with_filter(self, prefix_message_text): - handler = self.make_default_handler(filters=Filters.group) + handler = self.make_default_handler(filters=Filters.chat_type.group) text = prefix_message_text assert is_match(handler, make_message_update(text, chat=Chat(-23, Chat.GROUP))) assert not is_match(handler, make_message_update(text, chat=Chat(23, Chat.PRIVATE))) - def test_pass_args(self, dp, prefix_message): - handler = self.make_default_handler(self.ch_callback_args, pass_args=True) - dp.add_handler(handler) - assert self.response(dp, make_message_update(prefix_message)) - - update_with_args = make_message_update(prefix_message.text + ' one two') - assert self.response(dp, update_with_args) - - @pytest.mark.parametrize('pass_keyword', BaseTest.PASS_KEYWORDS) - def test_pass_data(self, dp, pass_combination, prefix_message_update, pass_keyword): - """Assert that callbacks receive data iff its corresponding ``pass_*`` kwarg is enabled""" - handler = self.make_default_handler( - self.make_callback_for(pass_keyword), **pass_combination - ) - dp.add_handler(handler) - assert self.response(dp, prefix_message_update) == pass_combination.get( - pass_keyword, False - ) - def test_other_update_types(self, false_update): handler = self.make_default_handler() assert not is_match(handler, false_update) @@ -407,20 +333,20 @@ def test_other_update_types(self, false_update): def test_filters_for_wrong_command(self, mock_filter): """Filters should not be executed if the command does not match the handler""" handler = self.make_default_handler(filters=mock_filter) - assert not is_match(handler, make_message_update('/test')) + assert not is_match(handler, make_message_update("/test")) assert not mock_filter.tested def test_edit_prefix(self): handler = self.make_default_handler() - handler.prefix = ['?', 'Β§'] - assert handler._commands == list(combinations(['?', 'Β§'], self.COMMANDS)) - handler.prefix = '+' - assert handler._commands == list(combinations(['+'], self.COMMANDS)) + handler.prefix = ["?", "Β§"] + assert handler._commands == list(combinations(["?", "Β§"], self.COMMANDS)) + handler.prefix = "+" + assert handler._commands == list(combinations(["+"], self.COMMANDS)) def test_edit_command(self): handler = self.make_default_handler() - handler.command = 'foo' - assert handler._commands == list(combinations(self.PREFIXES, ['foo'])) + handler.command = "foo" + assert handler._commands == list(combinations(self.PREFIXES, ["foo"])) def test_basic_after_editing(self, dp, prefix, command): """Test the basic expected response from a prefix handler""" @@ -429,27 +355,27 @@ def test_basic_after_editing(self, dp, prefix, command): text = prefix + command assert self.response(dp, make_message_update(text)) - handler.command = 'foo' - text = prefix + 'foo' + handler.command = "foo" + text = prefix + "foo" assert self.response(dp, make_message_update(text)) - def test_context(self, cdp, prefix_message_update): + def test_context(self, dp, prefix_message_update): handler = self.make_default_handler(self.callback_context) - cdp.add_handler(handler) - assert self.response(cdp, prefix_message_update) + dp.add_handler(handler) + assert self.response(dp, prefix_message_update) - def test_context_args(self, cdp, prefix_message_text): + def test_context_args(self, dp, prefix_message_text): handler = self.make_default_handler(self.callback_context_args) - self._test_context_args_or_regex(cdp, handler, prefix_message_text) + self._test_context_args_or_regex(dp, handler, prefix_message_text) - def test_context_regex(self, cdp, prefix_message_text): + def test_context_regex(self, dp, prefix_message_text): handler = self.make_default_handler( - self.callback_context_regex1, filters=Filters.regex('one two') + self.callback_context_regex1, filters=Filters.regex("one two") ) - self._test_context_args_or_regex(cdp, handler, prefix_message_text) + self._test_context_args_or_regex(dp, handler, prefix_message_text) - def test_context_multiple_regex(self, cdp, prefix_message_text): + def test_context_multiple_regex(self, dp, prefix_message_text): handler = self.make_default_handler( - self.callback_context_regex2, filters=Filters.regex('one') & Filters.regex('two') + self.callback_context_regex2, filters=Filters.regex("one") & Filters.regex("two") ) - self._test_context_args_or_regex(cdp, handler, prefix_message_text) + self._test_context_args_or_regex(dp, handler, prefix_message_text) diff --git a/tests/test_constants.py b/tests/test_constants.py index 58d1cbc9732..0692a5a6505 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -26,24 +26,24 @@ class TestConstants: @flaky(3, 1) def test_max_message_length(self, bot, chat_id): - bot.send_message(chat_id=chat_id, text='a' * constants.MAX_MESSAGE_LENGTH) + bot.send_message(chat_id=chat_id, text="a" * constants.MAX_MESSAGE_LENGTH) with pytest.raises( BadRequest, - match='Message is too long', + match="Message is too long", ): - bot.send_message(chat_id=chat_id, text='a' * (constants.MAX_MESSAGE_LENGTH + 1)) + bot.send_message(chat_id=chat_id, text="a" * (constants.MAX_MESSAGE_LENGTH + 1)) @flaky(3, 1) def test_max_caption_length(self, bot, chat_id): - good_caption = 'a' * constants.MAX_CAPTION_LENGTH - with open('tests/data/telegram.png', 'rb') as f: + good_caption = "a" * constants.MAX_CAPTION_LENGTH + with open("tests/data/telegram.png", "rb") as f: good_msg = bot.send_photo(photo=f, caption=good_caption, chat_id=chat_id) assert good_msg.caption == good_caption - bad_caption = good_caption + 'Z' + bad_caption = good_caption + "Z" with pytest.raises( BadRequest, match="Media_caption_too_long", - ), open('tests/data/telegram.png', 'rb') as f: + ), 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_contact.py b/tests/test_contact.py index 4ad6b699a97..8d2e7253edc 100644 --- a/tests/test_contact.py +++ b/tests/test_contact.py @@ -24,7 +24,7 @@ from telegram.error import BadRequest -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def contact(): return Contact( TestContact.phone_number, @@ -35,21 +35,18 @@ def contact(): class TestContact: - phone_number = '+11234567890' - first_name = 'Leandro' - last_name = 'Toledo' + phone_number = "+11234567890" + first_name = "Leandro" + last_name = "Toledo" user_id = 23 - def test_slot_behaviour(self, contact, recwarn, mro_slots): + def test_slot_behaviour(self, contact, mro_slots): for attr in contact.__slots__: - assert getattr(contact, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not contact.__dict__, f"got missing slot(s): {contact.__dict__}" + assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(contact)) == len(set(mro_slots(contact))), "duplicate slot" - contact.custom, contact.first_name = 'should give warning', self.first_name - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json_required(self, bot): - json_dict = {'phone_number': self.phone_number, 'first_name': self.first_name} + json_dict = {"phone_number": self.phone_number, "first_name": self.first_name} contact = Contact.de_json(json_dict, bot) assert contact.phone_number == self.phone_number @@ -57,10 +54,10 @@ def test_de_json_required(self, bot): def test_de_json_all(self, bot): json_dict = { - 'phone_number': self.phone_number, - 'first_name': self.first_name, - 'last_name': self.last_name, - 'user_id': self.user_id, + "phone_number": self.phone_number, + "first_name": self.first_name, + "last_name": self.last_name, + "user_id": self.user_id, } contact = Contact.de_json(json_dict, bot) @@ -71,29 +68,29 @@ def test_de_json_all(self, bot): def test_send_with_contact(self, monkeypatch, bot, chat_id, contact): def test(url, data, **kwargs): - phone = data['phone_number'] == contact.phone_number - first = data['first_name'] == contact.first_name - last = data['last_name'] == contact.last_name + phone = data["phone_number"] == contact.phone_number + first = data["first_name"] == contact.first_name + last = data["last_name"] == contact.last_name return phone and first and last - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_contact(contact=contact, chat_id=chat_id) assert message @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_contact_default_allow_sending_without_reply( self, default_bot, chat_id, contact, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_contact( @@ -109,30 +106,30 @@ def test_send_contact_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_contact( chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id ) def test_send_contact_without_required(self, bot, chat_id): - with pytest.raises(ValueError, match='Either contact or phone_number and first_name'): + with pytest.raises(ValueError, match="Either contact or phone_number and first_name"): bot.send_contact(chat_id=chat_id) def test_to_dict(self, contact): contact_dict = contact.to_dict() assert isinstance(contact_dict, dict) - assert contact_dict['phone_number'] == contact.phone_number - assert contact_dict['first_name'] == contact.first_name - assert contact_dict['last_name'] == contact.last_name - assert contact_dict['user_id'] == contact.user_id + assert contact_dict["phone_number"] == contact.phone_number + assert contact_dict["first_name"] == contact.first_name + assert contact_dict["last_name"] == contact.last_name + assert contact_dict["user_id"] == contact.user_id def test_equality(self): a = Contact(self.phone_number, self.first_name) b = Contact(self.phone_number, self.first_name) - c = Contact(self.phone_number, '') - d = Contact('', self.first_name) - e = Voice('', 'unique_id', 0) + c = Contact(self.phone_number, "") + d = Contact("", self.first_name) + e = Voice("", "unique_id", 0) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_contexttypes.py b/tests/test_contexttypes.py index 20dd405f9fe..ebe1c4a427c 100644 --- a/tests/test_contexttypes.py +++ b/tests/test_contexttypes.py @@ -29,10 +29,8 @@ class TestContextTypes: def test_slot_behaviour(self, mro_slots): instance = ContextTypes() for attr in instance.__slots__: - assert getattr(instance, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert getattr(instance, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(instance)) == len(set(mro_slots(instance))), "duplicate slot" - with pytest.raises(AttributeError): - instance.custom def test_data_init(self): ct = ContextTypes(SubClass, int, float, bool) @@ -41,7 +39,7 @@ def test_data_init(self): assert ct.chat_data is float assert ct.user_data is bool - with pytest.raises(ValueError, match='subclass of CallbackContext'): + with pytest.raises(ValueError, match="subclass of CallbackContext"): ContextTypes(context=bool) def test_data_assignment(self): diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index eaee2afa31d..7f2d7f0c71e 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -48,14 +48,14 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def user1(): - return User(first_name='Misses Test', id=123, is_bot=False) + return User(first_name="Misses Test", id=123, is_bot=False) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def user2(): - return User(first_name='Mister Test', id=124, is_bot=False) + return User(first_name="Mister Test", id=124, is_bot=False) @pytest.fixture(autouse=True) @@ -85,7 +85,7 @@ class TestConversationHandler: # Drinking state definitions (nested) # At first we're holding the cup. Then we sip coffee, and last we swallow it - HOLDING, SIPPING, SWALLOWING, REPLENISHING, STOPPING = map(chr, range(ord('a'), ord('f'))) + HOLDING, SIPPING, SWALLOWING, REPLENISHING, STOPPING = map(chr, range(ord("a"), ord("f"))) current_state, entry_points, states, fallbacks = None, None, None, None group = Chat(0, Chat.GROUP) @@ -94,16 +94,11 @@ class TestConversationHandler: raise_dp_handler_stop = False test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): handler = ConversationHandler(self.entry_points, self.states, self.fallbacks) for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler._persistence = 'should give warning', handler._persistence - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), [ - w.message for w in recwarn.list - ] # Test related @pytest.fixture(autouse=True) @@ -111,46 +106,46 @@ def reset(self): self.raise_dp_handler_stop = False self.test_flag = False self.current_state = {} - self.entry_points = [CommandHandler('start', self.start)] + self.entry_points = [CommandHandler("start", self.start)] self.states = { - self.THIRSTY: [CommandHandler('brew', self.brew), CommandHandler('wait', self.start)], - self.BREWING: [CommandHandler('pourCoffee', self.drink)], + self.THIRSTY: [CommandHandler("brew", self.brew), CommandHandler("wait", self.start)], + self.BREWING: [CommandHandler("pourCoffee", self.drink)], self.DRINKING: [ - CommandHandler('startCoding', self.code), - CommandHandler('drinkMore', self.drink), - CommandHandler('end', self.end), + CommandHandler("startCoding", self.code), + CommandHandler("drinkMore", self.drink), + CommandHandler("end", self.end), ], self.CODING: [ - CommandHandler('keepCoding', self.code), - CommandHandler('gettingThirsty', self.start), - CommandHandler('drinkMore', self.drink), + CommandHandler("keepCoding", self.code), + CommandHandler("gettingThirsty", self.start), + CommandHandler("drinkMore", self.drink), ], } - self.fallbacks = [CommandHandler('eat', self.start)] + self.fallbacks = [CommandHandler("eat", self.start)] self.is_timeout = False # for nesting tests self.nested_states = { - self.THIRSTY: [CommandHandler('brew', self.brew), CommandHandler('wait', self.start)], - self.BREWING: [CommandHandler('pourCoffee', self.drink)], + self.THIRSTY: [CommandHandler("brew", self.brew), CommandHandler("wait", self.start)], + self.BREWING: [CommandHandler("pourCoffee", self.drink)], self.CODING: [ - CommandHandler('keepCoding', self.code), - CommandHandler('gettingThirsty', self.start), - CommandHandler('drinkMore', self.drink), + CommandHandler("keepCoding", self.code), + CommandHandler("gettingThirsty", self.start), + CommandHandler("drinkMore", self.drink), ], } - self.drinking_entry_points = [CommandHandler('hold', self.hold)] + self.drinking_entry_points = [CommandHandler("hold", self.hold)] self.drinking_states = { - self.HOLDING: [CommandHandler('sip', self.sip)], - self.SIPPING: [CommandHandler('swallow', self.swallow)], - self.SWALLOWING: [CommandHandler('hold', self.hold)], + self.HOLDING: [CommandHandler("sip", self.sip)], + self.SIPPING: [CommandHandler("swallow", self.swallow)], + self.SWALLOWING: [CommandHandler("hold", self.hold)], } self.drinking_fallbacks = [ - CommandHandler('replenish', self.replenish), - CommandHandler('stop', self.stop), - CommandHandler('end', self.end), - CommandHandler('startCoding', self.code), - CommandHandler('drinkMore', self.drink), + CommandHandler("replenish", self.replenish), + CommandHandler("stop", self.stop), + CommandHandler("end", self.end), + CommandHandler("startCoding", self.code), + CommandHandler("drinkMore", self.drink), ] self.drinking_entry_points.extend(self.drinking_fallbacks) @@ -175,51 +170,51 @@ def _set_state(self, update, state): # Actions @raise_dphs - def start(self, bot, update): + def start(self, update, context): if isinstance(update, Update): return self._set_state(update, self.THIRSTY) - return self._set_state(bot, self.THIRSTY) + return self._set_state(context.bot, self.THIRSTY) @raise_dphs - def end(self, bot, update): + def end(self, update, context): return self._set_state(update, self.END) @raise_dphs - def start_end(self, bot, update): + def start_end(self, update, context): return self._set_state(update, self.END) @raise_dphs - def start_none(self, bot, update): + def start_none(self, update, context): return self._set_state(update, None) @raise_dphs - def brew(self, bot, update): + def brew(self, update, context): if isinstance(update, Update): return self._set_state(update, self.BREWING) - return self._set_state(bot, self.BREWING) + return self._set_state(context.bot, self.BREWING) @raise_dphs - def drink(self, bot, update): + def drink(self, update, context): return self._set_state(update, self.DRINKING) @raise_dphs - def code(self, bot, update): + def code(self, update, context): return self._set_state(update, self.CODING) @raise_dphs - def passout(self, bot, update): - assert update.message.text == '/brew' + def passout(self, update, context): + assert update.message.text == "/brew" assert isinstance(update, Update) self.is_timeout = True @raise_dphs - def passout2(self, bot, update): + def passout2(self, update, context): assert isinstance(update, Update) self.is_timeout = True @raise_dphs def passout_context(self, update, context): - assert update.message.text == '/brew' + assert update.message.text == "/brew" assert isinstance(context, CallbackContext) self.is_timeout = True @@ -231,53 +226,53 @@ def passout2_context(self, update, context): # Drinking actions (nested) @raise_dphs - def hold(self, bot, update): + def hold(self, update, context): return self._set_state(update, self.HOLDING) @raise_dphs - def sip(self, bot, update): + def sip(self, update, context): return self._set_state(update, self.SIPPING) @raise_dphs - def swallow(self, bot, update): + def swallow(self, update, context): return self._set_state(update, self.SWALLOWING) @raise_dphs - def replenish(self, bot, update): + def replenish(self, update, context): return self._set_state(update, self.REPLENISHING) @raise_dphs - def stop(self, bot, update): + def stop(self, update, context): return self._set_state(update, self.STOPPING) # Tests @pytest.mark.parametrize( - 'attr', + "attr", [ - 'entry_points', - 'states', - 'fallbacks', - 'per_chat', - 'name', - 'per_user', - 'allow_reentry', - 'conversation_timeout', - 'map_to_parent', + "entry_points", + "states", + "fallbacks", + "per_chat", + "name", + "per_user", + "allow_reentry", + "conversation_timeout", + "map_to_parent", ], indirect=False, ) def test_immutable(self, attr): ch = ConversationHandler( - 'entry_points', - {'states': ['states']}, - 'fallbacks', - per_chat='per_chat', - per_user='per_user', + "entry_points", + {"states": ["states"]}, + "fallbacks", + per_chat="per_chat", + per_user="per_user", per_message=False, - allow_reentry='allow_reentry', - conversation_timeout='conversation_timeout', - name='name', - map_to_parent='map_to_parent', + allow_reentry="allow_reentry", + conversation_timeout="conversation_timeout", + name="name", + map_to_parent="map_to_parent", ) value = getattr(ch, attr) @@ -287,24 +282,24 @@ def test_immutable(self, attr): assert list(value.keys())[0] == attr else: assert getattr(ch, attr) == attr - with pytest.raises(ValueError, match=f'You can not assign a new value to {attr}'): + with pytest.raises(ValueError, match=f"You can not assign a new value to {attr}"): setattr(ch, attr, True) def test_immutable_per_message(self): ch = ConversationHandler( - 'entry_points', - {'states': ['states']}, - 'fallbacks', - per_chat='per_chat', - per_user='per_user', + "entry_points", + {"states": ["states"]}, + "fallbacks", + per_chat="per_chat", + per_user="per_user", per_message=False, - allow_reentry='allow_reentry', - conversation_timeout='conversation_timeout', - name='name', - map_to_parent='map_to_parent', + allow_reentry="allow_reentry", + conversation_timeout="conversation_timeout", + name="name", + map_to_parent="map_to_parent", ) assert ch.per_message is False - with pytest.raises(ValueError, match='You can not assign a new value to per_message'): + with pytest.raises(ValueError, match="You can not assign a new value to per_message"): ch.per_message = True def test_per_all_false(self): @@ -336,9 +331,9 @@ def test_conversation_handler(self, dp, bot, user1, user2): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -346,20 +341,20 @@ def test_conversation_handler(self, dp, bot, user1, user2): assert self.current_state[user1.id] == self.THIRSTY # The user is thirsty and wants to brew coffee. - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.BREWING # Lets see if an invalid command makes sure, no state is changed. - message.text = '/nothing' - message.entities[0].length = len('/nothing') + message.text = "/nothing" + message.entities[0].length = len("/nothing") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.BREWING # Lets see if the state machine still works by pouring coffee. - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING @@ -381,21 +376,21 @@ def test_conversation_handler_end(self, caplog, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) - message.text = '/end' - message.entities[0].length = len('/end') + message.text = "/end" + message.entities[0].length = len("/end") caplog.clear() with caplog.at_level(logging.ERROR): dp.process_update(Update(update_id=0, message=message)) @@ -416,8 +411,8 @@ def test_conversation_handler_fallback(self, dp, bot, user1, user2): None, self.group, from_user=user1, - text='/eat', - entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/eat'))], + text="/eat", + entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/eat"))], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) @@ -425,20 +420,20 @@ def test_conversation_handler_fallback(self, dp, bot, user1, user2): self.current_state[user1.id] # User starts the state machine. - message.text = '/start' - message.entities[0].length = len('/start') + message.text = "/start" + message.entities[0].length = len("/start") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY # The user is thirsty and wants to brew coffee. - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.BREWING # Now a fallback command is issued - message.text = '/eat' - message.entities[0].length = len('/eat') + message.text = "/eat" + message.entities[0].length = len("/eat") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY @@ -458,9 +453,9 @@ def test_unknown_state_warning(self, dp, bot, user1, recwarn): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -488,24 +483,24 @@ def test_conversation_handler_per_chat(self, dp, bot, user1, user2): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) # The user is thirsty and wants to brew coffee. - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) # Let's now verify that for another user, who did not start yet, # the state will be changed because they are in the same group. message.from_user = user2 - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert handler.conversations[(self.group.id,)] == self.DRINKING @@ -525,36 +520,36 @@ def test_conversation_handler_per_user(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) # The user is thirsty and wants to brew coffee. - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) # Let's now verify that for the same user in a different group, the state will still be # updated message.chat = self.second_group - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert handler.conversations[(user1.id,)] == self.DRINKING def test_conversation_handler_per_message(self, dp, bot, user1, user2): - def entry(bot, update): + def entry(update, context): return 1 - def one(bot, update): + def one(update, context): return 2 - def two(bot, update): + def two(update, context): return ConversationHandler.END handler = ConversationHandler( @@ -567,10 +562,10 @@ def two(bot, update): # User one, starts the state machine. message = Message( - 0, None, self.group, from_user=user1, text='msg w/ inlinekeyboard', bot=bot + 0, None, self.group, from_user=user1, text="msg w/ inlinekeyboard", bot=bot ) - cbq = CallbackQuery(0, user1, None, message=message, data='data', bot=bot) + cbq = CallbackQuery(0, user1, None, message=message, data="data", bot=bot) dp.process_update(Update(update_id=0, callback_query=cbq)) assert handler.conversations[(self.group.id, user1.id, message.message_id)] == 1 @@ -588,7 +583,7 @@ def two(bot, update): def test_end_on_first_message(self, dp, bot, user1): handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[] + entry_points=[CommandHandler("start", self.start_end)], states={}, fallbacks=[] ) dp.add_handler(handler) @@ -598,9 +593,9 @@ def test_end_on_first_message(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -611,7 +606,7 @@ def test_end_on_first_message_async(self, dp, bot, user1): handler = ConversationHandler( entry_points=[ CommandHandler( - 'start', lambda bot, update: dp.run_async(self.start_end, bot, update) + "start", lambda update, context: dp.run_async(self.start_end, update, context) ) ], states={}, @@ -626,9 +621,9 @@ def test_end_on_first_message_async(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -637,8 +632,8 @@ def test_end_on_first_message_async(self, dp, bot, user1): # Assert that the Promise has been accepted as the new state assert len(handler.conversations) == 1 - message.text = 'resolve promise pls' - message.entities[0].length = len('resolve promise pls') + message.text = "resolve promise pls" + message.entities[0].length = len("resolve promise pls") dp.update_queue.put(Update(update_id=0, message=message)) sleep(0.1) # Assert that the Promise has been resolved and the conversation ended. @@ -646,7 +641,7 @@ def test_end_on_first_message_async(self, dp, bot, user1): def test_end_on_first_message_async_handler(self, dp, bot, user1): handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_end, run_async=True)], + entry_points=[CommandHandler("start", self.start_end, run_async=True)], states={}, fallbacks=[], ) @@ -658,10 +653,10 @@ def test_end_on_first_message_async_handler(self, dp, bot, user1): 0, None, self.group, - text='/start', + text="/start", from_user=user1, entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -670,8 +665,8 @@ def test_end_on_first_message_async_handler(self, dp, bot, user1): # Assert that the Promise has been accepted as the new state assert len(handler.conversations) == 1 - message.text = 'resolve promise pls' - message.entities[0].length = len('resolve promise pls') + message.text = "resolve promise pls" + message.entities[0].length = len("resolve promise pls") dp.update_queue.put(Update(update_id=0, message=message)) sleep(0.1) # Assert that the Promise has been resolved and the conversation ended. @@ -679,12 +674,12 @@ def test_end_on_first_message_async_handler(self, dp, bot, user1): def test_none_on_first_message(self, dp, bot, user1): handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_none)], states={}, fallbacks=[] + entry_points=[CommandHandler("start", self.start_none)], states={}, fallbacks=[] ) dp.add_handler(handler) # User starts the state machine and a callback function returns None - message = Message(0, None, self.group, from_user=user1, text='/start', bot=bot) + message = Message(0, None, self.group, from_user=user1, text="/start", bot=bot) dp.process_update(Update(update_id=0, message=message)) assert len(handler.conversations) == 0 @@ -692,7 +687,7 @@ def test_none_on_first_message_async(self, dp, bot, user1): handler = ConversationHandler( entry_points=[ CommandHandler( - 'start', lambda bot, update: dp.run_async(self.start_none, bot, update) + "start", lambda update, context: dp.run_async(self.start_none, update, context) ) ], states={}, @@ -707,9 +702,9 @@ def test_none_on_first_message_async(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -718,7 +713,7 @@ def test_none_on_first_message_async(self, dp, bot, user1): # Assert that the Promise has been accepted as the new state assert len(handler.conversations) == 1 - message.text = 'resolve promise pls' + message.text = "resolve promise pls" dp.update_queue.put(Update(update_id=0, message=message)) sleep(0.1) # Assert that the Promise has been resolved and the conversation ended. @@ -726,7 +721,7 @@ def test_none_on_first_message_async(self, dp, bot, user1): def test_none_on_first_message_async_handler(self, dp, bot, user1): handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_none, run_async=True)], + entry_points=[CommandHandler("start", self.start_none, run_async=True)], states={}, fallbacks=[], ) @@ -738,10 +733,10 @@ def test_none_on_first_message_async_handler(self, dp, bot, user1): 0, None, self.group, - text='/start', + text="/start", from_user=user1, entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -750,7 +745,7 @@ def test_none_on_first_message_async_handler(self, dp, bot, user1): # Assert that the Promise has been accepted as the new state assert len(handler.conversations) == 1 - message.text = 'resolve promise pls' + message.text = "resolve promise pls" dp.update_queue.put(Update(update_id=0, message=message)) sleep(0.1) # Assert that the Promise has been resolved and the conversation ended. @@ -758,7 +753,7 @@ def test_none_on_first_message_async_handler(self, dp, bot, user1): def test_per_chat_message_without_chat(self, bot, user1): handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[] + entry_points=[CommandHandler("start", self.start_end)], states={}, fallbacks=[] ) cbq = CallbackQuery(0, user1, None, None, bot=bot) update = Update(0, callback_query=cbq) @@ -768,7 +763,7 @@ def test_channel_message_without_chat(self, bot): handler = ConversationHandler( entry_points=[MessageHandler(Filters.all, self.start_end)], states={}, fallbacks=[] ) - message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, 'Misses Test'), bot=bot) + message = Message(0, date=None, chat=Chat(0, Chat.CHANNEL, "Misses Test"), bot=bot) update = Update(0, channel_post=message) assert not handler.check_update(update) @@ -778,13 +773,13 @@ def test_channel_message_without_chat(self, bot): def test_all_update_types(self, dp, bot, user1): handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_end)], states={}, fallbacks=[] + entry_points=[CommandHandler("start", self.start_end)], states={}, fallbacks=[] ) - message = Message(0, None, self.group, from_user=user1, text='ignore', bot=bot) - callback_query = CallbackQuery(0, user1, None, message=message, data='data', bot=bot) - chosen_inline_result = ChosenInlineResult(0, user1, 'query', bot=bot) - inline_query = InlineQuery(0, user1, 'query', 0, bot=bot) - pre_checkout_query = PreCheckoutQuery(0, user1, 'USD', 100, [], bot=bot) + message = Message(0, None, self.group, from_user=user1, text="ignore", bot=bot) + callback_query = CallbackQuery(0, user1, None, message=message, data="data", bot=bot) + chosen_inline_result = ChosenInlineResult(0, user1, "query", bot=bot) + inline_query = InlineQuery(0, user1, "query", 0, bot=bot) + pre_checkout_query = PreCheckoutQuery(0, user1, "USD", 100, [], bot=bot) shipping_query = ShippingQuery(0, user1, [], None, bot=bot) assert not handler.check_update(Update(0, callback_query=callback_query)) assert not handler.check_update(Update(0, chosen_inline_result=chosen_inline_result)) @@ -811,9 +806,9 @@ def test_no_jobqueue_warning(self, dp, bot, user1, caplog): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -833,6 +828,10 @@ def test_schedule_job_exception(self, dp, bot, user1, monkeypatch, caplog): def mocked_run_once(*a, **kw): raise Exception("job error") + class DictJB(JobQueue): + pass + + dp.job_queue = DictJB() monkeypatch.setattr(dp.job_queue, "run_once", mocked_run_once) handler = ConversationHandler( entry_points=self.entry_points, @@ -847,9 +846,9 @@ def mocked_run_once(*a, **kw): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -889,9 +888,9 @@ def raise_error(*a, **kw): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -927,9 +926,9 @@ def test_conversation_timeout(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -941,8 +940,8 @@ def test_conversation_timeout(self, dp, bot, user1): # Start state machine, do something, then reach timeout dp.process_update(Update(update_id=1, message=message)) assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=2, message=message)) assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING sleep(0.7) @@ -967,25 +966,25 @@ def timeout(*a, **kw): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) # start the conversation dp.process_update(Update(update_id=0, message=message)) sleep(0.1) - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=1, message=message)) sleep(0.1) - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=2, message=message)) sleep(0.1) - message.text = '/end' - message.entities[0].length = len('/end') + message.text = "/end" + message.entities[0].length = len("/end") dp.process_update(Update(update_id=3, message=message)) sleep(1) # assert timeout handler didn't got called @@ -1010,10 +1009,10 @@ def timeout(*args, **kwargs): 0, None, self.group, - text='/start', + text="/start", from_user=user1, entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -1025,9 +1024,9 @@ def timeout(*args, **kwargs): assert handler.conversations.get((self.group.id, user1.id)) is None assert len(caplog.records) == 1 rec = caplog.records[-1] - assert rec.getMessage().startswith('DispatcherHandlerStop in TIMEOUT') + assert rec.getMessage().startswith("DispatcherHandlerStop in TIMEOUT") - def test_conversation_handler_timeout_update_and_context(self, cdp, bot, user1): + def test_conversation_handler_timeout_update_and_context(self, dp, bot, user1): context = None def start_callback(u, c): @@ -1036,15 +1035,15 @@ def start_callback(u, c): return self.start(u, c) states = self.states - timeout_handler = CommandHandler('start', None) + timeout_handler = CommandHandler("start", None) states.update({ConversationHandler.TIMEOUT: [timeout_handler]}) handler = ConversationHandler( - entry_points=[CommandHandler('start', start_callback)], + entry_points=[CommandHandler("start", start_callback)], states=states, fallbacks=self.fallbacks, conversation_timeout=0.5, ) - cdp.add_handler(handler) + dp.add_handler(handler) # Start state machine, then reach timeout message = Message( @@ -1052,9 +1051,9 @@ def start_callback(u, c): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -1068,7 +1067,7 @@ def timeout_callback(u, c): timeout_handler.callback = timeout_callback - cdp.process_update(update) + dp.process_update(update) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None assert self.is_timeout @@ -1095,9 +1094,9 @@ def test_conversation_timeout_keeps_extending(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) @@ -1105,14 +1104,14 @@ def test_conversation_timeout_keeps_extending(self, dp, bot, user1): assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY sleep(0.35) # t=.35 assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING sleep(0.25) # t=.6 assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert handler.conversations.get((self.group.id, user1.id)) == self.DRINKING sleep(0.4) # t=1.0 @@ -1135,22 +1134,22 @@ def test_conversation_timeout_two_users(self, dp, bot, user1, user2): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) assert handler.conversations.get((self.group.id, user1.id)) == self.THIRSTY - message.text = '/brew' - message.entities[0].length = len('/brew') - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") + message.entities[0].length = len("/brew") message.from_user = user2 dp.process_update(Update(update_id=0, message=message)) assert handler.conversations.get((self.group.id, user2.id)) is None - message.text = '/start' - message.entities[0].length = len('/start') + message.text = "/start" + message.entities[0].length = len("/start") dp.process_update(Update(update_id=0, message=message)) assert handler.conversations.get((self.group.id, user2.id)) == self.THIRSTY sleep(0.7) @@ -1162,8 +1161,8 @@ def test_conversation_handler_timeout_state(self, dp, bot, user1): states.update( { ConversationHandler.TIMEOUT: [ - CommandHandler('brew', self.passout), - MessageHandler(~Filters.regex('oding'), self.passout2), + CommandHandler("brew", self.passout), + MessageHandler(~Filters.regex("oding"), self.passout2), ] } ) @@ -1181,15 +1180,15 @@ def test_conversation_handler_timeout_state(self, dp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None @@ -1197,8 +1196,8 @@ def test_conversation_handler_timeout_state(self, dp, bot, user1): # MessageHandler timeout self.is_timeout = False - message.text = '/start' - message.entities[0].length = len('/start') + message.text = "/start" + message.entities[0].length = len("/start") dp.process_update(Update(update_id=1, message=message)) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None @@ -1207,23 +1206,23 @@ def test_conversation_handler_timeout_state(self, dp, bot, user1): # Timeout but no valid handler self.is_timeout = False dp.process_update(Update(update_id=0, message=message)) - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) - message.text = '/startCoding' - message.entities[0].length = len('/startCoding') + message.text = "/startCoding" + message.entities[0].length = len("/startCoding") dp.process_update(Update(update_id=0, message=message)) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None assert not self.is_timeout - def test_conversation_handler_timeout_state_context(self, cdp, bot, user1): + def test_conversation_handler_timeout_state_context(self, dp, bot, user1): states = self.states states.update( { ConversationHandler.TIMEOUT: [ - CommandHandler('brew', self.passout_context), - MessageHandler(~Filters.regex('oding'), self.passout2_context), + CommandHandler("brew", self.passout_context), + MessageHandler(~Filters.regex("oding"), self.passout2_context), ] } ) @@ -1233,7 +1232,7 @@ def test_conversation_handler_timeout_state_context(self, cdp, bot, user1): fallbacks=self.fallbacks, conversation_timeout=0.5, ) - cdp.add_handler(handler) + dp.add_handler(handler) # CommandHandler timeout message = Message( @@ -1241,38 +1240,38 @@ def test_conversation_handler_timeout_state_context(self, cdp, bot, user1): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) - cdp.process_update(Update(update_id=0, message=message)) - message.text = '/brew' - message.entities[0].length = len('/brew') - cdp.process_update(Update(update_id=0, message=message)) + dp.process_update(Update(update_id=0, message=message)) + message.text = "/brew" + message.entities[0].length = len("/brew") + dp.process_update(Update(update_id=0, message=message)) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None assert self.is_timeout # MessageHandler timeout self.is_timeout = False - message.text = '/start' - message.entities[0].length = len('/start') - cdp.process_update(Update(update_id=1, message=message)) + message.text = "/start" + message.entities[0].length = len("/start") + dp.process_update(Update(update_id=1, message=message)) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None assert self.is_timeout # Timeout but no valid handler self.is_timeout = False - cdp.process_update(Update(update_id=0, message=message)) - message.text = '/brew' - message.entities[0].length = len('/brew') - cdp.process_update(Update(update_id=0, message=message)) - message.text = '/startCoding' - message.entities[0].length = len('/startCoding') - cdp.process_update(Update(update_id=0, message=message)) + dp.process_update(Update(update_id=0, message=message)) + message.text = "/brew" + message.entities[0].length = len("/brew") + dp.process_update(Update(update_id=0, message=message)) + message.text = "/startCoding" + message.entities[0].length = len("/startCoding") + dp.process_update(Update(update_id=0, message=message)) sleep(0.7) assert handler.conversations.get((self.group.id, user1.id)) is None assert not self.is_timeout @@ -1286,7 +1285,7 @@ def test_conversation_timeout_cancel_conflict(self, dp, bot, user1): # | t=.75 /slowbrew returns (timeout=1.25) # t=1.25 timeout - def slowbrew(_bot, update): + def slowbrew(_update, context): sleep(0.25) # Let's give to the original timeout a chance to execute sleep(0.25) @@ -1294,7 +1293,7 @@ def slowbrew(_bot, update): # we can see if the timeout has been executed states = self.states - states[self.THIRSTY].append(CommandHandler('slowbrew', slowbrew)) + states[self.THIRSTY].append(CommandHandler("slowbrew", slowbrew)) states.update({ConversationHandler.TIMEOUT: [MessageHandler(None, self.passout2)]}) handler = ConversationHandler( @@ -1311,16 +1310,16 @@ def slowbrew(_bot, update): None, self.group, from_user=user1, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ) dp.process_update(Update(update_id=0, message=message)) sleep(0.25) - message.text = '/slowbrew' - message.entities[0].length = len('/slowbrew') + message.text = "/slowbrew" + message.entities[0].length = len("/slowbrew") dp.process_update(Update(update_id=0, message=message)) assert handler.conversations.get((self.group.id, user1.id)) is not None assert not self.is_timeout @@ -1337,7 +1336,7 @@ def test_conversation_timeout_warning_only_shown_once(self, recwarn): ConversationHandler( entry_points=self.entry_points, states={ - self.BREWING: [CommandHandler('pourCoffee', self.drink)], + self.BREWING: [CommandHandler("pourCoffee", self.drink)], }, fallbacks=self.fallbacks, ) @@ -1346,7 +1345,7 @@ def test_conversation_timeout_warning_only_shown_once(self, recwarn): ConversationHandler( entry_points=self.entry_points, states={ - self.CODING: [CommandHandler('startCoding', self.code)], + self.CODING: [CommandHandler("startCoding", self.code)], }, fallbacks=self.fallbacks, ) @@ -1366,8 +1365,8 @@ def test_per_message_warning_is_only_shown_once(self, recwarn): ConversationHandler( entry_points=self.entry_points, states={ - self.THIRSTY: [CommandHandler('pourCoffee', self.drink)], - self.BREWING: [CommandHandler('startCoding', self.code)], + self.THIRSTY: [CommandHandler("pourCoffee", self.drink)], + self.BREWING: [CommandHandler("startCoding", self.code)], }, fallbacks=self.fallbacks, per_message=True, @@ -1396,10 +1395,10 @@ def test_per_message_false_warning_is_only_shown_once(self, recwarn): ) def test_warnings_per_chat_is_only_shown_once(self, recwarn): - def hello(bot, update): + def hello(update, context): return self.BREWING - def bye(bot, update): + def bye(update, context): return ConversationHandler.END ConversationHandler( @@ -1437,98 +1436,98 @@ def test_nested_conversation_handler(self, dp, bot, user1, user2): None, self.group, from_user=user1, - text='/start', + text="/start", bot=bot, entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], ) dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.THIRSTY # The user is thirsty and wants to brew coffee. - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.BREWING # Lets pour some coffee. - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING # The user is holding the cup - message.text = '/hold' - message.entities[0].length = len('/hold') + message.text = "/hold" + message.entities[0].length = len("/hold") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.HOLDING # The user is sipping coffee - message.text = '/sip' - message.entities[0].length = len('/sip') + message.text = "/sip" + message.entities[0].length = len("/sip") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.SIPPING # The user is swallowing - message.text = '/swallow' - message.entities[0].length = len('/swallow') + message.text = "/swallow" + message.entities[0].length = len("/swallow") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.SWALLOWING # The user is holding the cup again - message.text = '/hold' - message.entities[0].length = len('/hold') + message.text = "/hold" + message.entities[0].length = len("/hold") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.HOLDING # The user wants to replenish the coffee supply - message.text = '/replenish' - message.entities[0].length = len('/replenish') + message.text = "/replenish" + message.entities[0].length = len("/replenish") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.REPLENISHING assert handler.conversations[(0, user1.id)] == self.BREWING # The user wants to drink their coffee again - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING # The user is now ready to start coding - message.text = '/startCoding' - message.entities[0].length = len('/startCoding') + message.text = "/startCoding" + message.entities[0].length = len("/startCoding") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.CODING # The user decides it's time to drink again - message.text = '/drinkMore' - message.entities[0].length = len('/drinkMore') + message.text = "/drinkMore" + message.entities[0].length = len("/drinkMore") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING # The user is holding their cup - message.text = '/hold' - message.entities[0].length = len('/hold') + message.text = "/hold" + message.entities[0].length = len("/hold") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.HOLDING # The user wants to end with the drinking and go back to coding - message.text = '/end' - message.entities[0].length = len('/end') + message.text = "/end" + message.entities[0].length = len("/end") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.END assert handler.conversations[(0, user1.id)] == self.CODING # The user wants to drink once more - message.text = '/drinkMore' - message.entities[0].length = len('/drinkMore') + message.text = "/drinkMore" + message.entities[0].length = len("/drinkMore") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING # The user wants to stop altogether - message.text = '/stop' - message.entities[0].length = len('/stop') + message.text = "/stop" + message.entities[0].length = len("/stop") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.STOPPING assert handler.conversations.get((0, user1.id)) is None @@ -1558,11 +1557,11 @@ def test_callback(u, c): 0, None, self.group, - text='/start', + text="/start", bot=bot, from_user=user1, entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], ) dp.process_update(Update(update_id=0, message=message)) @@ -1570,101 +1569,101 @@ def test_callback(u, c): assert not self.test_flag # The user is thirsty and wants to brew coffee. - message.text = '/brew' - message.entities[0].length = len('/brew') + message.text = "/brew" + message.entities[0].length = len("/brew") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.BREWING assert not self.test_flag # Lets pour some coffee. - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING assert not self.test_flag # The user is holding the cup - message.text = '/hold' - message.entities[0].length = len('/hold') + message.text = "/hold" + message.entities[0].length = len("/hold") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.HOLDING assert not self.test_flag # The user is sipping coffee - message.text = '/sip' - message.entities[0].length = len('/sip') + message.text = "/sip" + message.entities[0].length = len("/sip") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.SIPPING assert not self.test_flag # The user is swallowing - message.text = '/swallow' - message.entities[0].length = len('/swallow') + message.text = "/swallow" + message.entities[0].length = len("/swallow") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.SWALLOWING assert not self.test_flag # The user is holding the cup again - message.text = '/hold' - message.entities[0].length = len('/hold') + message.text = "/hold" + message.entities[0].length = len("/hold") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.HOLDING assert not self.test_flag # The user wants to replenish the coffee supply - message.text = '/replenish' - message.entities[0].length = len('/replenish') + message.text = "/replenish" + message.entities[0].length = len("/replenish") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.REPLENISHING assert handler.conversations[(0, user1.id)] == self.BREWING assert not self.test_flag # The user wants to drink their coffee again - message.text = '/pourCoffee' - message.entities[0].length = len('/pourCoffee') + message.text = "/pourCoffee" + message.entities[0].length = len("/pourCoffee") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING assert not self.test_flag # The user is now ready to start coding - message.text = '/startCoding' - message.entities[0].length = len('/startCoding') + message.text = "/startCoding" + message.entities[0].length = len("/startCoding") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.CODING assert not self.test_flag # The user decides it's time to drink again - message.text = '/drinkMore' - message.entities[0].length = len('/drinkMore') + message.text = "/drinkMore" + message.entities[0].length = len("/drinkMore") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING assert not self.test_flag # The user is holding their cup - message.text = '/hold' - message.entities[0].length = len('/hold') + message.text = "/hold" + message.entities[0].length = len("/hold") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.HOLDING assert not self.test_flag # The user wants to end with the drinking and go back to coding - message.text = '/end' - message.entities[0].length = len('/end') + message.text = "/end" + message.entities[0].length = len("/end") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.END assert handler.conversations[(0, user1.id)] == self.CODING assert not self.test_flag # The user wants to drink once more - message.text = '/drinkMore' - message.entities[0].length = len('/drinkMore') + message.text = "/drinkMore" + message.entities[0].length = len("/drinkMore") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.DRINKING assert not self.test_flag # The user wants to stop altogether - message.text = '/stop' - message.entities[0].length = len('/stop') + message.text = "/stop" + message.entities[0].length = len("/stop") dp.process_update(Update(update_id=0, message=message)) assert self.current_state[user1.id] == self.STOPPING assert handler.conversations.get((0, user1.id)) is None @@ -1687,7 +1686,7 @@ def test_conversation_handler_run_async_true(self, dp): def test_conversation_handler_run_async_false(self, dp): conv_handler = ConversationHandler( - entry_points=[CommandHandler('start', self.start_end, run_async=True)], + entry_points=[CommandHandler("start", self.start_end, run_async=True)], states=self.states, fallbacks=self.fallbacks, run_async=False, diff --git a/tests/test_defaults.py b/tests/test_defaults.py index 99a85bae481..232bb98a5c1 100644 --- a/tests/test_defaults.py +++ b/tests/test_defaults.py @@ -24,16 +24,13 @@ class TestDefault: - def test_slot_behaviour(self, recwarn, mro_slots): - a = Defaults(parse_mode='HTML', quote=True) + def test_slot_behaviour(self, mro_slots): + a = Defaults(parse_mode="HTML", quote=True) for attr in a.__slots__: - assert getattr(a, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not a.__dict__, f"got missing slot(s): {a.__dict__}" + assert getattr(a, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(a)) == len(set(mro_slots(a))), "duplicate slot" - a.custom, a._parse_mode = 'should give warning', a._parse_mode - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - def test_data_assignment(self, cdp): + def test_data_assignment(self, dp): defaults = Defaults() with pytest.raises(AttributeError): @@ -56,11 +53,11 @@ def test_data_assignment(self, cdp): defaults.run_async = True def test_equality(self): - a = Defaults(parse_mode='HTML', quote=True) - b = Defaults(parse_mode='HTML', quote=True) - c = Defaults(parse_mode='HTML', quote=False) - d = Defaults(parse_mode='HTML', timeout=50) - e = User(123, 'test_user', False) + a = Defaults(parse_mode="HTML", quote=True) + b = Defaults(parse_mode="HTML", quote=True) + c = Defaults(parse_mode="HTML", quote=False) + d = Defaults(parse_mode="HTML", timeout=50) + e = User(123, "test_user", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_dice.py b/tests/test_dice.py index cced0400199..dfeecf8d6ce 100644 --- a/tests/test_dice.py +++ b/tests/test_dice.py @@ -30,17 +30,14 @@ def dice(request): class TestDice: value = 4 - def test_slot_behaviour(self, dice, recwarn, mro_slots): + def test_slot_behaviour(self, dice, mro_slots): for attr in dice.__slots__: - assert getattr(dice, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not dice.__dict__, f"got missing slot(s): {dice.__dict__}" + assert getattr(dice, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(dice)) == len(set(mro_slots(dice))), "duplicate slot" - dice.custom, dice.value = 'should give warning', self.value - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - @pytest.mark.parametrize('emoji', Dice.ALL_EMOJI) + @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI) def test_de_json(self, bot, emoji): - json_dict = {'value': self.value, 'emoji': emoji} + json_dict = {"value": self.value, "emoji": emoji} dice = Dice.de_json(json_dict, bot) assert dice.value == self.value @@ -51,15 +48,15 @@ def test_to_dict(self, dice): dice_dict = dice.to_dict() assert isinstance(dice_dict, dict) - assert dice_dict['value'] == dice.value - assert dice_dict['emoji'] == dice.emoji + assert dice_dict["value"] == dice.value + assert dice_dict["emoji"] == dice.emoji def test_equality(self): - a = Dice(3, '🎯') - b = Dice(3, '🎯') - c = Dice(3, '🎲') - d = Dice(4, '🎯') - e = BotCommand('start', 'description') + a = Dice(3, "🎯") + b = Dice(3, "🎯") + c = Dice(3, "🎲") + d = Dice(4, "🎯") + e = BotCommand("start", "description") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index 4c25f8a3ab1..0bfdb5c7737 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -34,14 +34,14 @@ BasePersistence, ContextTypes, ) -from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop -from telegram.utils.deprecate import TelegramDeprecationWarning +from telegram.ext import PersistenceInput +from telegram.ext.dispatcher import Dispatcher, DispatcherHandlerStop from telegram.utils.helpers import DEFAULT_FALSE from tests.conftest import create_dp from collections import defaultdict -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def dp2(bot): yield from create_dp(bot) @@ -52,31 +52,18 @@ class CustomContext(CallbackContext): class TestDispatcher: message_update = Update( - 1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') + 1, message=Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") ) received = None count = 0 - def test_slot_behaviour(self, dp2, recwarn, mro_slots): + def test_slot_behaviour(self, dp2, mro_slots): for at in dp2.__slots__: - at = f"_Dispatcher{at}" if at.startswith('__') and not at.endswith('__') else at - assert getattr(dp2, at, 'err') != 'err', f"got extra slot '{at}'" - assert not dp2.__dict__, f"got missing slot(s): {dp2.__dict__}" + at = f"_Dispatcher{at}" if at.startswith("__") and not at.endswith("__") else at + assert getattr(dp2, at, "err") != "err", f"got extra slot '{at}'" assert len(mro_slots(dp2)) == len(set(mro_slots(dp2))), "duplicate slot" - dp2.custom, dp2.running = 'should give warning', dp2.running - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - class CustomDispatcher(Dispatcher): - pass # Tests that setting custom attrs of Dispatcher subclass doesn't raise warning - - a = CustomDispatcher(None, None) - a.my_custom = 'no error!' - assert len(recwarn) == 1 - - dp2.__setattr__('__test', 'mangled success') - assert getattr(dp2, '_Dispatcher__test', 'e') == 'mangled success', "mangling failed" - - @pytest.fixture(autouse=True, name='reset') + @pytest.fixture(autouse=True, name="reset") def reset_fixture(self): self.reset() @@ -84,16 +71,13 @@ def reset(self): self.received = None self.count = 0 - def error_handler(self, bot, update, error): - self.received = error.message - def error_handler_context(self, update, context): self.received = context.error.message - def error_handler_raise_error(self, bot, update, error): - raise Exception('Failing bigly') + def error_handler_raise_error(self, update, context): + raise Exception("Failing bigly") - def callback_increase_count(self, bot, update): + def callback_increase_count(self, update, context): self.count += 1 def callback_set_count(self, count): @@ -102,14 +86,11 @@ def callback(bot, update): return callback - def callback_raise_error(self, bot, update): - if isinstance(bot, Bot): - raise TelegramError(update.message.text) - raise TelegramError(bot.message.text) + def callback_raise_error(self, update, context): + raise TelegramError(update.message.text) - def callback_if_not_update_queue(self, bot, update, update_queue=None): - if update_queue is not None: - self.received = update.message + def callback_received(self, update, context): + self.received = update.message def callback_context(self, update, context): if ( @@ -122,42 +103,42 @@ def callback_context(self, update, context): self.received = context.error.message def test_less_than_one_worker_warning(self, dp, recwarn): - Dispatcher(dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0, use_context=True) + Dispatcher(dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0) assert len(recwarn) == 1 assert ( str(recwarn[0].message) - == 'Asynchronous callbacks can not be processed without at least one worker thread.' + == "Asynchronous callbacks can not be processed without at least one worker thread." ) - def test_one_context_per_update(self, cdp): + def test_one_context_per_update(self, dp): def one(update, context): - if update.message.text == 'test': + if update.message.text == "test": context.my_flag = True def two(update, context): - if update.message.text == 'test': - if not hasattr(context, 'my_flag'): + if update.message.text == "test": + if not hasattr(context, "my_flag"): pytest.fail() else: - if hasattr(context, 'my_flag'): + if hasattr(context, "my_flag"): pytest.fail() - cdp.add_handler(MessageHandler(Filters.regex('test'), one), group=1) - cdp.add_handler(MessageHandler(None, two), group=2) - u = Update(1, Message(1, None, None, None, text='test')) - cdp.process_update(u) - u.message.text = 'something' - cdp.process_update(u) + dp.add_handler(MessageHandler(Filters.regex("test"), one), group=1) + dp.add_handler(MessageHandler(None, two), group=2) + u = Update(1, Message(1, None, None, None, text="test")) + dp.process_update(u) + u.message.text = "something" + dp.process_update(u) def test_error_handler(self, dp): - dp.add_error_handler(self.error_handler) - error = TelegramError('Unauthorized.') + dp.add_error_handler(self.error_handler_context) + error = TelegramError("Unauthorized.") dp.update_queue.put(error) sleep(0.1) - assert self.received == 'Unauthorized.' + assert self.received == "Unauthorized." # Remove handler - dp.remove_error_handler(self.error_handler) + dp.remove_error_handler(self.error_handler_context) self.reset() dp.update_queue.put(error) @@ -165,22 +146,19 @@ def test_error_handler(self, dp): assert self.received is None def test_double_add_error_handler(self, dp, caplog): - dp.add_error_handler(self.error_handler) + dp.add_error_handler(self.error_handler_context) with caplog.at_level(logging.DEBUG): - dp.add_error_handler(self.error_handler) + dp.add_error_handler(self.error_handler_context) assert len(caplog.records) == 1 - assert caplog.records[-1].getMessage().startswith('The callback is already registered') + assert caplog.records[-1].getMessage().startswith("The callback is already registered") def test_construction_with_bad_persistence(self, caplog, bot): class my_per: def __init__(self): - self.store_user_data = False - self.store_chat_data = False - self.store_bot_data = False - self.store_callback_data = False + self.store_data = PersistenceInput(False, False, False, False) with pytest.raises( - TypeError, match='persistence must be based on telegram.ext.BasePersistence' + TypeError, match="persistence must be based on telegram.ext.BasePersistence" ): Dispatcher(bot, None, persistence=my_per()) @@ -190,7 +168,7 @@ def test_error_handler_that_raises_errors(self, dp): """ handler_raise_error = MessageHandler(Filters.all, self.callback_raise_error) handler_increase_count = MessageHandler(Filters.all, self.callback_increase_count) - error = TelegramError('Unauthorized.') + error = TelegramError("Unauthorized.") dp.add_error_handler(self.error_handler_raise_error) @@ -208,7 +186,7 @@ def test_error_handler_that_raises_errors(self, dp): assert self.count == 1 - @pytest.mark.parametrize(['run_async', 'expected_output'], [(True, 5), (False, 0)]) + @pytest.mark.parametrize(["run_async", "expected_output"], [(True, 5), (False, 0)]) def test_default_run_async_error_handler(self, dp, monkeypatch, run_async, expected_output): def mock_async_err_handler(*args, **kwargs): self.count = 5 @@ -217,9 +195,9 @@ def mock_async_err_handler(*args, **kwargs): dp.bot.defaults = Defaults(run_async=run_async) try: dp.add_handler(MessageHandler(Filters.all, self.callback_raise_error)) - dp.add_error_handler(self.error_handler) + dp.add_error_handler(self.error_handler_context) - monkeypatch.setattr(dp, 'run_async', mock_async_err_handler) + monkeypatch.setattr(dp, "run_async", mock_async_err_handler) dp.process_update(self.message_update) assert self.count == expected_output @@ -229,17 +207,17 @@ def mock_async_err_handler(*args, **kwargs): dp.bot.defaults = None @pytest.mark.parametrize( - ['run_async', 'expected_output'], [(True, 'running async'), (False, None)] + ["run_async", "expected_output"], [(True, "running async"), (False, None)] ) def test_default_run_async(self, monkeypatch, dp, run_async, expected_output): def mock_run_async(*args, **kwargs): - self.received = 'running async' + self.received = "running async" # set defaults value to dp.bot dp.bot.defaults = Defaults(run_async=run_async) try: dp.add_handler(MessageHandler(Filters.all, lambda u, c: None)) - monkeypatch.setattr(dp, 'run_async', mock_run_async) + monkeypatch.setattr(dp, "run_async", mock_run_async) dp.process_update(self.message_update) assert self.received == expected_output @@ -264,65 +242,11 @@ def get_dispatcher_name(q): assert name1 != name2 - def test_multiple_run_async_decorator(self, dp, dp2): - # Make sure we got two dispatchers and that they are not the same - assert isinstance(dp, Dispatcher) - assert isinstance(dp2, Dispatcher) - assert dp is not dp2 - - @run_async - def must_raise_runtime_error(): - pass - - with pytest.raises(RuntimeError): - must_raise_runtime_error() - - def test_run_async_with_args(self, dp): - dp.add_handler( - MessageHandler( - Filters.all, run_async(self.callback_if_not_update_queue), pass_update_queue=True - ) - ) - - dp.update_queue.put(self.message_update) - sleep(0.1) - assert self.received == self.message_update.message - - def test_multiple_run_async_deprecation(self, dp): - assert isinstance(dp, Dispatcher) - - @run_async - def callback(update, context): - pass - - dp.add_handler(MessageHandler(Filters.all, callback)) - - with pytest.warns(TelegramDeprecationWarning, match='@run_async decorator'): - dp.process_update(self.message_update) - def test_async_raises_dispatcher_handler_stop(self, dp, caplog): - @run_async def callback(update, context): raise DispatcherHandlerStop() - dp.add_handler(MessageHandler(Filters.all, callback)) - - with caplog.at_level(logging.WARNING): - dp.update_queue.put(self.message_update) - sleep(0.1) - assert len(caplog.records) == 1 - assert ( - caplog.records[-1] - .getMessage() - .startswith('DispatcherHandlerStop is not supported ' 'with async functions') - ) - - def test_async_raises_exception(self, dp, caplog): - @run_async - def callback(update, context): - raise RuntimeError('async raising exception') - - dp.add_handler(MessageHandler(Filters.all, callback)) + dp.add_handler(MessageHandler(Filters.all, callback, run_async=True)) with caplog.at_level(logging.WARNING): dp.update_queue.put(self.message_update) @@ -331,15 +255,14 @@ def callback(update, context): assert ( caplog.records[-1] .getMessage() - .startswith('A promise with deactivated error handling') + .startswith("DispatcherHandlerStop is not supported with async functions") ) def test_add_async_handler(self, dp): dp.add_handler( MessageHandler( Filters.all, - self.callback_if_not_update_queue, - pass_update_queue=True, + self.callback_received, run_async=True, ) ) @@ -350,27 +273,19 @@ def test_add_async_handler(self, dp): def test_run_async_no_error_handler(self, dp, caplog): def func(): - raise RuntimeError('Async Error') + raise RuntimeError("Async Error") with caplog.at_level(logging.ERROR): dp.run_async(func) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].getMessage().startswith('No error handlers are registered') + assert caplog.records[-1].getMessage().startswith("No error handlers are registered") - def test_async_handler_error_handler(self, dp): + def test_async_handler_async_error_handler_context(self, dp): dp.add_handler(MessageHandler(Filters.all, self.callback_raise_error, run_async=True)) - dp.add_error_handler(self.error_handler) + dp.add_error_handler(self.error_handler_context, run_async=True) dp.update_queue.put(self.message_update) - sleep(0.1) - assert self.received == self.message_update.message.text - - def test_async_handler_async_error_handler_context(self, cdp): - cdp.add_handler(MessageHandler(Filters.all, self.callback_raise_error, run_async=True)) - cdp.add_error_handler(self.error_handler_context, run_async=True) - - cdp.update_queue.put(self.message_update) sleep(2) assert self.received == self.message_update.message.text @@ -383,7 +298,7 @@ def test_async_handler_error_handler_that_raises_error(self, dp, caplog): dp.update_queue.put(self.message_update) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].getMessage().startswith('An uncaught error was raised') + assert caplog.records[-1].getMessage().startswith("An uncaught error was raised") # Make sure that the main loop still runs dp.remove_handler(handler) @@ -401,7 +316,7 @@ def test_async_handler_async_error_handler_that_raises_error(self, dp, caplog): dp.update_queue.put(self.message_update) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].getMessage().startswith('An uncaught error was raised') + assert caplog.records[-1].getMessage().startswith("An uncaught error was raised") # Make sure that the main loop still runs dp.remove_handler(handler) @@ -412,7 +327,7 @@ def test_async_handler_async_error_handler_that_raises_error(self, dp, caplog): def test_error_in_handler(self, dp): dp.add_handler(MessageHandler(Filters.all, self.callback_raise_error)) - dp.add_error_handler(self.error_handler) + dp.add_error_handler(self.error_handler_context) dp.update_queue.put(self.message_update) sleep(0.1) @@ -457,29 +372,29 @@ def test_groups(self, dp): assert self.count == 3 def test_add_handler_errors(self, dp): - handler = 'not a handler' - with pytest.raises(TypeError, match='handler is not an instance of'): + handler = "not a handler" + with pytest.raises(TypeError, match="handler is not an instance of"): dp.add_handler(handler) handler = MessageHandler(Filters.photo, self.callback_set_count(1)) - with pytest.raises(TypeError, match='group is not int'): - dp.add_handler(handler, 'one') + with pytest.raises(TypeError, match="group is not int"): + dp.add_handler(handler, "one") def test_flow_stop(self, dp, bot): passed = [] def start1(b, u): - passed.append('start1') + passed.append("start1") raise DispatcherHandlerStop def start2(b, u): - passed.append('start2') + passed.append("start2") def start3(b, u): - passed.append('start3') + passed.append("start3") def error(b, u, e): - passed.append('error') + passed.append("error") passed.append(e) update = Update( @@ -489,9 +404,9 @@ def error(b, u, e): None, None, None, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ), @@ -499,29 +414,29 @@ def error(b, u, e): # If Stop raised handlers in other groups should not be called. passed = [] - dp.add_handler(CommandHandler('start', start1), 1) - dp.add_handler(CommandHandler('start', start3), 1) - dp.add_handler(CommandHandler('start', start2), 2) + dp.add_handler(CommandHandler("start", start1), 1) + dp.add_handler(CommandHandler("start", start3), 1) + dp.add_handler(CommandHandler("start", start2), 2) dp.process_update(update) - assert passed == ['start1'] + assert passed == ["start1"] def test_exception_in_handler(self, dp, bot): passed = [] - err = Exception('General exception') + err = Exception("General exception") - def start1(b, u): - passed.append('start1') + def start1(u, c): + passed.append("start1") raise err - def start2(b, u): - passed.append('start2') + def start2(u, c): + passed.append("start2") - def start3(b, u): - passed.append('start3') + def start3(u, c): + passed.append("start3") - def error(b, u, e): - passed.append('error') - passed.append(e) + def error(u, c): + passed.append("error") + passed.append(c.error) update = Update( 1, @@ -530,9 +445,9 @@ def error(b, u, e): None, None, None, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ), @@ -541,30 +456,30 @@ def error(b, u, e): # If an unhandled exception was caught, no further handlers from the same group should be # called. Also, the error handler should be called and receive the exception passed = [] - dp.add_handler(CommandHandler('start', start1), 1) - dp.add_handler(CommandHandler('start', start2), 1) - dp.add_handler(CommandHandler('start', start3), 2) + dp.add_handler(CommandHandler("start", start1), 1) + dp.add_handler(CommandHandler("start", start2), 1) + dp.add_handler(CommandHandler("start", start3), 2) dp.add_error_handler(error) dp.process_update(update) - assert passed == ['start1', 'error', err, 'start3'] + assert passed == ["start1", "error", err, "start3"] def test_telegram_error_in_handler(self, dp, bot): passed = [] - err = TelegramError('Telegram error') + err = TelegramError("Telegram error") - def start1(b, u): - passed.append('start1') + def start1(u, c): + passed.append("start1") raise err - def start2(b, u): - passed.append('start2') + def start2(u, c): + passed.append("start2") - def start3(b, u): - passed.append('start3') + def start3(u, c): + passed.append("start3") - def error(b, u, e): - passed.append('error') - passed.append(e) + def error(u, c): + passed.append("error") + passed.append(c.error) update = Update( 1, @@ -573,9 +488,9 @@ def error(b, u, e): None, None, None, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ), @@ -583,25 +498,18 @@ def error(b, u, e): # If a TelegramException was caught, an error handler should be called and no further # handlers from the same group should be called. - dp.add_handler(CommandHandler('start', start1), 1) - dp.add_handler(CommandHandler('start', start2), 1) - dp.add_handler(CommandHandler('start', start3), 2) + dp.add_handler(CommandHandler("start", start1), 1) + dp.add_handler(CommandHandler("start", start2), 1) + dp.add_handler(CommandHandler("start", start3), 2) dp.add_error_handler(error) dp.process_update(update) - assert passed == ['start1', 'error', err, 'start3'] + assert passed == ["start1", "error", err, "start3"] assert passed[2] is err def test_error_while_saving_chat_data(self, bot): increment = [] class OwnPersistence(BasePersistence): - def __init__(self): - super().__init__() - self.store_user_data = True - self.store_chat_data = True - self.store_bot_data = True - self.store_callback_data = True - def get_callback_data(self): return None @@ -632,10 +540,22 @@ def get_conversations(self, name): def update_conversation(self, name, key, new_state): pass - def start1(b, u): + def refresh_user_data(self, user_id, user_data): + pass + + def refresh_chat_data(self, chat_id, chat_data): + pass + + def refresh_bot_data(self, bot_data): + pass + + def flush(self): + pass + + def start1(u, c): pass - def error(b, u, e): + def error(u, c): increment.append("error") # If updating a user_data or chat_data from a persistence object throws an error, @@ -648,37 +568,37 @@ def error(b, u, e): None, Chat(1, "lala"), from_user=User(1, "Test", False), - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ), ) my_persistence = OwnPersistence() - dp = Dispatcher(bot, None, persistence=my_persistence, use_context=False) - dp.add_handler(CommandHandler('start', start1)) + dp = Dispatcher(bot, None, persistence=my_persistence) + dp.add_handler(CommandHandler("start", start1)) dp.add_error_handler(error) dp.process_update(update) assert increment == ["error", "error", "error", "error"] def test_flow_stop_in_error_handler(self, dp, bot): passed = [] - err = TelegramError('Telegram error') + err = TelegramError("Telegram error") - def start1(b, u): - passed.append('start1') + def start1(u, c): + passed.append("start1") raise err - def start2(b, u): - passed.append('start2') + def start2(u, c): + passed.append("start2") - def start3(b, u): - passed.append('start3') + def start3(u, c): + passed.append("start3") - def error(b, u, e): - passed.append('error') - passed.append(e) + def error(u, c): + passed.append("error") + passed.append(c.error) raise DispatcherHandlerStop update = Update( @@ -688,9 +608,9 @@ def error(b, u, e): None, None, None, - text='/start', + text="/start", entities=[ - MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len('/start')) + MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, length=len("/start")) ], bot=bot, ), @@ -698,44 +618,23 @@ def error(b, u, e): # If a TelegramException was caught, an error handler should be called and no further # handlers from the same group should be called. - dp.add_handler(CommandHandler('start', start1), 1) - dp.add_handler(CommandHandler('start', start2), 1) - dp.add_handler(CommandHandler('start', start3), 2) + dp.add_handler(CommandHandler("start", start1), 1) + dp.add_handler(CommandHandler("start", start2), 1) + dp.add_handler(CommandHandler("start", start3), 2) dp.add_error_handler(error) dp.process_update(update) - assert passed == ['start1', 'error', err] + assert passed == ["start1", "error", err] assert passed[2] is err - def test_error_handler_context(self, cdp): - cdp.add_error_handler(self.callback_context) - - error = TelegramError('Unauthorized.') - cdp.update_queue.put(error) - sleep(0.1) - assert self.received == 'Unauthorized.' - def test_sensible_worker_thread_names(self, dp2): thread_names = [thread.name for thread in dp2._Dispatcher__async_threads] for thread_name in thread_names: assert thread_name.startswith(f"Bot:{dp2.bot.id}:worker:") - def test_non_context_deprecation(self, dp): - with pytest.warns(TelegramDeprecationWarning): - Dispatcher( - dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0, use_context=False - ) - - def test_error_while_persisting(self, cdp, monkeypatch): + def test_error_while_persisting(self, dp, monkeypatch): class OwnPersistence(BasePersistence): - def __init__(self): - super().__init__() - self.store_user_data = True - self.store_chat_data = True - self.store_bot_data = True - self.store_callback_data = True - def update(self, data): - raise Exception('PersistenceError') + raise Exception("PersistenceError") def update_callback_data(self, data): self.update(data) @@ -776,6 +675,9 @@ def refresh_user_data(self, user_id, user_data): def refresh_chat_data(self, chat_id, chat_data): pass + def flush(self): + pass + def callback(update, context): pass @@ -783,31 +685,28 @@ def callback(update, context): def error(update, context): nonlocal test_flag - test_flag = str(context.error) == 'PersistenceError' - raise Exception('ErrorHandlingError') + test_flag = str(context.error) == "PersistenceError" + raise Exception("ErrorHandlingError") def logger(message): - assert 'uncaught error was raised while handling' in message + assert "uncaught error was raised while handling" in message update = Update( - 1, message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') + 1, message=Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") ) handler = MessageHandler(Filters.all, callback) - cdp.add_handler(handler) - cdp.add_error_handler(error) - monkeypatch.setattr(cdp.logger, 'exception', logger) + dp.add_handler(handler) + dp.add_error_handler(error) + monkeypatch.setattr(dp.logger, "exception", logger) - cdp.persistence = OwnPersistence() - cdp.process_update(update) + dp.persistence = OwnPersistence() + dp.process_update(update) assert test_flag - def test_persisting_no_user_no_chat(self, cdp): + def test_persisting_no_user_no_chat(self, dp): class OwnPersistence(BasePersistence): def __init__(self): super().__init__() - self.store_user_data = True - self.store_chat_data = True - self.store_bot_data = True self.test_flag_bot_data = False self.test_flag_chat_data = False self.test_flag_user_data = False @@ -845,29 +744,38 @@ def refresh_user_data(self, user_id, user_data): def refresh_chat_data(self, chat_id, chat_data): pass + def get_callback_data(self): + pass + + def update_callback_data(self, data): + pass + + def flush(self): + pass + def callback(update, context): pass handler = MessageHandler(Filters.all, callback) - cdp.add_handler(handler) - cdp.persistence = OwnPersistence() + dp.add_handler(handler) + dp.persistence = OwnPersistence() update = Update( - 1, message=Message(1, None, None, from_user=User(1, '', False), text='Text') + 1, message=Message(1, None, None, from_user=User(1, "", False), text="Text") ) - cdp.process_update(update) - assert cdp.persistence.test_flag_bot_data - assert cdp.persistence.test_flag_user_data - assert not cdp.persistence.test_flag_chat_data - - cdp.persistence.test_flag_bot_data = False - cdp.persistence.test_flag_user_data = False - cdp.persistence.test_flag_chat_data = False - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text='Text')) - cdp.process_update(update) - assert cdp.persistence.test_flag_bot_data - assert not cdp.persistence.test_flag_user_data - assert cdp.persistence.test_flag_chat_data + dp.process_update(update) + assert dp.persistence.test_flag_bot_data + assert dp.persistence.test_flag_user_data + assert not dp.persistence.test_flag_chat_data + + dp.persistence.test_flag_bot_data = False + dp.persistence.test_flag_user_data = False + dp.persistence.test_flag_chat_data = False + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text="Text")) + dp.process_update(update) + assert dp.persistence.test_flag_bot_data + assert not dp.persistence.test_flag_user_data + assert dp.persistence.test_flag_chat_data def test_update_persistence_once_per_update(self, monkeypatch, dp): def update_persistence(*args, **kwargs): @@ -876,16 +784,16 @@ def update_persistence(*args, **kwargs): def dummy_callback(*args): pass - monkeypatch.setattr(dp, 'update_persistence', update_persistence) + monkeypatch.setattr(dp, "update_persistence", update_persistence) for group in range(5): dp.add_handler(MessageHandler(Filters.text, dummy_callback), group=group) - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text=None)) + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text=None)) dp.process_update(update) assert self.count == 0 - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text='text')) + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text="text")) dp.process_update(update) assert self.count == 1 @@ -896,15 +804,15 @@ def update_persistence(*args, **kwargs): def dummy_callback(*args, **kwargs): pass - monkeypatch.setattr(dp, 'update_persistence', update_persistence) - monkeypatch.setattr(dp, 'run_async', dummy_callback) + monkeypatch.setattr(dp, "update_persistence", update_persistence) + monkeypatch.setattr(dp, "run_async", dummy_callback) for group in range(5): dp.add_handler( MessageHandler(Filters.text, dummy_callback, run_async=True), group=group ) - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text='Text')) + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text="Text")) dp.process_update(update) assert self.count == 0 @@ -913,13 +821,13 @@ def dummy_callback(*args, **kwargs): for group in range(5): dp.add_handler(MessageHandler(Filters.text, dummy_callback), group=group) - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text='Text')) + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text="Text")) dp.process_update(update) assert self.count == 0 finally: dp.bot.defaults = None - @pytest.mark.parametrize('run_async', [DEFAULT_FALSE, False]) + @pytest.mark.parametrize("run_async", [DEFAULT_FALSE, False]) def test_update_persistence_one_sync(self, monkeypatch, dp, run_async): def update_persistence(*args, **kwargs): self.count += 1 @@ -927,8 +835,8 @@ def update_persistence(*args, **kwargs): def dummy_callback(*args, **kwargs): pass - monkeypatch.setattr(dp, 'update_persistence', update_persistence) - monkeypatch.setattr(dp, 'run_async', dummy_callback) + monkeypatch.setattr(dp, "update_persistence", update_persistence) + monkeypatch.setattr(dp, "run_async", dummy_callback) for group in range(5): dp.add_handler( @@ -936,11 +844,11 @@ def dummy_callback(*args, **kwargs): ) dp.add_handler(MessageHandler(Filters.text, dummy_callback, run_async=run_async), group=5) - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text='Text')) + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text="Text")) dp.process_update(update) assert self.count == 1 - @pytest.mark.parametrize('run_async,expected', [(DEFAULT_FALSE, 1), (False, 1), (True, 0)]) + @pytest.mark.parametrize("run_async,expected", [(DEFAULT_FALSE, 1), (False, 1), (True, 0)]) def test_update_persistence_defaults_async(self, monkeypatch, dp, run_async, expected): def update_persistence(*args, **kwargs): self.count += 1 @@ -948,15 +856,15 @@ def update_persistence(*args, **kwargs): def dummy_callback(*args, **kwargs): pass - monkeypatch.setattr(dp, 'update_persistence', update_persistence) - monkeypatch.setattr(dp, 'run_async', dummy_callback) + monkeypatch.setattr(dp, "update_persistence", update_persistence) + monkeypatch.setattr(dp, "run_async", dummy_callback) dp.bot.defaults = Defaults(run_async=run_async) try: for group in range(5): dp.add_handler(MessageHandler(Filters.text, dummy_callback), group=group) - update = Update(1, message=Message(1, None, Chat(1, ''), from_user=None, text='Text')) + update = Update(1, message=Message(1, None, Chat(1, ""), from_user=None, text="Text")) dp.process_update(update) assert self.count == expected finally: diff --git a/tests/test_document.py b/tests/test_document.py index fa00faf6ea1..3991cfb902f 100644 --- a/tests/test_document.py +++ b/tests/test_document.py @@ -28,45 +28,42 @@ from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def document_file(): - f = open('tests/data/telegram.png', 'rb') + f = open("tests/data/telegram.png", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def document(bot, chat_id): - with open('tests/data/telegram.png', 'rb') as f: + with open("tests/data/telegram.png", "rb") as f: return bot.send_document(chat_id, document=f, timeout=50).document class TestDocument: - caption = 'DocumentTest - *Caption*' - document_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.gif' + caption = "DocumentTest - *Caption*" + document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif" file_size = 12948 - mime_type = 'image/png' - file_name = 'telegram.png' + mime_type = "image/png" + file_name = "telegram.png" thumb_file_size = 8090 thumb_width = 300 thumb_height = 300 - document_file_id = '5a3128a4d2a04750b5b58397f3b5e812' - document_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + document_file_id = "5a3128a4d2a04750b5b58397f3b5e812" + document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" - def test_slot_behaviour(self, document, recwarn, mro_slots): + def test_slot_behaviour(self, document, mro_slots): for attr in document.__slots__: - assert getattr(document, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not document.__dict__, f"got missing slot(s): {document.__dict__}" + assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(document)) == len(set(mro_slots(document))), "duplicate slot" - document.custom, document.file_name = 'should give warning', self.file_name - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), f"{recwarn}" def test_creation(self, document): assert isinstance(document, Document) assert isinstance(document.file_id, str) assert isinstance(document.file_unique_id, str) - assert document.file_id != '' - assert document.file_unique_id != '' + assert document.file_id != "" + assert document.file_unique_id != "" def test_expected_values(self, document): assert document.file_size == self.file_size @@ -83,21 +80,21 @@ def test_send_all_args(self, bot, chat_id, document_file, document, thumb_file): document=document_file, caption=self.caption, disable_notification=False, - filename='telegram_custom.png', - parse_mode='Markdown', + filename="telegram_custom.png", + parse_mode="Markdown", thumb=thumb_file, ) assert isinstance(message.document, Document) assert isinstance(message.document.file_id, str) - assert message.document.file_id != '' + assert message.document.file_id != "" assert isinstance(message.document.file_unique_id, str) - assert message.document.file_unique_id != '' + assert message.document.file_unique_id != "" assert isinstance(message.document.thumb, PhotoSize) - assert message.document.file_name == 'telegram_custom.png' + assert message.document.file_name == "telegram_custom.png" assert message.document.mime_type == document.mime_type assert message.document.file_size == document.file_size - assert message.caption == self.caption.replace('*', '') + assert message.caption == self.caption.replace("*", "") assert message.document.thumb.width == self.thumb_width assert message.document.thumb.height == self.thumb_height @@ -108,11 +105,11 @@ def test_get_and_download(self, bot, document): assert new_file.file_size == document.file_size assert new_file.file_id == document.file_id assert new_file.file_unique_id == document.file_unique_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.png') + new_file.download("telegram.png") - assert os.path.isfile('telegram.png') + assert os.path.isfile("telegram.png") @flaky(3, 1) def test_send_url_gif_file(self, bot, chat_id): @@ -122,12 +119,12 @@ def test_send_url_gif_file(self, bot, chat_id): assert isinstance(document, Document) assert isinstance(document.file_id, str) - assert document.file_id != '' + assert document.file_id != "" assert isinstance(message.document.file_unique_id, str) - assert message.document.file_unique_id != '' + assert message.document.file_unique_id != "" assert isinstance(document.thumb, PhotoSize) - assert document.file_name == 'telegram.gif' - assert document.mime_type == 'image/gif' + assert document.file_name == "telegram.gif" + assert document.mime_type == "image/gif" assert document.file_size == 3878 @flaky(3, 1) @@ -136,17 +133,17 @@ def test_send_resend(self, bot, chat_id, document): assert message.document == document - @pytest.mark.parametrize('disable_content_type_detection', [True, False, None]) + @pytest.mark.parametrize("disable_content_type_detection", [True, False, None]) def test_send_with_document( self, monkeypatch, bot, chat_id, document, disable_content_type_detection ): def make_assertion(url, data, **kwargs): type_detection = ( - data.get('disable_content_type_detection') == disable_content_type_detection + data.get("disable_content_type_detection") == disable_content_type_detection ) - return data['document'] == document.file_id and type_detection + return data["document"] == document.file_id and type_detection - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) message = bot.send_document( document=document, @@ -158,7 +155,7 @@ def make_assertion(url, data, **kwargs): @flaky(3, 1) def test_send_document_caption_entities(self, bot, chat_id, document): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -172,19 +169,19 @@ def test_send_document_caption_entities(self, bot, chat_id, document): assert message.caption_entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_document_default_parse_mode_1(self, default_bot, chat_id, document): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_document(chat_id, document, caption=test_markdown_string) assert message.caption_markdown == test_markdown_string assert message.caption == test_string @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_document_default_parse_mode_2(self, default_bot, chat_id, document): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_document( chat_id, document, caption=test_markdown_string, parse_mode=None @@ -193,30 +190,30 @@ def test_send_document_default_parse_mode_2(self, default_bot, chat_id, document assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_document_default_parse_mode_3(self, default_bot, chat_id, document): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_document( - chat_id, document, caption=test_markdown_string, parse_mode='HTML' + chat_id, document, caption=test_markdown_string, parse_mode="HTML" ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_document_default_allow_sending_without_reply( self, default_bot, chat_id, document, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_document( @@ -232,7 +229,7 @@ def test_send_document_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_document( chat_id, document, reply_to_message_id=reply_to_message.message_id ) @@ -240,26 +237,26 @@ def test_send_document_default_allow_sending_without_reply( def test_send_document_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('document') == expected and data.get('thumb') == expected + test_flag = data.get("document") == expected and data.get("thumb") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_document(chat_id, file, thumb=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") def test_de_json(self, bot, document): json_dict = { - 'file_id': self.document_file_id, - 'file_unique_id': self.document_file_unique_id, - 'thumb': document.thumb.to_dict(), - 'file_name': self.file_name, - 'mime_type': self.mime_type, - 'file_size': self.file_size, + "file_id": self.document_file_id, + "file_unique_id": self.document_file_unique_id, + "thumb": document.thumb.to_dict(), + "file_name": self.file_name, + "mime_type": self.mime_type, + "file_size": self.file_size, } test_document = Document.de_json(json_dict, bot) @@ -274,21 +271,21 @@ def test_to_dict(self, document): document_dict = document.to_dict() assert isinstance(document_dict, dict) - assert document_dict['file_id'] == document.file_id - assert document_dict['file_unique_id'] == document.file_unique_id - assert document_dict['file_name'] == document.file_name - assert document_dict['mime_type'] == document.mime_type - assert document_dict['file_size'] == document.file_size + assert document_dict["file_id"] == document.file_id + assert document_dict["file_unique_id"] == document.file_unique_id + assert document_dict["file_name"] == document.file_name + assert document_dict["mime_type"] == document.mime_type + assert document_dict["file_size"] == document.file_size @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): - with open(os.devnull, 'rb') as f, pytest.raises(TelegramError): + 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): with pytest.raises(TelegramError): - bot.send_document(chat_id=chat_id, document='') + bot.send_document(chat_id=chat_id, document="") def test_error_send_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -296,19 +293,19 @@ def test_error_send_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, document): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == document.file_id + return kwargs["file_id"] == document.file_id - assert check_shortcut_signature(Document.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(document.get_file, document.bot, 'get_file') + assert check_shortcut_signature(Document.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(document.get_file, document.bot, "get_file") assert check_defaults_handling(document.get_file, document.bot) - monkeypatch.setattr(document.bot, 'get_file', make_assertion) + monkeypatch.setattr(document.bot, "get_file", make_assertion) assert document.get_file() def test_equality(self, document): a = Document(document.file_id, document.file_unique_id) - b = Document('', document.file_unique_id) - d = Document('', '') + b = Document("", document.file_unique_id) + d = Document("", "") e = Voice(document.file_id, document.file_unique_id, 0) assert a == b diff --git a/tests/test_encryptedcredentials.py b/tests/test_encryptedcredentials.py index 085f82f12e4..fb44809ac92 100644 --- a/tests/test_encryptedcredentials.py +++ b/tests/test_encryptedcredentials.py @@ -22,7 +22,7 @@ from telegram import EncryptedCredentials, PassportElementError -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def encrypted_credentials(): return EncryptedCredentials( TestEncryptedCredentials.data, @@ -32,18 +32,15 @@ def encrypted_credentials(): class TestEncryptedCredentials: - data = 'data' - hash = 'hash' - secret = 'secret' + data = "data" + hash = "hash" + secret = "secret" - def test_slot_behaviour(self, encrypted_credentials, recwarn, mro_slots): + def test_slot_behaviour(self, encrypted_credentials, mro_slots): inst = encrypted_credentials for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.data = 'should give warning', self.data - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, encrypted_credentials): assert encrypted_credentials.data == self.data @@ -54,17 +51,17 @@ def test_to_dict(self, encrypted_credentials): encrypted_credentials_dict = encrypted_credentials.to_dict() assert isinstance(encrypted_credentials_dict, dict) - assert encrypted_credentials_dict['data'] == encrypted_credentials.data - assert encrypted_credentials_dict['hash'] == encrypted_credentials.hash - assert encrypted_credentials_dict['secret'] == encrypted_credentials.secret + assert encrypted_credentials_dict["data"] == encrypted_credentials.data + assert encrypted_credentials_dict["hash"] == encrypted_credentials.hash + assert encrypted_credentials_dict["secret"] == encrypted_credentials.secret def test_equality(self): a = EncryptedCredentials(self.data, self.hash, self.secret) b = EncryptedCredentials(self.data, self.hash, self.secret) - c = EncryptedCredentials(self.data, '', '') - d = EncryptedCredentials('', self.hash, '') - e = EncryptedCredentials('', '', self.secret) - f = PassportElementError('source', 'type', 'message') + c = EncryptedCredentials(self.data, "", "") + d = EncryptedCredentials("", self.hash, "") + e = EncryptedCredentials("", "", self.secret) + f = PassportElementError("source", "type", "message") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_encryptedpassportelement.py b/tests/test_encryptedpassportelement.py index 0505c5ad0e6..7474bef6f2c 100644 --- a/tests/test_encryptedpassportelement.py +++ b/tests/test_encryptedpassportelement.py @@ -22,10 +22,11 @@ from telegram import EncryptedPassportElement, PassportFile, PassportElementError -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def encrypted_passport_element(): return EncryptedPassportElement( TestEncryptedPassportElement.type_, + "this is a hash", data=TestEncryptedPassportElement.data, phone_number=TestEncryptedPassportElement.phone_number, email=TestEncryptedPassportElement.email, @@ -37,26 +38,25 @@ def encrypted_passport_element(): class TestEncryptedPassportElement: - type_ = 'type' - data = 'data' - phone_number = 'phone_number' - email = 'email' - files = [PassportFile('file_id', 50, 0)] - front_side = PassportFile('file_id', 50, 0) - reverse_side = PassportFile('file_id', 50, 0) - selfie = PassportFile('file_id', 50, 0) + type_ = "type" + hash = "this is a hash" + data = "data" + phone_number = "phone_number" + email = "email" + files = [PassportFile("file_id", 50, 0, 25)] + front_side = PassportFile("file_id", 50, 0, 25) + reverse_side = PassportFile("file_id", 50, 0, 25) + selfie = PassportFile("file_id", 50, 0, 25) - def test_slot_behaviour(self, encrypted_passport_element, recwarn, mro_slots): + def test_slot_behaviour(self, encrypted_passport_element, mro_slots): inst = encrypted_passport_element for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.phone_number = 'should give warning', self.phone_number - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, encrypted_passport_element): assert encrypted_passport_element.type == self.type_ + assert encrypted_passport_element.hash == self.hash assert encrypted_passport_element.data == self.data assert encrypted_passport_element.phone_number == self.phone_number assert encrypted_passport_element.email == self.email @@ -69,32 +69,32 @@ def test_to_dict(self, encrypted_passport_element): encrypted_passport_element_dict = encrypted_passport_element.to_dict() assert isinstance(encrypted_passport_element_dict, dict) - assert encrypted_passport_element_dict['type'] == encrypted_passport_element.type - assert encrypted_passport_element_dict['data'] == encrypted_passport_element.data + assert encrypted_passport_element_dict["type"] == encrypted_passport_element.type + assert encrypted_passport_element_dict["data"] == encrypted_passport_element.data assert ( - encrypted_passport_element_dict['phone_number'] + encrypted_passport_element_dict["phone_number"] == encrypted_passport_element.phone_number ) - assert encrypted_passport_element_dict['email'] == encrypted_passport_element.email - assert isinstance(encrypted_passport_element_dict['files'], list) + assert encrypted_passport_element_dict["email"] == encrypted_passport_element.email + assert isinstance(encrypted_passport_element_dict["files"], list) assert ( - encrypted_passport_element_dict['front_side'] + encrypted_passport_element_dict["front_side"] == encrypted_passport_element.front_side.to_dict() ) assert ( - encrypted_passport_element_dict['reverse_side'] + encrypted_passport_element_dict["reverse_side"] == encrypted_passport_element.reverse_side.to_dict() ) assert ( - encrypted_passport_element_dict['selfie'] + encrypted_passport_element_dict["selfie"] == encrypted_passport_element.selfie.to_dict() ) def test_equality(self): - a = EncryptedPassportElement(self.type_, data=self.data) - b = EncryptedPassportElement(self.type_, data=self.data) - c = EncryptedPassportElement(self.data, '') - d = PassportElementError('source', 'type', 'message') + a = EncryptedPassportElement(self.type_, self.hash, data=self.data) + b = EncryptedPassportElement(self.type_, self.hash, data=self.data) + c = EncryptedPassportElement(self.data, "") + d = PassportElementError("source", "type", "message") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_error.py b/tests/test_error.py index 1b2eebac1d9..4231339b3ca 100644 --- a/tests/test_error.py +++ b/tests/test_error.py @@ -21,7 +21,7 @@ import pytest -from telegram import TelegramError, TelegramDecryptionError +from telegram import TelegramError, PassportDecryptionError from telegram.error import ( Unauthorized, InvalidToken, @@ -97,8 +97,8 @@ def test_retry_after(self): raise RetryAfter(12) def test_conflict(self): - with pytest.raises(Conflict, match='Something something.'): - raise Conflict('Something something.') + with pytest.raises(Conflict, match="Something something."): + raise Conflict("Something something.") @pytest.mark.parametrize( "exception, attributes", @@ -112,8 +112,8 @@ def test_conflict(self): (ChatMigrated(1234), ["message", "new_chat_id"]), (RetryAfter(12), ["message", "retry_after"]), (Conflict("test message"), ["message"]), - (TelegramDecryptionError("test message"), ["message"]), - (InvalidCallbackData('test data'), ['callback_data']), + (PassportDecryptionError("test message"), ["message"]), + (InvalidCallbackData("test data"), ["callback_data"]), ], ) def test_errors_pickling(self, exception, attributes): @@ -147,7 +147,7 @@ def make_assertion(cls): ChatMigrated, RetryAfter, Conflict, - TelegramDecryptionError, + PassportDecryptionError, InvalidCallbackData, }, NetworkError: {BadRequest, TimedOut}, diff --git a/tests/test_file.py b/tests/test_file.py index 953be29e9ab..c4d1a841a23 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -26,7 +26,7 @@ from telegram import File, TelegramError, Voice -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def file(bot): return File( TestFile.file_id, @@ -37,40 +37,37 @@ def file(bot): ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def local_file(bot): return File( TestFile.file_id, TestFile.file_unique_id, - file_path=str(Path.cwd() / 'tests' / 'data' / 'local_file.txt'), + file_path=str(Path.cwd() / "tests" / "data" / "local_file.txt"), file_size=TestFile.file_size, bot=bot, ) class TestFile: - file_id = 'NOTVALIDDOESNOTMATTER' - file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + file_id = "NOTVALIDDOESNOTMATTER" + file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" file_path = ( - 'https://api.org/file/bot133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0/document/file_3' + "https://api.org/file/bot133505823:AAHZFMHno3mzVLErU5b5jJvaeG--qUyLyG0/document/file_3" ) file_size = 28232 - file_content = 'Saint-SaΓ«ns'.encode() # Intentionally contains unicode chars. + file_content = "Saint-SaΓ«ns".encode() # Intentionally contains unicode chars. - def test_slot_behaviour(self, file, recwarn, mro_slots): + def test_slot_behaviour(self, file, mro_slots): for attr in file.__slots__: - assert getattr(file, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not file.__dict__, f"got missing slot(s): {file.__dict__}" + assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(file)) == len(set(mro_slots(file))), "duplicate slot" - file.custom, file.file_id = 'should give warning', self.file_id - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'file_id': self.file_id, - 'file_unique_id': self.file_unique_id, - 'file_path': self.file_path, - 'file_size': self.file_size, + "file_id": self.file_id, + "file_unique_id": self.file_unique_id, + "file_path": self.file_path, + "file_size": self.file_size, } new_file = File.de_json(json_dict, bot) @@ -83,29 +80,29 @@ def test_to_dict(self, file): file_dict = file.to_dict() assert isinstance(file_dict, dict) - assert file_dict['file_id'] == file.file_id - assert file_dict['file_unique_id'] == file.file_unique_id - assert file_dict['file_path'] == file.file_path - assert file_dict['file_size'] == file.file_size + assert file_dict["file_id"] == file.file_id + assert file_dict["file_unique_id"] == file.file_unique_id + assert file_dict["file_path"] == file.file_path + assert file_dict["file_size"] == file.file_size @flaky(3, 1) def test_error_get_empty_file_id(self, bot): with pytest.raises(TelegramError): - bot.get_file(file_id='') + bot.get_file(file_id="") def test_download_mutuall_exclusive(self, file): - with pytest.raises(ValueError, match='custom_path and out are mutually exclusive'): - file.download('custom_path', 'out') + with pytest.raises(ValueError, match="custom_path and out are mutually exclusive"): + file.download("custom_path", "out") def test_download(self, monkeypatch, file): def test(*args, **kwargs): return self.file_content - monkeypatch.setattr('telegram.utils.request.Request.retrieve', test) + monkeypatch.setattr("telegram.utils.request.Request.retrieve", test) out_file = file.download() try: - with open(out_file, 'rb') as fobj: + with open(out_file, "rb") as fobj: assert fobj.read() == self.file_content finally: os.unlink(out_file) @@ -117,13 +114,13 @@ def test_download_custom_path(self, monkeypatch, file): def test(*args, **kwargs): return self.file_content - monkeypatch.setattr('telegram.utils.request.Request.retrieve', test) + monkeypatch.setattr("telegram.utils.request.Request.retrieve", test) file_handle, custom_path = mkstemp() try: out_file = file.download(custom_path) assert out_file == custom_path - with open(out_file, 'rb') as fobj: + with open(out_file, "rb") as fobj: assert fobj.read() == self.file_content finally: os.close(file_handle) @@ -135,7 +132,7 @@ def test_download_custom_path_local_file(self, local_file): out_file = local_file.download(custom_path) assert out_file == custom_path - with open(out_file, 'rb') as fobj: + with open(out_file, "rb") as fobj: assert fobj.read() == self.file_content finally: os.close(file_handle) @@ -147,12 +144,12 @@ def test(*args, **kwargs): file.file_path = None - monkeypatch.setattr('telegram.utils.request.Request.retrieve', test) + monkeypatch.setattr("telegram.utils.request.Request.retrieve", test) out_file = file.download() assert out_file[-len(file.file_id) :] == file.file_id try: - with open(out_file, 'rb') as fobj: + with open(out_file, "rb") as fobj: assert fobj.read() == self.file_content finally: os.unlink(out_file) @@ -161,7 +158,7 @@ def test_download_file_obj(self, monkeypatch, file): def test(*args, **kwargs): return self.file_content - monkeypatch.setattr('telegram.utils.request.Request.retrieve', test) + monkeypatch.setattr("telegram.utils.request.Request.retrieve", test) with TemporaryFile() as custom_fobj: out_fobj = file.download(out=custom_fobj) assert out_fobj is custom_fobj @@ -181,7 +178,7 @@ def test_download_bytearray(self, monkeypatch, file): def test(*args, **kwargs): return self.file_content - monkeypatch.setattr('telegram.utils.request.Request.retrieve', test) + monkeypatch.setattr("telegram.utils.request.Request.retrieve", test) # Check that a download to a newly allocated bytearray works. buf = file.download_as_bytearray() @@ -208,9 +205,9 @@ def test_download_bytearray_local_file(self, local_file): def test_equality(self, bot): a = File(self.file_id, self.file_unique_id, bot) - b = File('', self.file_unique_id, bot) + b = File("", self.file_unique_id, bot) c = File(self.file_id, self.file_unique_id, None) - d = File('', '', bot) + d = File("", "", bot) e = Voice(self.file_id, self.file_unique_id, 0) assert a == b diff --git a/tests/test_filters.py b/tests/test_filters.py index efebc477faf..169654b4e23 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -22,46 +22,44 @@ from telegram import Message, User, Chat, MessageEntity, Document, Update, Dice from telegram.ext import Filters, BaseFilter, MessageFilter, UpdateFilter -from sys import version_info as py_ver + import inspect import re -from telegram.utils.deprecate import TelegramDeprecationWarning - -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def update(): return Update( 0, Message( 0, datetime.datetime.utcnow(), - Chat(0, 'private'), - from_user=User(0, 'Testuser', False), + Chat(0, "private"), + from_user=User(0, "Testuser", False), via_bot=User(0, "Testbot", True), - sender_chat=Chat(0, 'Channel'), + sender_chat=Chat(0, "Channel"), forward_from=User(0, "HAL9000", False), forward_from_chat=Chat(0, "Channel"), ), ) -@pytest.fixture(scope='function', params=MessageEntity.ALL_TYPES) +@pytest.fixture(scope="function", params=MessageEntity.ALL_TYPES) def message_entity(request): - return MessageEntity(request.param, 0, 0, url='', user=User(1, 'first_name', False)) + return MessageEntity(request.param, 0, 0, url="", user=User(1, "first_name", False)) @pytest.fixture( - scope='class', - params=[{'class': MessageFilter}, {'class': UpdateFilter}], - ids=['MessageFilter', 'UpdateFilter'], + scope="class", + params=[{"class": MessageFilter}, {"class": UpdateFilter}], + ids=["MessageFilter", "UpdateFilter"], ) def base_class(request): - return request.param['class'] + return request.param["class"] class TestFilters: - def test_all_filters_slot_behaviour(self, recwarn, mro_slots): + def test_all_filters_slot_behaviour(self, mro_slots): """ Use depth first search to get all nested filters, and instantiate them (which need it) with the correct number of arguments, then test each filter separately. Also tests setting @@ -89,68 +87,58 @@ def test_all_filters_slot_behaviour(self, recwarn, mro_slots): # Now start the actual testing for name, cls in classes: # Can't instantiate abstract classes without overriding methods, so skip them for now - if inspect.isabstract(cls) or name in {'__class__', '__base__'}: + if inspect.isabstract(cls) or name in {"__class__", "__base__"}: continue - assert '__slots__' in cls.__dict__, f"Filter {name!r} doesn't have __slots__" + assert "__slots__" in cls.__dict__, f"Filter {name!r} doesn't have __slots__" # get no. of args minus the 'self' argument args = len(inspect.signature(cls.__init__).parameters) - 1 - if cls.__base__.__name__ == '_ChatUserBaseFilter': # Special case, only 1 arg needed - inst = cls('1') + if cls.__base__.__name__ == "_ChatUserBaseFilter": # Special case, only 1 arg needed + inst = cls("1") else: - inst = cls() if args < 1 else cls(*['blah'] * args) # unpack variable no. of args + inst = cls() if args < 1 else cls(*["blah"] * args) # unpack variable no. of args - for attr in cls.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}' for {name}" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__} for {name}" - assert len(mro_slots(inst)) == len(set(mro_slots(inst))), f"same slot in {name}" + assert len(mro_slots(inst)) == len(set(mro_slots(inst))), f"same slot in {name}" - with pytest.warns(TelegramDeprecationWarning, match='custom attributes') as warn: - inst.custom = 'should give warning' - if not warn: - pytest.fail(f"Filter {name!r} didn't warn when setting custom attr") - - assert '__dict__' not in BaseFilter.__slots__ if py_ver < (3, 7) else True, 'dict in abc' + for attr in cls.__slots__: + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}' for {name}" class CustomFilter(MessageFilter): def filter(self, message: Message): pass with pytest.warns(None): - CustomFilter().custom = 'allowed' # Test setting custom attr to custom filters - - with pytest.warns(TelegramDeprecationWarning, match='custom attributes'): - Filters().custom = 'raise warning' + CustomFilter().custom = "allowed" # Test setting custom attr to custom filters def test_filters_all(self, update): assert Filters.all(update) def test_filters_text(self, update): - update.message.text = 'test' + update.message.text = "test" assert (Filters.text)(update) - update.message.text = '/test' + update.message.text = "/test" assert (Filters.text)(update) def test_filters_text_strings(self, update): - update.message.text = '/test' - assert Filters.text({'/test', 'test1'})(update) - assert not Filters.text(['test1', 'test2'])(update) + update.message.text = "/test" + assert Filters.text({"/test", "test1"})(update) + assert not Filters.text(["test1", "test2"])(update) def test_filters_caption(self, update): - update.message.caption = 'test' + update.message.caption = "test" assert (Filters.caption)(update) update.message.caption = None assert not (Filters.caption)(update) def test_filters_caption_strings(self, update): - update.message.caption = 'test' - assert Filters.caption({'test', 'test1'})(update) - assert not Filters.caption(['test1', 'test2'])(update) + update.message.caption = "test" + assert Filters.caption({"test", "test1"})(update) + assert not Filters.caption(["test1", "test2"])(update) def test_filters_command_default(self, update): - update.message.text = 'test' + update.message.text = "test" assert not Filters.command(update) - update.message.text = '/test' + update.message.text = "/test" assert not Filters.command(update) # Only accept commands at the beginning update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 3, 5)] @@ -159,95 +147,95 @@ def test_filters_command_default(self, update): assert Filters.command(update) def test_filters_command_anywhere(self, update): - update.message.text = 'test /cmd' + update.message.text = "test /cmd" assert not (Filters.command(False))(update) update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 5, 4)] assert (Filters.command(False))(update) def test_filters_regex(self, update): SRE_TYPE = type(re.match("", "")) - update.message.text = '/start deep-linked param' - result = Filters.regex(r'deep-linked param')(update) + update.message.text = "/start deep-linked param" + result = Filters.regex(r"deep-linked param")(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert type(matches[0]) is SRE_TYPE - update.message.text = '/help' - assert Filters.regex(r'help')(update) + update.message.text = "/help" + assert Filters.regex(r"help")(update) - update.message.text = 'test' - assert not Filters.regex(r'fail')(update) - assert Filters.regex(r'test')(update) - assert Filters.regex(re.compile(r'test'))(update) - assert Filters.regex(re.compile(r'TEST', re.IGNORECASE))(update) + update.message.text = "test" + assert not Filters.regex(r"fail")(update) + assert Filters.regex(r"test")(update) + assert Filters.regex(re.compile(r"test"))(update) + assert Filters.regex(re.compile(r"TEST", re.IGNORECASE))(update) - update.message.text = 'i love python' - assert Filters.regex(r'.\b[lo]{2}ve python')(update) + update.message.text = "i love python" + assert Filters.regex(r".\b[lo]{2}ve python")(update) update.message.text = None - assert not Filters.regex(r'fail')(update) + assert not Filters.regex(r"fail")(update) def test_filters_regex_multiple(self, update): SRE_TYPE = type(re.match("", "")) - update.message.text = '/start deep-linked param' - result = (Filters.regex('deep') & Filters.regex(r'linked param'))(update) + update.message.text = "/start deep-linked param" + result = (Filters.regex("deep") & Filters.regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.regex('deep') | Filters.regex(r'linked param'))(update) + result = (Filters.regex("deep") | Filters.regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.regex('not int') | Filters.regex(r'linked param'))(update) + result = (Filters.regex("not int") | Filters.regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.regex('not int') & Filters.regex(r'linked param'))(update) + result = (Filters.regex("not int") & Filters.regex(r"linked param"))(update) assert not result def test_filters_merged_with_regex(self, update): SRE_TYPE = type(re.match("", "")) - update.message.text = '/start deep-linked param' + update.message.text = "/start deep-linked param" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] - result = (Filters.command & Filters.regex(r'linked param'))(update) + result = (Filters.command & Filters.regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.regex(r'linked param') & Filters.command)(update) + result = (Filters.regex(r"linked param") & Filters.command)(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.regex(r'linked param') | Filters.command)(update) + result = (Filters.regex(r"linked param") | Filters.command)(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) 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) + result = (Filters.command | Filters.regex(r"linked param"))(update) assert result is True def test_regex_complex_merges(self, update): SRE_TYPE = type(re.match("", "")) - update.message.text = 'test it out' - test_filter = Filters.regex('test') & ( - (Filters.status_update | Filters.forwarded) | Filters.regex('out') + update.message.text = "test it out" + test_filter = Filters.regex("test") & ( + (Filters.status_update | Filters.forwarded) | Filters.regex("out") ) result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 2 assert all(type(res) is SRE_TYPE for res in matches) @@ -255,206 +243,206 @@ def test_regex_complex_merges(self, update): result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - update.message.text = 'test it' + update.message.text = "test it" result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) update.message.forward_date = None result = test_filter(update) assert not result - update.message.text = 'test it out' + update.message.text = "test it out" result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) update.message.pinned_message = True result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - update.message.text = 'it out' + update.message.text = "it out" result = test_filter(update) assert not result - update.message.text = 'test it out' + update.message.text = "test it out" update.message.forward_date = None update.message.pinned_message = None - test_filter = (Filters.regex('test') | Filters.command) & ( - Filters.regex('it') | Filters.status_update + test_filter = (Filters.regex("test") | Filters.command) & ( + Filters.regex("it") | Filters.status_update ) result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 2 assert all(type(res) is SRE_TYPE for res in matches) - update.message.text = 'test' + update.message.text = "test" result = test_filter(update) assert not result update.message.pinned_message = True result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 1 assert all(type(res) is SRE_TYPE for res in matches) - update.message.text = 'nothing' + update.message.text = "nothing" result = test_filter(update) assert not result - update.message.text = '/start' + update.message.text = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] result = test_filter(update) assert result assert isinstance(result, bool) - update.message.text = '/start it' + update.message.text = "/start it" result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 1 assert all(type(res) is SRE_TYPE for res in matches) def test_regex_inverted(self, update): - update.message.text = '/start deep-linked param' + update.message.text = "/start deep-linked param" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] - filter = ~Filters.regex(r'deep-linked param') + filter = ~Filters.regex(r"deep-linked param") result = filter(update) assert not result - update.message.text = 'not it' + update.message.text = "not it" result = filter(update) assert result assert isinstance(result, bool) - filter = ~Filters.regex('linked') & Filters.command + filter = ~Filters.regex("linked") & Filters.command update.message.text = "it's linked" result = filter(update) assert not result - update.message.text = '/start' + update.message.text = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] result = filter(update) assert result - update.message.text = '/linked' + update.message.text = "/linked" result = filter(update) assert not result - filter = ~Filters.regex('linked') | Filters.command + filter = ~Filters.regex("linked") | Filters.command update.message.text = "it's linked" update.message.entities = [] result = filter(update) assert not result - update.message.text = '/start linked' + update.message.text = "/start linked" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] result = filter(update) assert result - update.message.text = '/start' + update.message.text = "/start" result = filter(update) assert result - update.message.text = 'nothig' + update.message.text = "nothig" update.message.entities = [] result = filter(update) assert result def test_filters_caption_regex(self, update): SRE_TYPE = type(re.match("", "")) - update.message.caption = '/start deep-linked param' - result = Filters.caption_regex(r'deep-linked param')(update) + update.message.caption = "/start deep-linked param" + result = Filters.caption_regex(r"deep-linked param")(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert type(matches[0]) is SRE_TYPE - update.message.caption = '/help' - assert Filters.caption_regex(r'help')(update) + update.message.caption = "/help" + assert Filters.caption_regex(r"help")(update) - update.message.caption = 'test' - assert not Filters.caption_regex(r'fail')(update) - assert Filters.caption_regex(r'test')(update) - assert Filters.caption_regex(re.compile(r'test'))(update) - assert Filters.caption_regex(re.compile(r'TEST', re.IGNORECASE))(update) + update.message.caption = "test" + assert not Filters.caption_regex(r"fail")(update) + assert Filters.caption_regex(r"test")(update) + assert Filters.caption_regex(re.compile(r"test"))(update) + assert Filters.caption_regex(re.compile(r"TEST", re.IGNORECASE))(update) - update.message.caption = 'i love python' - assert Filters.caption_regex(r'.\b[lo]{2}ve python')(update) + update.message.caption = "i love python" + assert Filters.caption_regex(r".\b[lo]{2}ve python")(update) update.message.caption = None - assert not Filters.caption_regex(r'fail')(update) + assert not Filters.caption_regex(r"fail")(update) def test_filters_caption_regex_multiple(self, update): SRE_TYPE = type(re.match("", "")) - update.message.caption = '/start deep-linked param' - result = (Filters.caption_regex('deep') & Filters.caption_regex(r'linked param'))(update) + update.message.caption = "/start deep-linked param" + result = (Filters.caption_regex("deep") & Filters.caption_regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.caption_regex('deep') | Filters.caption_regex(r'linked param'))(update) + result = (Filters.caption_regex("deep") | Filters.caption_regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.caption_regex('not int') | Filters.caption_regex(r'linked param'))( + result = (Filters.caption_regex("not int") | Filters.caption_regex(r"linked param"))( update ) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.caption_regex('not int') & Filters.caption_regex(r'linked param'))( + result = (Filters.caption_regex("not int") & Filters.caption_regex(r"linked param"))( update ) assert not result def test_filters_merged_with_caption_regex(self, update): SRE_TYPE = type(re.match("", "")) - update.message.caption = '/start deep-linked param' + update.message.caption = "/start deep-linked param" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] - result = (Filters.command & Filters.caption_regex(r'linked param'))(update) + result = (Filters.command & Filters.caption_regex(r"linked param"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.caption_regex(r'linked param') & Filters.command)(update) + result = (Filters.caption_regex(r"linked param") & Filters.command)(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - result = (Filters.caption_regex(r'linked param') | Filters.command)(update) + result = (Filters.caption_regex(r"linked param") | Filters.command)(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) 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) + result = (Filters.command | Filters.caption_regex(r"linked param"))(update) assert result is True def test_caption_regex_complex_merges(self, update): SRE_TYPE = type(re.match("", "")) - update.message.caption = 'test it out' - test_filter = Filters.caption_regex('test') & ( - (Filters.status_update | Filters.forwarded) | Filters.caption_regex('out') + update.message.caption = "test it out" + test_filter = Filters.caption_regex("test") & ( + (Filters.status_update | Filters.forwarded) | Filters.caption_regex("out") ) result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 2 assert all(type(res) is SRE_TYPE for res in matches) @@ -462,114 +450,114 @@ def test_caption_regex_complex_merges(self, update): result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - update.message.caption = 'test it' + update.message.caption = "test it" result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) update.message.forward_date = None result = test_filter(update) assert not result - update.message.caption = 'test it out' + update.message.caption = "test it out" result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) update.message.pinned_message = True result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert all(type(res) is SRE_TYPE for res in matches) - update.message.caption = 'it out' + update.message.caption = "it out" result = test_filter(update) assert not result - update.message.caption = 'test it out' + update.message.caption = "test it out" update.message.forward_date = None update.message.pinned_message = None - test_filter = (Filters.caption_regex('test') | Filters.command) & ( - Filters.caption_regex('it') | Filters.status_update + test_filter = (Filters.caption_regex("test") | Filters.command) & ( + Filters.caption_regex("it") | Filters.status_update ) result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 2 assert all(type(res) is SRE_TYPE for res in matches) - update.message.caption = 'test' + update.message.caption = "test" result = test_filter(update) assert not result update.message.pinned_message = True result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 1 assert all(type(res) is SRE_TYPE for res in matches) - update.message.caption = 'nothing' + update.message.caption = "nothing" result = test_filter(update) assert not result - update.message.caption = '/start' + update.message.caption = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] result = test_filter(update) assert result assert isinstance(result, bool) - update.message.caption = '/start it' + update.message.caption = "/start it" result = test_filter(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert len(matches) == 1 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.caption = "/start deep-linked param" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] - test_filter = ~Filters.caption_regex(r'deep-linked param') + test_filter = ~Filters.caption_regex(r"deep-linked param") result = test_filter(update) assert not result - update.message.caption = 'not it' + update.message.caption = "not it" result = test_filter(update) assert result assert isinstance(result, bool) - test_filter = ~Filters.caption_regex('linked') & Filters.command + test_filter = ~Filters.caption_regex("linked") & Filters.command update.message.caption = "it's linked" result = test_filter(update) assert not result - update.message.caption = '/start' + update.message.caption = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] result = test_filter(update) assert result - update.message.caption = '/linked' + update.message.caption = "/linked" result = test_filter(update) assert not result - test_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 = test_filter(update) assert not result - update.message.caption = '/start linked' + update.message.caption = "/start linked" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] result = test_filter(update) assert result - update.message.caption = '/start' + update.message.caption = "/start" result = test_filter(update) assert result - update.message.caption = 'nothig' + update.message.caption = "nothig" update.message.entities = [] result = test_filter(update) assert result @@ -578,27 +566,27 @@ def test_filters_reply(self, update): another_message = Message( 1, datetime.datetime.utcnow(), - Chat(0, 'private'), - from_user=User(1, 'TestOther', False), + Chat(0, "private"), + from_user=User(1, "TestOther", False), ) - update.message.text = 'test' + update.message.text = "test" assert not Filters.reply(update) update.message.reply_to_message = another_message assert Filters.reply(update) def test_filters_audio(self, update): assert not Filters.audio(update) - update.message.audio = 'test' + update.message.audio = "test" assert Filters.audio(update) def test_filters_document(self, update): assert not Filters.document(update) - update.message.document = 'test' + update.message.document = "test" assert Filters.document(update) def test_filters_document_type(self, update): update.message.document = Document( - "file_id", 'unique_id', mime_type="application/vnd.android.package-archive" + "file_id", "unique_id", mime_type="application/vnd.android.package-archive" ) assert Filters.document.apk(update) assert Filters.document.application(update) @@ -807,68 +795,68 @@ def test_filters_file_extension_name(self): def test_filters_animation(self, update): assert not Filters.animation(update) - update.message.animation = 'test' + update.message.animation = "test" assert Filters.animation(update) def test_filters_photo(self, update): assert not Filters.photo(update) - update.message.photo = 'test' + update.message.photo = "test" assert Filters.photo(update) def test_filters_sticker(self, update): assert not Filters.sticker(update) - update.message.sticker = 'test' + update.message.sticker = "test" assert Filters.sticker(update) def test_filters_video(self, update): assert not Filters.video(update) - update.message.video = 'test' + update.message.video = "test" assert Filters.video(update) def test_filters_voice(self, update): assert not Filters.voice(update) - update.message.voice = 'test' + update.message.voice = "test" assert Filters.voice(update) def test_filters_video_note(self, update): assert not Filters.video_note(update) - update.message.video_note = 'test' + update.message.video_note = "test" assert Filters.video_note(update) def test_filters_contact(self, update): assert not Filters.contact(update) - update.message.contact = 'test' + update.message.contact = "test" assert Filters.contact(update) def test_filters_location(self, update): assert not Filters.location(update) - update.message.location = 'test' + update.message.location = "test" assert Filters.location(update) def test_filters_venue(self, update): assert not Filters.venue(update) - update.message.venue = 'test' + update.message.venue = "test" assert Filters.venue(update) def test_filters_status_update(self, update): assert not Filters.status_update(update) - update.message.new_chat_members = ['test'] + update.message.new_chat_members = ["test"] assert Filters.status_update(update) assert Filters.status_update.new_chat_members(update) update.message.new_chat_members = None - update.message.left_chat_member = 'test' + update.message.left_chat_member = "test" assert Filters.status_update(update) assert Filters.status_update.left_chat_member(update) update.message.left_chat_member = None - update.message.new_chat_title = 'test' + update.message.new_chat_title = "test" assert Filters.status_update(update) assert Filters.status_update.new_chat_title(update) - update.message.new_chat_title = '' + update.message.new_chat_title = "" - update.message.new_chat_photo = 'test' + update.message.new_chat_photo = "test" assert Filters.status_update(update) assert Filters.status_update.new_chat_photo(update) update.message.new_chat_photo = None @@ -908,37 +896,37 @@ def test_filters_status_update(self, update): assert Filters.status_update.migrate(update) update.message.migrate_from_chat_id = 0 - update.message.pinned_message = 'test' + update.message.pinned_message = "test" assert Filters.status_update(update) assert Filters.status_update.pinned_message(update) update.message.pinned_message = None - update.message.connected_website = 'http://example.com/' + update.message.connected_website = "http://example.com/" assert Filters.status_update(update) assert Filters.status_update.connected_website(update) update.message.connected_website = None - update.message.proximity_alert_triggered = 'alert' + update.message.proximity_alert_triggered = "alert" assert Filters.status_update(update) assert Filters.status_update.proximity_alert_triggered(update) update.message.proximity_alert_triggered = None - update.message.voice_chat_scheduled = 'scheduled' + update.message.voice_chat_scheduled = "scheduled" assert Filters.status_update(update) assert Filters.status_update.voice_chat_scheduled(update) update.message.voice_chat_scheduled = None - update.message.voice_chat_started = 'hello' + update.message.voice_chat_started = "hello" assert Filters.status_update(update) assert Filters.status_update.voice_chat_started(update) update.message.voice_chat_started = None - update.message.voice_chat_ended = 'bye' + update.message.voice_chat_ended = "bye" assert Filters.status_update(update) assert Filters.status_update.voice_chat_ended(update) update.message.voice_chat_ended = None - update.message.voice_chat_participants_invited = 'invited' + update.message.voice_chat_participants_invited = "invited" assert Filters.status_update(update) assert Filters.status_update.voice_chat_participants_invited(update) update.message.voice_chat_participants_invited = None @@ -950,7 +938,7 @@ def test_filters_forwarded(self, update): def test_filters_game(self, update): assert not Filters.game(update) - update.message.game = 'test' + update.message.game = "test" assert Filters.game(update) def test_entities_filter(self, update, message_entity): @@ -961,7 +949,7 @@ def test_entities_filter(self, update, message_entity): assert not Filters.entity(MessageEntity.MENTION)(update) second = message_entity.to_dict() - second['type'] = 'bold' + second["type"] = "bold" second = MessageEntity.de_json(second, None) update.message.entities = [message_entity, second] assert Filters.entity(message_entity.type)(update) @@ -975,34 +963,14 @@ def test_caption_entities_filter(self, update, message_entity): assert not Filters.caption_entity(MessageEntity.MENTION)(update) second = message_entity.to_dict() - second['type'] = 'bold' + second["type"] = "bold" second = MessageEntity.de_json(second, None) update.message.caption_entities = [message_entity, second] assert Filters.caption_entity(message_entity.type)(update) assert not Filters.entity(message_entity.type)(update) - def test_private_filter(self, update): - assert Filters.private(update) - update.message.chat.type = 'group' - assert not Filters.private(update) - - def test_private_filter_deprecation(self, update): - with pytest.warns(TelegramDeprecationWarning): - Filters.private(update) - - def test_group_filter(self, update): - assert not Filters.group(update) - update.message.chat.type = 'group' - assert Filters.group(update) - update.message.chat.type = 'supergroup' - assert Filters.group(update) - - def test_group_filter_deprecation(self, update): - with pytest.warns(TelegramDeprecationWarning): - Filters.group(update) - @pytest.mark.parametrize( - ('chat_type, results'), + ("chat_type, results"), [ (None, (False, False, False, False, False, False)), (Chat.PRIVATE, (True, True, False, False, False, False)), @@ -1021,8 +989,8 @@ def test_filters_chat_types(self, update, chat_type, results): assert Filters.chat_type.channel(update) is results[5] def test_filters_user_init(self): - with pytest.raises(RuntimeError, match='in conjunction with'): - Filters.user(user_id=1, username='user') + with pytest.raises(RuntimeError, match="in conjunction with"): + Filters.user(user_id=1, username="user") def test_filters_user_allow_empty(self, update): assert not Filters.user()(update) @@ -1039,15 +1007,15 @@ def test_filters_user_id(self, update): assert not Filters.user(user_id=[3, 4])(update) def test_filters_username(self, update): - assert not Filters.user(username='user')(update) - assert not Filters.user(username='Testuser')(update) - update.message.from_user.username = 'user@' - assert Filters.user(username='@user@')(update) - assert Filters.user(username='user@')(update) - assert Filters.user(username=['user1', 'user@', 'user2'])(update) - assert not Filters.user(username=['@username', '@user_2'])(update) + assert not Filters.user(username="user")(update) + assert not Filters.user(username="Testuser")(update) + update.message.from_user.username = "user@" + assert Filters.user(username="@user@")(update) + assert Filters.user(username="user@")(update) + assert Filters.user(username=["user1", "user@", "user2"])(update) + assert not Filters.user(username=["@username", "@user_2"])(update) update.message.from_user = None - assert not Filters.user(username=['@username', '@user_2'])(update) + assert not Filters.user(username=["@username", "@user_2"])(update) def test_filters_user_change_id(self, update): f = Filters.user(user_id=1) @@ -1060,37 +1028,37 @@ def test_filters_user_change_id(self, update): assert f.user_ids == {2} assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.usernames = 'user' + with pytest.raises(RuntimeError, match="username in conjunction"): + f.usernames = "user" def test_filters_user_change_username(self, update): - f = Filters.user(username='user') - update.message.from_user.username = 'user' + f = Filters.user(username="user") + update.message.from_user.username = "user" assert f(update) - update.message.from_user.username = 'User' + update.message.from_user.username = "User" assert not f(update) - f.usernames = 'User' + f.usernames = "User" assert f(update) - with pytest.raises(RuntimeError, match='user_id in conjunction'): + with pytest.raises(RuntimeError, match="user_id in conjunction"): f.user_ids = 1 def test_filters_user_add_user_by_name(self, update): - users = ['user_a', 'user_b', 'user_c'] + users = ["user_a", "user_b", "user_c"] f = Filters.user() for user in users: update.message.from_user.username = user assert not f(update) - f.add_usernames('user_a') - f.add_usernames(['user_b', 'user_c']) + f.add_usernames("user_a") + f.add_usernames(["user_b", "user_c"]) for user in users: update.message.from_user.username = user assert f(update) - with pytest.raises(RuntimeError, match='user_id in conjunction'): + with pytest.raises(RuntimeError, match="user_id in conjunction"): f.add_user_ids(1) def test_filters_user_add_user_by_id(self, update): @@ -1108,22 +1076,22 @@ def test_filters_user_add_user_by_id(self, update): update.message.from_user.username = user assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.add_usernames('user') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.add_usernames("user") def test_filters_user_remove_user_by_name(self, update): - users = ['user_a', 'user_b', 'user_c'] + users = ["user_a", "user_b", "user_c"] f = Filters.user(username=users) - with pytest.raises(RuntimeError, match='user_id in conjunction'): + with pytest.raises(RuntimeError, match="user_id in conjunction"): f.remove_user_ids(1) for user in users: update.message.from_user.username = user assert f(update) - f.remove_usernames('user_a') - f.remove_usernames(['user_b', 'user_c']) + f.remove_usernames("user_a") + f.remove_usernames(["user_b", "user_c"]) for user in users: update.message.from_user.username = user @@ -1133,8 +1101,8 @@ def test_filters_user_remove_user_by_id(self, update): users = [1, 2, 3] f = Filters.user(user_id=users) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.remove_usernames('user') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.remove_usernames("user") for user in users: update.message.from_user.id = user @@ -1149,23 +1117,23 @@ def test_filters_user_remove_user_by_id(self, update): def test_filters_user_repr(self): f = Filters.user([1, 2]) - assert str(f) == 'Filters.user(1, 2)' + assert str(f) == "Filters.user(1, 2)" f.remove_user_ids(1) f.remove_user_ids(2) - assert str(f) == 'Filters.user()' - f.add_usernames('@foobar') - assert str(f) == 'Filters.user(foobar)' - f.add_usernames('@barfoo') - assert str(f).startswith('Filters.user(') + assert str(f) == "Filters.user()" + f.add_usernames("@foobar") + assert str(f) == "Filters.user(foobar)" + f.add_usernames("@barfoo") + assert str(f).startswith("Filters.user(") # we don't know th exact order - assert 'barfoo' in str(f) and 'foobar' in str(f) + assert "barfoo" in str(f) and "foobar" in str(f) - with pytest.raises(RuntimeError, match='Cannot set name'): - f.name = 'foo' + with pytest.raises(RuntimeError, match="Cannot set name"): + f.name = "foo" def test_filters_chat_init(self): - with pytest.raises(RuntimeError, match='in conjunction with'): - Filters.chat(chat_id=1, username='chat') + with pytest.raises(RuntimeError, match="in conjunction with"): + Filters.chat(chat_id=1, username="chat") def test_filters_chat_allow_empty(self, update): assert not Filters.chat()(update) @@ -1182,15 +1150,15 @@ def test_filters_chat_id(self, update): assert not Filters.chat(chat_id=[3, 4])(update) def test_filters_chat_username(self, update): - assert not Filters.chat(username='chat')(update) - assert not Filters.chat(username='Testchat')(update) - update.message.chat.username = 'chat@' - assert Filters.chat(username='@chat@')(update) - assert Filters.chat(username='chat@')(update) - assert Filters.chat(username=['chat1', 'chat@', 'chat2'])(update) - assert not Filters.chat(username=['@username', '@chat_2'])(update) + assert not Filters.chat(username="chat")(update) + assert not Filters.chat(username="Testchat")(update) + update.message.chat.username = "chat@" + assert Filters.chat(username="@chat@")(update) + assert Filters.chat(username="chat@")(update) + assert Filters.chat(username=["chat1", "chat@", "chat2"])(update) + assert not Filters.chat(username=["@username", "@chat_2"])(update) update.message.chat = None - assert not Filters.chat(username=['@username', '@chat_2'])(update) + assert not Filters.chat(username=["@username", "@chat_2"])(update) def test_filters_chat_change_id(self, update): f = Filters.chat(chat_id=1) @@ -1203,37 +1171,37 @@ def test_filters_chat_change_id(self, update): assert f.chat_ids == {2} assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.usernames = 'chat' + with pytest.raises(RuntimeError, match="username in conjunction"): + f.usernames = "chat" def test_filters_chat_change_username(self, update): - f = Filters.chat(username='chat') - update.message.chat.username = 'chat' + f = Filters.chat(username="chat") + update.message.chat.username = "chat" assert f(update) - update.message.chat.username = 'User' + update.message.chat.username = "User" assert not f(update) - f.usernames = 'User' + f.usernames = "User" assert f(update) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.chat_ids = 1 def test_filters_chat_add_chat_by_name(self, update): - chats = ['chat_a', 'chat_b', 'chat_c'] + chats = ["chat_a", "chat_b", "chat_c"] f = Filters.chat() for chat in chats: update.message.chat.username = chat assert not f(update) - f.add_usernames('chat_a') - f.add_usernames(['chat_b', 'chat_c']) + f.add_usernames("chat_a") + f.add_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.chat.username = chat assert f(update) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.add_chat_ids(1) def test_filters_chat_add_chat_by_id(self, update): @@ -1251,22 +1219,22 @@ def test_filters_chat_add_chat_by_id(self, update): update.message.chat.username = chat assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.add_usernames('chat') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.add_usernames("chat") def test_filters_chat_remove_chat_by_name(self, update): - chats = ['chat_a', 'chat_b', 'chat_c'] + chats = ["chat_a", "chat_b", "chat_c"] f = Filters.chat(username=chats) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.remove_chat_ids(1) for chat in chats: update.message.chat.username = chat assert f(update) - f.remove_usernames('chat_a') - f.remove_usernames(['chat_b', 'chat_c']) + f.remove_usernames("chat_a") + f.remove_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.chat.username = chat @@ -1276,8 +1244,8 @@ def test_filters_chat_remove_chat_by_id(self, update): chats = [1, 2, 3] f = Filters.chat(chat_id=chats) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.remove_usernames('chat') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.remove_usernames("chat") for chat in chats: update.message.chat.id = chat @@ -1292,23 +1260,23 @@ def test_filters_chat_remove_chat_by_id(self, update): def test_filters_chat_repr(self): f = Filters.chat([1, 2]) - assert str(f) == 'Filters.chat(1, 2)' + assert str(f) == "Filters.chat(1, 2)" f.remove_chat_ids(1) f.remove_chat_ids(2) - assert str(f) == 'Filters.chat()' - f.add_usernames('@foobar') - assert str(f) == 'Filters.chat(foobar)' - f.add_usernames('@barfoo') - assert str(f).startswith('Filters.chat(') + assert str(f) == "Filters.chat()" + f.add_usernames("@foobar") + assert str(f) == "Filters.chat(foobar)" + f.add_usernames("@barfoo") + assert str(f).startswith("Filters.chat(") # we don't know th exact order - assert 'barfoo' in str(f) and 'foobar' in str(f) + assert "barfoo" in str(f) and "foobar" in str(f) - with pytest.raises(RuntimeError, match='Cannot set name'): - f.name = 'foo' + with pytest.raises(RuntimeError, match="Cannot set name"): + f.name = "foo" def test_filters_forwarded_from_init(self): - with pytest.raises(RuntimeError, match='in conjunction with'): - Filters.forwarded_from(chat_id=1, username='chat') + with pytest.raises(RuntimeError, match="in conjunction with"): + Filters.forwarded_from(chat_id=1, username="chat") def test_filters_forwarded_from_allow_empty(self, update): assert not Filters.forwarded_from()(update) @@ -1336,26 +1304,26 @@ def test_filters_forwarded_from_id(self, update): def test_filters_forwarded_from_username(self, update): # For User username - assert not Filters.forwarded_from(username='chat')(update) - assert not Filters.forwarded_from(username='Testchat')(update) - update.message.forward_from.username = 'chat@' - assert Filters.forwarded_from(username='@chat@')(update) - assert Filters.forwarded_from(username='chat@')(update) - assert Filters.forwarded_from(username=['chat1', 'chat@', 'chat2'])(update) - assert not Filters.forwarded_from(username=['@username', '@chat_2'])(update) + assert not Filters.forwarded_from(username="chat")(update) + assert not Filters.forwarded_from(username="Testchat")(update) + update.message.forward_from.username = "chat@" + assert Filters.forwarded_from(username="@chat@")(update) + assert Filters.forwarded_from(username="chat@")(update) + assert Filters.forwarded_from(username=["chat1", "chat@", "chat2"])(update) + assert not Filters.forwarded_from(username=["@username", "@chat_2"])(update) update.message.forward_from = None - assert not Filters.forwarded_from(username=['@username', '@chat_2'])(update) + assert not Filters.forwarded_from(username=["@username", "@chat_2"])(update) # For Chat username - assert not Filters.forwarded_from(username='chat')(update) - assert not Filters.forwarded_from(username='Testchat')(update) - update.message.forward_from_chat.username = 'chat@' - assert Filters.forwarded_from(username='@chat@')(update) - assert Filters.forwarded_from(username='chat@')(update) - assert Filters.forwarded_from(username=['chat1', 'chat@', 'chat2'])(update) - assert not Filters.forwarded_from(username=['@username', '@chat_2'])(update) + assert not Filters.forwarded_from(username="chat")(update) + assert not Filters.forwarded_from(username="Testchat")(update) + update.message.forward_from_chat.username = "chat@" + assert Filters.forwarded_from(username="@chat@")(update) + assert Filters.forwarded_from(username="chat@")(update) + assert Filters.forwarded_from(username=["chat1", "chat@", "chat2"])(update) + assert not Filters.forwarded_from(username=["@username", "@chat_2"])(update) update.message.forward_from_chat = None - assert not Filters.forwarded_from(username=['@username', '@chat_2'])(update) + assert not Filters.forwarded_from(username=["@username", "@chat_2"])(update) def test_filters_forwarded_from_change_id(self, update): f = Filters.forwarded_from(chat_id=1) @@ -1381,34 +1349,34 @@ def test_filters_forwarded_from_change_id(self, update): assert f.chat_ids == {2} assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.usernames = 'chat' + with pytest.raises(RuntimeError, match="username in conjunction"): + f.usernames = "chat" def test_filters_forwarded_from_change_username(self, update): # For User usernames - f = Filters.forwarded_from(username='chat') - update.message.forward_from.username = 'chat' + f = Filters.forwarded_from(username="chat") + update.message.forward_from.username = "chat" assert f(update) - update.message.forward_from.username = 'User' + update.message.forward_from.username = "User" assert not f(update) - f.usernames = 'User' + f.usernames = "User" assert f(update) # For Chat usernames update.message.forward_from = None - f = Filters.forwarded_from(username='chat') - update.message.forward_from_chat.username = 'chat' + f = Filters.forwarded_from(username="chat") + update.message.forward_from_chat.username = "chat" assert f(update) - update.message.forward_from_chat.username = 'User' + update.message.forward_from_chat.username = "User" assert not f(update) - f.usernames = 'User' + f.usernames = "User" assert f(update) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.chat_ids = 1 def test_filters_forwarded_from_add_chat_by_name(self, update): - chats = ['chat_a', 'chat_b', 'chat_c'] + chats = ["chat_a", "chat_b", "chat_c"] f = Filters.forwarded_from() # For User usernames @@ -1416,8 +1384,8 @@ def test_filters_forwarded_from_add_chat_by_name(self, update): update.message.forward_from.username = chat assert not f(update) - f.add_usernames('chat_a') - f.add_usernames(['chat_b', 'chat_c']) + f.add_usernames("chat_a") + f.add_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.forward_from.username = chat @@ -1430,14 +1398,14 @@ def test_filters_forwarded_from_add_chat_by_name(self, update): update.message.forward_from_chat.username = chat assert not f(update) - f.add_usernames('chat_a') - f.add_usernames(['chat_b', 'chat_c']) + f.add_usernames("chat_a") + f.add_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.forward_from_chat.username = chat assert f(update) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.add_chat_ids(1) def test_filters_forwarded_from_add_chat_by_id(self, update): @@ -1470,14 +1438,14 @@ def test_filters_forwarded_from_add_chat_by_id(self, update): update.message.forward_from_chat.username = chat assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.add_usernames('chat') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.add_usernames("chat") def test_filters_forwarded_from_remove_chat_by_name(self, update): - chats = ['chat_a', 'chat_b', 'chat_c'] + chats = ["chat_a", "chat_b", "chat_c"] f = Filters.forwarded_from(username=chats) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.remove_chat_ids(1) # For User usernames @@ -1485,8 +1453,8 @@ def test_filters_forwarded_from_remove_chat_by_name(self, update): update.message.forward_from.username = chat assert f(update) - f.remove_usernames('chat_a') - f.remove_usernames(['chat_b', 'chat_c']) + f.remove_usernames("chat_a") + f.remove_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.forward_from.username = chat @@ -1499,8 +1467,8 @@ def test_filters_forwarded_from_remove_chat_by_name(self, update): update.message.forward_from_chat.username = chat assert f(update) - f.remove_usernames('chat_a') - f.remove_usernames(['chat_b', 'chat_c']) + f.remove_usernames("chat_a") + f.remove_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.forward_from_chat.username = chat @@ -1510,8 +1478,8 @@ def test_filters_forwarded_from_remove_chat_by_id(self, update): chats = [1, 2, 3] f = Filters.forwarded_from(chat_id=chats) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.remove_usernames('chat') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.remove_usernames("chat") # For User ids for chat in chats: @@ -1541,23 +1509,23 @@ def test_filters_forwarded_from_remove_chat_by_id(self, update): def test_filters_forwarded_from_repr(self): f = Filters.forwarded_from([1, 2]) - assert str(f) == 'Filters.forwarded_from(1, 2)' + assert str(f) == "Filters.forwarded_from(1, 2)" f.remove_chat_ids(1) f.remove_chat_ids(2) - assert str(f) == 'Filters.forwarded_from()' - f.add_usernames('@foobar') - assert str(f) == 'Filters.forwarded_from(foobar)' - f.add_usernames('@barfoo') - assert str(f).startswith('Filters.forwarded_from(') + assert str(f) == "Filters.forwarded_from()" + f.add_usernames("@foobar") + assert str(f) == "Filters.forwarded_from(foobar)" + f.add_usernames("@barfoo") + assert str(f).startswith("Filters.forwarded_from(") # we don't know the exact order - assert 'barfoo' in str(f) and 'foobar' in str(f) + assert "barfoo" in str(f) and "foobar" in str(f) - with pytest.raises(RuntimeError, match='Cannot set name'): - f.name = 'foo' + with pytest.raises(RuntimeError, match="Cannot set name"): + f.name = "foo" def test_filters_sender_chat_init(self): - with pytest.raises(RuntimeError, match='in conjunction with'): - Filters.sender_chat(chat_id=1, username='chat') + with pytest.raises(RuntimeError, match="in conjunction with"): + Filters.sender_chat(chat_id=1, username="chat") def test_filters_sender_chat_allow_empty(self, update): assert not Filters.sender_chat()(update) @@ -1574,15 +1542,15 @@ def test_filters_sender_chat_id(self, update): assert not Filters.sender_chat(chat_id=[3, 4])(update) def test_filters_sender_chat_username(self, update): - assert not Filters.sender_chat(username='chat')(update) - assert not Filters.sender_chat(username='Testchat')(update) - update.message.sender_chat.username = 'chat@' - assert Filters.sender_chat(username='@chat@')(update) - assert Filters.sender_chat(username='chat@')(update) - assert Filters.sender_chat(username=['chat1', 'chat@', 'chat2'])(update) - assert not Filters.sender_chat(username=['@username', '@chat_2'])(update) + assert not Filters.sender_chat(username="chat")(update) + assert not Filters.sender_chat(username="Testchat")(update) + update.message.sender_chat.username = "chat@" + assert Filters.sender_chat(username="@chat@")(update) + assert Filters.sender_chat(username="chat@")(update) + assert Filters.sender_chat(username=["chat1", "chat@", "chat2"])(update) + assert not Filters.sender_chat(username=["@username", "@chat_2"])(update) update.message.sender_chat = None - assert not Filters.sender_chat(username=['@username', '@chat_2'])(update) + assert not Filters.sender_chat(username=["@username", "@chat_2"])(update) def test_filters_sender_chat_change_id(self, update): f = Filters.sender_chat(chat_id=1) @@ -1595,37 +1563,37 @@ def test_filters_sender_chat_change_id(self, update): assert f.chat_ids == {2} assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.usernames = 'chat' + with pytest.raises(RuntimeError, match="username in conjunction"): + f.usernames = "chat" def test_filters_sender_chat_change_username(self, update): - f = Filters.sender_chat(username='chat') - update.message.sender_chat.username = 'chat' + f = Filters.sender_chat(username="chat") + update.message.sender_chat.username = "chat" assert f(update) - update.message.sender_chat.username = 'User' + update.message.sender_chat.username = "User" assert not f(update) - f.usernames = 'User' + f.usernames = "User" assert f(update) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.chat_ids = 1 def test_filters_sender_chat_add_sender_chat_by_name(self, update): - chats = ['chat_a', 'chat_b', 'chat_c'] + chats = ["chat_a", "chat_b", "chat_c"] f = Filters.sender_chat() for chat in chats: update.message.sender_chat.username = chat assert not f(update) - f.add_usernames('chat_a') - f.add_usernames(['chat_b', 'chat_c']) + f.add_usernames("chat_a") + f.add_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.sender_chat.username = chat assert f(update) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.add_chat_ids(1) def test_filters_sender_chat_add_sender_chat_by_id(self, update): @@ -1643,22 +1611,22 @@ def test_filters_sender_chat_add_sender_chat_by_id(self, update): update.message.sender_chat.username = chat assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.add_usernames('chat') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.add_usernames("chat") def test_filters_sender_chat_remove_sender_chat_by_name(self, update): - chats = ['chat_a', 'chat_b', 'chat_c'] + chats = ["chat_a", "chat_b", "chat_c"] f = Filters.sender_chat(username=chats) - with pytest.raises(RuntimeError, match='chat_id in conjunction'): + with pytest.raises(RuntimeError, match="chat_id in conjunction"): f.remove_chat_ids(1) for chat in chats: update.message.sender_chat.username = chat assert f(update) - f.remove_usernames('chat_a') - f.remove_usernames(['chat_b', 'chat_c']) + f.remove_usernames("chat_a") + f.remove_usernames(["chat_b", "chat_c"]) for chat in chats: update.message.sender_chat.username = chat @@ -1668,8 +1636,8 @@ def test_filters_sender_chat_remove_sender_chat_by_id(self, update): chats = [1, 2, 3] f = Filters.sender_chat(chat_id=chats) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.remove_usernames('chat') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.remove_usernames("chat") for chat in chats: update.message.sender_chat.id = chat @@ -1684,19 +1652,19 @@ def test_filters_sender_chat_remove_sender_chat_by_id(self, update): def test_filters_sender_chat_repr(self): f = Filters.sender_chat([1, 2]) - assert str(f) == 'Filters.sender_chat(1, 2)' + assert str(f) == "Filters.sender_chat(1, 2)" f.remove_chat_ids(1) f.remove_chat_ids(2) - assert str(f) == 'Filters.sender_chat()' - f.add_usernames('@foobar') - assert str(f) == 'Filters.sender_chat(foobar)' - f.add_usernames('@barfoo') - assert str(f).startswith('Filters.sender_chat(') + assert str(f) == "Filters.sender_chat()" + f.add_usernames("@foobar") + assert str(f) == "Filters.sender_chat(foobar)" + f.add_usernames("@barfoo") + assert str(f).startswith("Filters.sender_chat(") # we don't know th exact order - assert 'barfoo' in str(f) and 'foobar' in str(f) + assert "barfoo" in str(f) and "foobar" in str(f) - with pytest.raises(RuntimeError, match='Cannot set name'): - f.name = 'foo' + with pytest.raises(RuntimeError, match="Cannot set name"): + f.name = "foo" def test_filters_sender_chat_super_group(self, update): update.message.sender_chat.type = Chat.PRIVATE @@ -1720,32 +1688,32 @@ def test_filters_sender_chat_channel(self, update): def test_filters_invoice(self, update): assert not Filters.invoice(update) - update.message.invoice = 'test' + update.message.invoice = "test" assert Filters.invoice(update) def test_filters_successful_payment(self, update): assert not Filters.successful_payment(update) - update.message.successful_payment = 'test' + update.message.successful_payment = "test" assert Filters.successful_payment(update) def test_filters_passport_data(self, update): assert not Filters.passport_data(update) - update.message.passport_data = 'test' + update.message.passport_data = "test" assert Filters.passport_data(update) def test_filters_poll(self, update): assert not Filters.poll(update) - update.message.poll = 'test' + update.message.poll = "test" assert Filters.poll(update) - @pytest.mark.parametrize('emoji', Dice.ALL_EMOJI) + @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI) def test_filters_dice(self, update, emoji): update.message.dice = Dice(4, emoji) assert Filters.dice(update) update.message.dice = None assert not Filters.dice(update) - @pytest.mark.parametrize('emoji', Dice.ALL_EMOJI) + @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI) def test_filters_dice_list(self, update, emoji): update.message.dice = None assert not Filters.dice(5)(update) @@ -1757,42 +1725,42 @@ def test_filters_dice_list(self, update, emoji): assert not Filters.dice([2, 3])(update) def test_filters_dice_type(self, update): - update.message.dice = Dice(5, '🎲') + update.message.dice = Dice(5, "🎲") assert Filters.dice.dice(update) assert Filters.dice.dice([4, 5])(update) assert not Filters.dice.darts(update) assert not Filters.dice.basketball(update) assert not Filters.dice.dice([6])(update) - update.message.dice = Dice(5, '🎯') + update.message.dice = Dice(5, "🎯") assert Filters.dice.darts(update) assert Filters.dice.darts([4, 5])(update) assert not Filters.dice.dice(update) assert not Filters.dice.basketball(update) assert not Filters.dice.darts([6])(update) - update.message.dice = Dice(5, 'πŸ€') + update.message.dice = Dice(5, "πŸ€") assert Filters.dice.basketball(update) assert Filters.dice.basketball([4, 5])(update) assert not Filters.dice.dice(update) assert not Filters.dice.darts(update) assert not Filters.dice.basketball([4])(update) - update.message.dice = Dice(5, '⚽') + update.message.dice = Dice(5, "⚽") assert Filters.dice.football(update) assert Filters.dice.football([4, 5])(update) assert not Filters.dice.dice(update) assert not Filters.dice.darts(update) assert not Filters.dice.football([4])(update) - update.message.dice = Dice(5, '🎰') + update.message.dice = Dice(5, "🎰") assert Filters.dice.slot_machine(update) assert Filters.dice.slot_machine([4, 5])(update) assert not Filters.dice.dice(update) assert not Filters.dice.darts(update) assert not Filters.dice.slot_machine([4])(update) - update.message.dice = Dice(5, '🎳') + update.message.dice = Dice(5, "🎳") assert Filters.dice.bowling(update) assert Filters.dice.bowling([4, 5])(update) assert not Filters.dice.dice(update) @@ -1800,42 +1768,42 @@ def test_filters_dice_type(self, update): assert not Filters.dice.bowling([4])(update) def test_language_filter_single(self, update): - update.message.from_user.language_code = 'en_US' - assert (Filters.language('en_US'))(update) - assert (Filters.language('en'))(update) - assert not (Filters.language('en_GB'))(update) - assert not (Filters.language('da'))(update) - update.message.from_user.language_code = 'da' - assert not (Filters.language('en_US'))(update) - assert not (Filters.language('en'))(update) - assert not (Filters.language('en_GB'))(update) - assert (Filters.language('da'))(update) + update.message.from_user.language_code = "en_US" + assert (Filters.language("en_US"))(update) + assert (Filters.language("en"))(update) + assert not (Filters.language("en_GB"))(update) + assert not (Filters.language("da"))(update) + update.message.from_user.language_code = "da" + assert not (Filters.language("en_US"))(update) + assert not (Filters.language("en"))(update) + assert not (Filters.language("en_GB"))(update) + assert (Filters.language("da"))(update) def test_language_filter_multiple(self, update): - f = Filters.language(['en_US', 'da']) - update.message.from_user.language_code = 'en_US' + f = Filters.language(["en_US", "da"]) + update.message.from_user.language_code = "en_US" assert f(update) - update.message.from_user.language_code = 'en_GB' + update.message.from_user.language_code = "en_GB" assert not f(update) - update.message.from_user.language_code = 'da' + update.message.from_user.language_code = "da" assert f(update) def test_and_filters(self, update): - update.message.text = 'test' + update.message.text = "test" update.message.forward_date = datetime.datetime.utcnow() assert (Filters.text & Filters.forwarded)(update) - update.message.text = '/test' + update.message.text = "/test" assert (Filters.text & Filters.forwarded)(update) - update.message.text = 'test' + update.message.text = "test" update.message.forward_date = None assert not (Filters.text & Filters.forwarded)(update) - update.message.text = 'test' + update.message.text = "test" update.message.forward_date = datetime.datetime.utcnow() - assert (Filters.text & Filters.forwarded & Filters.private)(update) + assert (Filters.text & Filters.forwarded & Filters.chat_type.private)(update) def test_or_filters(self, update): - update.message.text = 'test' + update.message.text = "test" assert (Filters.text | Filters.status_update)(update) update.message.group_chat_created = True assert (Filters.text | Filters.status_update)(update) @@ -1845,7 +1813,7 @@ def test_or_filters(self, update): assert not (Filters.text | Filters.status_update)(update) def test_and_or_filters(self, update): - update.message.text = 'test' + update.message.text = "test" update.message.forward_date = datetime.datetime.utcnow() assert (Filters.text & (Filters.status_update | Filters.forwarded))(update) update.message.forward_date = None @@ -1855,84 +1823,84 @@ def test_and_or_filters(self, update): assert ( str(Filters.text & (Filters.forwarded | Filters.entity(MessageEntity.MENTION))) - == '>' + == ">" ) def test_xor_filters(self, update): - update.message.text = 'test' + update.message.text = "test" update.effective_user.id = 123 assert not (Filters.text ^ Filters.user(123))(update) update.message.text = None update.effective_user.id = 1234 assert not (Filters.text ^ Filters.user(123))(update) - update.message.text = 'test' + update.message.text = "test" assert (Filters.text ^ Filters.user(123))(update) update.message.text = None update.effective_user.id = 123 assert (Filters.text ^ Filters.user(123))(update) def test_xor_filters_repr(self, update): - assert str(Filters.text ^ Filters.user(123)) == '' - with pytest.raises(RuntimeError, match='Cannot set name'): - (Filters.text ^ Filters.user(123)).name = 'foo' + assert str(Filters.text ^ Filters.user(123)) == "" + with pytest.raises(RuntimeError, match="Cannot set name"): + (Filters.text ^ Filters.user(123)).name = "foo" def test_and_xor_filters(self, update): - update.message.text = 'test' + update.message.text = "test" update.message.forward_date = datetime.datetime.utcnow() assert (Filters.forwarded & (Filters.text ^ Filters.user(123)))(update) update.message.text = None update.effective_user.id = 123 assert (Filters.forwarded & (Filters.text ^ Filters.user(123)))(update) - update.message.text = 'test' + update.message.text = "test" assert not (Filters.forwarded & (Filters.text ^ Filters.user(123)))(update) update.message.forward_date = None update.message.text = None update.effective_user.id = 123 assert not (Filters.forwarded & (Filters.text ^ Filters.user(123)))(update) - update.message.text = 'test' + update.message.text = "test" update.effective_user.id = 456 assert not (Filters.forwarded & (Filters.text ^ Filters.user(123)))(update) assert ( str(Filters.forwarded & (Filters.text ^ Filters.user(123))) - == '>' + == ">" ) def test_xor_regex_filters(self, update): SRE_TYPE = type(re.match("", "")) - update.message.text = 'test' + update.message.text = "test" update.message.forward_date = datetime.datetime.utcnow() - assert not (Filters.forwarded ^ Filters.regex('^test$'))(update) + assert not (Filters.forwarded ^ Filters.regex("^test$"))(update) update.message.forward_date = None - result = (Filters.forwarded ^ Filters.regex('^test$'))(update) + result = (Filters.forwarded ^ Filters.regex("^test$"))(update) assert result assert isinstance(result, dict) - matches = result['matches'] + matches = result["matches"] assert isinstance(matches, list) assert type(matches[0]) is SRE_TYPE update.message.forward_date = datetime.datetime.utcnow() update.message.text = None - assert (Filters.forwarded ^ Filters.regex('^test$'))(update) is True + assert (Filters.forwarded ^ Filters.regex("^test$"))(update) is True def test_inverted_filters(self, update): - update.message.text = '/test' + update.message.text = "/test" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] assert Filters.command(update) assert not (~Filters.command)(update) - update.message.text = 'test' + update.message.text = "test" update.message.entities = [] assert not Filters.command(update) assert (~Filters.command)(update) def test_inverted_filters_repr(self, update): - assert str(~Filters.text) == '' - with pytest.raises(RuntimeError, match='Cannot set name'): - (~Filters.text).name = 'foo' + assert str(~Filters.text) == "" + with pytest.raises(RuntimeError, match="Cannot set name"): + (~Filters.text).name = "foo" def test_inverted_and_filters(self, update): - update.message.text = '/test' + update.message.text = "/test" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] update.message.forward_date = 1 assert (Filters.forwarded & Filters.command)(update) @@ -1944,7 +1912,7 @@ def test_inverted_and_filters(self, update): assert (~Filters.forwarded & Filters.command)(update) assert not (Filters.forwarded & ~Filters.command)(update) assert (~(Filters.forwarded & Filters.command))(update) - update.message.text = 'test' + update.message.text = "test" update.message.entities = [] assert not (Filters.forwarded & Filters.command)(update) assert not (~Filters.forwarded & Filters.command)(update) @@ -1955,7 +1923,7 @@ def test_faulty_custom_filter(self, update): class _CustomFilter(BaseFilter): pass - with pytest.raises(TypeError, match='Can\'t instantiate abstract class _CustomFilter'): + with pytest.raises(TypeError, match="Can't instantiate abstract class _CustomFilter"): _CustomFilter() def test_custom_unnamed_filter(self, update, base_class): @@ -2006,7 +1974,7 @@ def test_update_type_edited_channel_post(self, update): assert Filters.update(update) def test_merged_short_circuit_and(self, update, base_class): - update.message.text = '/test' + update.message.text = "/test" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] class TestException(Exception): @@ -2021,16 +1989,16 @@ def filter(self, _): with pytest.raises(TestException): (Filters.command & raising_filter)(update) - update.message.text = 'test' + update.message.text = "test" update.message.entities = [] (Filters.command & raising_filter)(update) def test_merged_filters_repr(self, update): - with pytest.raises(RuntimeError, match='Cannot set name'): - (Filters.text & Filters.photo).name = 'foo' + with pytest.raises(RuntimeError, match="Cannot set name"): + (Filters.text & Filters.photo).name = "foo" def test_merged_short_circuit_or(self, update, base_class): - update.message.text = 'test' + update.message.text = "test" class TestException(Exception): pass @@ -2044,12 +2012,12 @@ def filter(self, _): with pytest.raises(TestException): (Filters.command | raising_filter)(update) - update.message.text = '/test' + update.message.text = "/test" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] (Filters.command | raising_filter)(update) def test_merged_data_merging_and(self, update, base_class): - update.message.text = '/test' + update.message.text = "/test" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)] class DataFilter(base_class): @@ -2059,21 +2027,21 @@ def __init__(self, data): self.data = data def filter(self, _): - return {'test': [self.data]} + return {"test": [self.data]} - result = (Filters.command & DataFilter('blah'))(update) - assert result['test'] == ['blah'] + result = (Filters.command & DataFilter("blah"))(update) + assert result["test"] == ["blah"] - result = (DataFilter('blah1') & DataFilter('blah2'))(update) - assert result['test'] == ['blah1', 'blah2'] + result = (DataFilter("blah1") & DataFilter("blah2"))(update) + assert result["test"] == ["blah1", "blah2"] - update.message.text = 'test' + update.message.text = "test" update.message.entities = [] - result = (Filters.command & DataFilter('blah'))(update) + result = (Filters.command & DataFilter("blah"))(update) assert not result def test_merged_data_merging_or(self, update, base_class): - update.message.text = '/test' + update.message.text = "/test" class DataFilter(base_class): data_filter = True @@ -2082,21 +2050,21 @@ def __init__(self, data): self.data = data def filter(self, _): - return {'test': [self.data]} + return {"test": [self.data]} - result = (Filters.command | DataFilter('blah'))(update) + result = (Filters.command | DataFilter("blah"))(update) assert result - result = (DataFilter('blah1') | DataFilter('blah2'))(update) - assert result['test'] == ['blah1'] + result = (DataFilter("blah1") | DataFilter("blah2"))(update) + assert result["test"] == ["blah1"] - update.message.text = 'test' - result = (Filters.command | DataFilter('blah'))(update) - assert result['test'] == ['blah'] + update.message.text = "test" + result = (Filters.command | DataFilter("blah"))(update) + assert result["test"] == ["blah"] def test_filters_via_bot_init(self): - with pytest.raises(RuntimeError, match='in conjunction with'): - Filters.via_bot(bot_id=1, username='bot') + with pytest.raises(RuntimeError, match="in conjunction with"): + Filters.via_bot(bot_id=1, username="bot") def test_filters_via_bot_allow_empty(self, update): assert not Filters.via_bot()(update) @@ -2113,15 +2081,15 @@ def test_filters_via_bot_id(self, update): assert not Filters.via_bot(bot_id=[3, 4])(update) def test_filters_via_bot_username(self, update): - assert not Filters.via_bot(username='bot')(update) - assert not Filters.via_bot(username='Testbot')(update) - update.message.via_bot.username = 'bot@' - assert Filters.via_bot(username='@bot@')(update) - assert Filters.via_bot(username='bot@')(update) - assert Filters.via_bot(username=['bot1', 'bot@', 'bot2'])(update) - assert not Filters.via_bot(username=['@username', '@bot_2'])(update) + assert not Filters.via_bot(username="bot")(update) + assert not Filters.via_bot(username="Testbot")(update) + update.message.via_bot.username = "bot@" + assert Filters.via_bot(username="@bot@")(update) + assert Filters.via_bot(username="bot@")(update) + assert Filters.via_bot(username=["bot1", "bot@", "bot2"])(update) + assert not Filters.via_bot(username=["@username", "@bot_2"])(update) update.message.via_bot = None - assert not Filters.user(username=['@username', '@bot_2'])(update) + assert not Filters.user(username=["@username", "@bot_2"])(update) def test_filters_via_bot_change_id(self, update): f = Filters.via_bot(bot_id=3) @@ -2134,37 +2102,37 @@ def test_filters_via_bot_change_id(self, update): assert f.bot_ids == {2} assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.usernames = 'user' + with pytest.raises(RuntimeError, match="username in conjunction"): + f.usernames = "user" def test_filters_via_bot_change_username(self, update): - f = Filters.via_bot(username='bot') - update.message.via_bot.username = 'bot' + f = Filters.via_bot(username="bot") + update.message.via_bot.username = "bot" assert f(update) - update.message.via_bot.username = 'Bot' + update.message.via_bot.username = "Bot" assert not f(update) - f.usernames = 'Bot' + f.usernames = "Bot" assert f(update) - with pytest.raises(RuntimeError, match='bot_id in conjunction'): + with pytest.raises(RuntimeError, match="bot_id in conjunction"): f.bot_ids = 1 def test_filters_via_bot_add_user_by_name(self, update): - users = ['bot_a', 'bot_b', 'bot_c'] + users = ["bot_a", "bot_b", "bot_c"] f = Filters.via_bot() for user in users: update.message.via_bot.username = user assert not f(update) - f.add_usernames('bot_a') - f.add_usernames(['bot_b', 'bot_c']) + f.add_usernames("bot_a") + f.add_usernames(["bot_b", "bot_c"]) for user in users: update.message.via_bot.username = user assert f(update) - with pytest.raises(RuntimeError, match='bot_id in conjunction'): + with pytest.raises(RuntimeError, match="bot_id in conjunction"): f.add_bot_ids(1) def test_filters_via_bot_add_user_by_id(self, update): @@ -2182,22 +2150,22 @@ def test_filters_via_bot_add_user_by_id(self, update): update.message.via_bot.username = user assert f(update) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.add_usernames('bot') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.add_usernames("bot") def test_filters_via_bot_remove_user_by_name(self, update): - users = ['bot_a', 'bot_b', 'bot_c'] + users = ["bot_a", "bot_b", "bot_c"] f = Filters.via_bot(username=users) - with pytest.raises(RuntimeError, match='bot_id in conjunction'): + with pytest.raises(RuntimeError, match="bot_id in conjunction"): f.remove_bot_ids(1) for user in users: update.message.via_bot.username = user assert f(update) - f.remove_usernames('bot_a') - f.remove_usernames(['bot_b', 'bot_c']) + f.remove_usernames("bot_a") + f.remove_usernames(["bot_b", "bot_c"]) for user in users: update.message.via_bot.username = user @@ -2207,8 +2175,8 @@ def test_filters_via_bot_remove_user_by_id(self, update): users = [1, 2, 3] f = Filters.via_bot(bot_id=users) - with pytest.raises(RuntimeError, match='username in conjunction'): - f.remove_usernames('bot') + with pytest.raises(RuntimeError, match="username in conjunction"): + f.remove_usernames("bot") for user in users: update.message.via_bot.id = user @@ -2223,19 +2191,19 @@ def test_filters_via_bot_remove_user_by_id(self, update): def test_filters_via_bot_repr(self): f = Filters.via_bot([1, 2]) - assert str(f) == 'Filters.via_bot(1, 2)' + assert str(f) == "Filters.via_bot(1, 2)" f.remove_bot_ids(1) f.remove_bot_ids(2) - assert str(f) == 'Filters.via_bot()' - f.add_usernames('@foobar') - assert str(f) == 'Filters.via_bot(foobar)' - f.add_usernames('@barfoo') - assert str(f).startswith('Filters.via_bot(') + assert str(f) == "Filters.via_bot()" + f.add_usernames("@foobar") + assert str(f) == "Filters.via_bot(foobar)" + f.add_usernames("@barfoo") + assert str(f).startswith("Filters.via_bot(") # we don't know th exact order - assert 'barfoo' in str(f) and 'foobar' in str(f) + assert "barfoo" in str(f) and "foobar" in str(f) - with pytest.raises(RuntimeError, match='Cannot set name'): - f.name = 'foo' + with pytest.raises(RuntimeError, match="Cannot set name"): + f.name = "foo" def test_filters_attachment(self, update): assert not Filters.attachment(update) @@ -2246,7 +2214,7 @@ def test_filters_attachment(self, update): Message( 0, datetime.datetime.utcnow(), - Chat(0, 'private'), + Chat(0, "private"), document=Document("str", "other_str"), ), ) diff --git a/tests/test_forcereply.py b/tests/test_forcereply.py index f5f09b26d44..df3ad5db2c4 100644 --- a/tests/test_forcereply.py +++ b/tests/test_forcereply.py @@ -23,10 +23,9 @@ from telegram import ForceReply, ReplyKeyboardRemove -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def force_reply(): return ForceReply( - TestForceReply.force_reply, TestForceReply.selective, TestForceReply.input_field_placeholder, ) @@ -35,21 +34,18 @@ def force_reply(): class TestForceReply: force_reply = True selective = True - input_field_placeholder = 'force replies can be annoying if not used properly' + input_field_placeholder = "force replies can be annoying if not used properly" - def test_slot_behaviour(self, force_reply, recwarn, mro_slots): + def test_slot_behaviour(self, force_reply, mro_slots): for attr in force_reply.__slots__: - assert getattr(force_reply, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not force_reply.__dict__, f"got missing slot(s): {force_reply.__dict__}" + assert getattr(force_reply, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(force_reply)) == len(set(mro_slots(force_reply))), "duplicate slot" - force_reply.custom, force_reply.force_reply = 'should give warning', self.force_reply - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @flaky(3, 1) def test_send_message_with_force_reply(self, bot, chat_id, force_reply): - message = bot.send_message(chat_id, 'text', reply_markup=force_reply) + message = bot.send_message(chat_id, "text", reply_markup=force_reply) - assert message.text == 'text' + assert message.text == "text" def test_expected(self, force_reply): assert force_reply.force_reply == self.force_reply @@ -60,21 +56,21 @@ def test_to_dict(self, force_reply): force_reply_dict = force_reply.to_dict() assert isinstance(force_reply_dict, dict) - assert force_reply_dict['force_reply'] == force_reply.force_reply - assert force_reply_dict['selective'] == force_reply.selective - assert force_reply_dict['input_field_placeholder'] == force_reply.input_field_placeholder + assert force_reply_dict["force_reply"] == force_reply.force_reply + assert force_reply_dict["selective"] == force_reply.selective + assert force_reply_dict["input_field_placeholder"] == force_reply.input_field_placeholder def test_equality(self): - a = ForceReply(True, False) - b = ForceReply(False, False) - c = ForceReply(True, True) + a = ForceReply(True, "test") + b = ForceReply(False, "pass") + c = ForceReply(True) d = ReplyKeyboardRemove() - assert a == b - assert hash(a) == hash(b) + assert a != b + assert hash(a) != hash(b) - assert a != c - assert hash(a) != hash(c) + assert a == c + assert hash(a) == hash(c) assert a != d assert hash(a) != hash(d) diff --git a/tests/test_game.py b/tests/test_game.py index 8207cd70855..db34b3700d5 100644 --- a/tests/test_game.py +++ b/tests/test_game.py @@ -22,7 +22,7 @@ from telegram import MessageEntity, Game, PhotoSize, Animation -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def game(): return Game( TestGame.title, @@ -35,29 +35,26 @@ def game(): class TestGame: - title = 'Python-telegram-bot Test Game' - description = 'description' - photo = [PhotoSize('Blah', 'ElseBlah', 640, 360, file_size=0)] + title = "Python-telegram-bot Test Game" + description = "description" + photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)] text = ( - b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467' - b'\\u200d\\U0001f467\\U0001f431http://google.com' - ).decode('unicode-escape') + b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" + b"\\u200d\\U0001f467\\U0001f431http://google.com" + ).decode("unicode-escape") text_entities = [MessageEntity(13, 17, MessageEntity.URL)] - animation = Animation('blah', 'unique_id', 320, 180, 1) + animation = Animation("blah", "unique_id", 320, 180, 1) - def test_slot_behaviour(self, game, recwarn, mro_slots): + def test_slot_behaviour(self, game, mro_slots): for attr in game.__slots__: - assert getattr(game, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not game.__dict__, f"got missing slot(s): {game.__dict__}" + assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(game)) == len(set(mro_slots(game))), "duplicate slot" - game.custom, game.title = 'should give warning', self.title - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json_required(self, bot): json_dict = { - 'title': self.title, - 'description': self.description, - 'photo': [self.photo[0].to_dict()], + "title": self.title, + "description": self.description, + "photo": [self.photo[0].to_dict()], } game = Game.de_json(json_dict, bot) @@ -67,12 +64,12 @@ def test_de_json_required(self, bot): def test_de_json_all(self, bot): json_dict = { - 'title': self.title, - 'description': self.description, - 'photo': [self.photo[0].to_dict()], - 'text': self.text, - 'text_entities': [self.text_entities[0].to_dict()], - 'animation': self.animation.to_dict(), + "title": self.title, + "description": self.description, + "photo": [self.photo[0].to_dict()], + "text": self.text, + "text_entities": [self.text_entities[0].to_dict()], + "animation": self.animation.to_dict(), } game = Game.de_json(json_dict, bot) @@ -87,42 +84,42 @@ def test_to_dict(self, game): game_dict = game.to_dict() assert isinstance(game_dict, dict) - assert game_dict['title'] == game.title - assert game_dict['description'] == game.description - assert game_dict['photo'] == [game.photo[0].to_dict()] - assert game_dict['text'] == game.text - assert game_dict['text_entities'] == [game.text_entities[0].to_dict()] - assert game_dict['animation'] == game.animation.to_dict() + assert game_dict["title"] == game.title + assert game_dict["description"] == game.description + assert game_dict["photo"] == [game.photo[0].to_dict()] + assert game_dict["text"] == game.text + assert game_dict["text_entities"] == [game.text_entities[0].to_dict()] + assert game_dict["animation"] == game.animation.to_dict() def test_parse_entity(self, game): entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) game.text_entities = [entity] - assert game.parse_text_entity(entity) == 'http://google.com' + assert game.parse_text_entity(entity) == "http://google.com" def test_parse_entities(self, game): entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1) game.text_entities = [entity_2, entity] - assert game.parse_text_entities(MessageEntity.URL) == {entity: 'http://google.com'} - assert game.parse_text_entities() == {entity: 'http://google.com', entity_2: 'h'} + assert game.parse_text_entities(MessageEntity.URL) == {entity: "http://google.com"} + assert game.parse_text_entities() == {entity: "http://google.com", entity_2: "h"} def test_equality(self): - a = Game('title', 'description', [PhotoSize('Blah', 'unique_id', 640, 360, file_size=0)]) + a = Game("title", "description", [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)]) b = Game( - 'title', - 'description', - [PhotoSize('Blah', 'unique_id', 640, 360, file_size=0)], - text='Here is a text', + "title", + "description", + [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)], + text="Here is a text", ) c = Game( - 'eltit', - 'description', - [PhotoSize('Blah', 'unique_id', 640, 360, file_size=0)], - animation=Animation('blah', 'unique_id', 320, 180, 1), + "eltit", + "description", + [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)], + animation=Animation("blah", "unique_id", 320, 180, 1), ) - d = Animation('blah', 'unique_id', 320, 180, 1) + d = Animation("blah", "unique_id", 320, 180, 1) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_gamehighscore.py b/tests/test_gamehighscore.py index 166e22cf617..5ce846e0106 100644 --- a/tests/test_gamehighscore.py +++ b/tests/test_gamehighscore.py @@ -22,7 +22,7 @@ from telegram import GameHighScore, User -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def game_highscore(): return GameHighScore( TestGameHighScore.position, TestGameHighScore.user, TestGameHighScore.score @@ -31,19 +31,16 @@ def game_highscore(): class TestGameHighScore: position = 12 - user = User(2, 'test user', False) + user = User(2, "test user", False) score = 42 - def test_slot_behaviour(self, game_highscore, recwarn, mro_slots): + def test_slot_behaviour(self, game_highscore, mro_slots): for attr in game_highscore.__slots__: - assert getattr(game_highscore, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not game_highscore.__dict__, f"got missing slot(s): {game_highscore.__dict__}" + assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(game_highscore)) == len(set(mro_slots(game_highscore))), "same slot" - game_highscore.custom, game_highscore.position = 'should give warning', self.position - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): - json_dict = {'position': self.position, 'user': self.user.to_dict(), 'score': self.score} + json_dict = {"position": self.position, "user": self.user.to_dict(), "score": self.score} highscore = GameHighScore.de_json(json_dict, bot) assert highscore.position == self.position @@ -54,16 +51,16 @@ def test_to_dict(self, game_highscore): game_highscore_dict = game_highscore.to_dict() assert isinstance(game_highscore_dict, dict) - assert game_highscore_dict['position'] == game_highscore.position - assert game_highscore_dict['user'] == game_highscore.user.to_dict() - assert game_highscore_dict['score'] == game_highscore.score + assert game_highscore_dict["position"] == game_highscore.position + assert game_highscore_dict["user"] == game_highscore.user.to_dict() + assert game_highscore_dict["score"] == game_highscore.score def test_equality(self): - a = GameHighScore(1, User(2, 'test user', False), 42) - b = GameHighScore(1, User(2, 'test user', False), 42) - c = GameHighScore(2, User(2, 'test user', False), 42) - d = GameHighScore(1, User(3, 'test user', False), 42) - e = User(3, 'test user', False) + a = GameHighScore(1, User(2, "test user", False), 42) + b = GameHighScore(1, User(2, "test user", False), 42) + c = GameHighScore(2, User(2, "test user", False), 42) + d = GameHighScore(1, User(3, "test user", False), 42) + e = User(3, "test user", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_handler.py b/tests/test_handler.py index b4a43c10ff2..11126fa22c1 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -17,13 +17,11 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -from sys import version_info as py_ver - from telegram.ext import Handler class TestHandler: - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): class SubclassHandler(Handler): __slots__ = () @@ -35,9 +33,5 @@ def check_update(self, update: object): inst = SubclassHandler() for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - assert '__dict__' not in Handler.__slots__ if py_ver < (3, 7) else True, 'dict in abc' - inst.custom = 'should not give warning' - assert len(recwarn) == 0, recwarn.list diff --git a/tests/test_helpers.py b/tests/test_helpers.py index b95588ab27f..4602b466f1f 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -63,17 +63,17 @@ Note that a fixture that just does this for every test that needs it is a nice idea, but for some reason makes test_updater.py hang indefinitely on GitHub Actions (at least when Hinrich tried that) """ -TEST_NO_PYTZ = env_var_2_bool(os.getenv('TEST_NO_PYTZ', False)) +TEST_NO_PYTZ = env_var_2_bool(os.getenv("TEST_NO_PYTZ", False)) if TEST_NO_PYTZ: orig_import = __import__ def import_mock(module_name, *args, **kwargs): - if module_name == 'pytz': - raise ModuleNotFoundError('We are testing without pytz here') + if module_name == "pytz": + raise ModuleNotFoundError("We are testing without pytz here") return orig_import(module_name, *args, **kwargs) - with mock.patch('builtins.__import__', side_effect=import_mock): + with mock.patch("builtins.__import__", side_effect=import_mock): reload(helpers) @@ -86,21 +86,21 @@ def test_helpers_utc(self): assert helpers.UTC is not helpers.DTM_UTC def test_escape_markdown(self): - test_str = '*bold*, _italic_, `code`, [text_link](http://github.com/)' - expected_str = r'\*bold\*, \_italic\_, \`code\`, \[text\_link](http://github.com/)' + test_str = "*bold*, _italic_, `code`, [text_link](http://github.com/)" + expected_str = r"\*bold\*, \_italic\_, \`code\`, \[text\_link](http://github.com/)" assert expected_str == helpers.escape_markdown(test_str) def test_escape_markdown_v2(self): - test_str = 'a_b*c[d]e (fg) h~I`>JK#L+MN -O=|p{qr}s.t! u' - expected_str = r'a\_b\*c\[d\]e \(fg\) h\~I\`\>JK\#L\+MN \-O\=\|p\{qr\}s\.t\! u' + test_str = "a_b*c[d]e (fg) h~I`>JK#L+MN -O=|p{qr}s.t! u" + expected_str = r"a\_b\*c\[d\]e \(fg\) h\~I\`\>JK\#L\+MN \-O\=\|p\{qr\}s\.t\! u" assert expected_str == helpers.escape_markdown(test_str, version=2) def test_escape_markdown_v2_monospaced(self): - test_str = r'mono/pre: `abc` \int (`\some \`stuff)' - expected_str = 'mono/pre: \\`abc\\` \\\\int (\\`\\\\some \\\\\\`stuff)' + test_str = r"mono/pre: `abc` \int (`\some \`stuff)" + expected_str = "mono/pre: \\`abc\\` \\\\int (\\`\\\\some \\\\\\`stuff)" assert expected_str == helpers.escape_markdown( test_str, version=2, entity_type=MessageEntity.PRE @@ -111,8 +111,8 @@ def test_escape_markdown_v2_monospaced(self): def test_escape_markdown_v2_text_link(self): - test_str = 'https://url.containing/funny)cha)\\ra\\)cter\\s' - expected_str = 'https://url.containing/funny\\)cha\\)\\\\ra\\\\\\)cter\\\\s' + test_str = "https://url.containing/funny)cha)\\ra\\)cter\\s" + expected_str = "https://url.containing/funny\\)cha\\)\\\\ra\\\\\\)cter\\\\s" assert expected_str == helpers.escape_markdown( test_str, version=2, entity_type=MessageEntity.TEXT_LINK @@ -120,7 +120,7 @@ def test_escape_markdown_v2_text_link(self): def test_markdown_invalid_version(self): with pytest.raises(ValueError): - helpers.escape_markdown('abc', version=-1) + helpers.escape_markdown("abc", version=-1) def test_to_float_timestamp_absolute_naive(self): """Conversion from timezone-naive datetime to timestamp. @@ -133,7 +133,7 @@ def test_to_float_timestamp_absolute_naive_no_pytz(self, monkeypatch): """Conversion from timezone-naive datetime to timestamp. Naive datetimes should be assumed to be in UTC. """ - monkeypatch.setattr(helpers, 'UTC', helpers.DTM_UTC) + monkeypatch.setattr(helpers, "UTC", helpers.DTM_UTC) datetime = dtm.datetime(2019, 11, 11, 0, 26, 16, 10 ** 5) assert helpers.to_float_timestamp(datetime) == 1573431976.1 @@ -153,11 +153,11 @@ def test_to_float_timestamp_absolute_no_reference(self): with pytest.raises(ValueError): helpers.to_float_timestamp(dtm.datetime(2019, 11, 11), reference_timestamp=123) - @pytest.mark.parametrize('time_spec', DELTA_TIME_SPECS, ids=str) + @pytest.mark.parametrize("time_spec", DELTA_TIME_SPECS, ids=str) def test_to_float_timestamp_delta(self, time_spec): """Conversion from a 'delta' time specification to timestamp""" reference_t = 0 - delta = time_spec.total_seconds() if hasattr(time_spec, 'total_seconds') else time_spec + delta = time_spec.total_seconds() if hasattr(time_spec, "total_seconds") else time_spec assert helpers.to_float_timestamp(time_spec, reference_t) == reference_t + delta def test_to_float_timestamp_time_of_day(self): @@ -186,7 +186,7 @@ def test_to_float_timestamp_time_of_day_timezone(self, timezone): ref_t + (-utc_offset.total_seconds() % (24 * 60 * 60)) ) - @pytest.mark.parametrize('time_spec', RELATIVE_TIME_SPECS, ids=str) + @pytest.mark.parametrize("time_spec", RELATIVE_TIME_SPECS, ids=str) def test_to_float_timestamp_default_reference(self, time_spec): """The reference timestamp for relative time specifications should default to now""" now = time.time() @@ -195,10 +195,10 @@ def test_to_float_timestamp_default_reference(self, time_spec): ) def test_to_float_timestamp_error(self): - with pytest.raises(TypeError, match='Defaults'): + with pytest.raises(TypeError, match="Defaults"): helpers.to_float_timestamp(Defaults()) - @pytest.mark.parametrize('time_spec', TIME_SPECS, ids=str) + @pytest.mark.parametrize("time_spec", TIME_SPECS, ids=str) def test_to_timestamp(self, time_spec): # delegate tests to `to_float_timestamp` assert helpers.to_timestamp(time_spec) == int(helpers.to_float_timestamp(time_spec)) @@ -227,7 +227,7 @@ def test_from_timestamp_aware(self, timezone): ) def test_create_deep_linked_url(self): - username = 'JamesTheMock' + username = "JamesTheMock" payload = "hello" expected = f"https://t.me/{username}?start={payload}" @@ -246,10 +246,10 @@ def test_create_deep_linked_url(self): assert expected == helpers.create_deep_linked_url(username, payload) with pytest.raises(ValueError): - helpers.create_deep_linked_url(username, 'text with spaces') + helpers.create_deep_linked_url(username, "text with spaces") with pytest.raises(ValueError): - helpers.create_deep_linked_url(username, '0' * 65) + helpers.create_deep_linked_url(username, "0" * 65) with pytest.raises(ValueError): helpers.create_deep_linked_url(None, None) @@ -267,26 +267,26 @@ def build_test_message(**kwargs): config.update(**kwargs) return Message(**config) - test_message = build_test_message(text='Test') - assert helpers.effective_message_type(test_message) == 'text' + test_message = build_test_message(text="Test") + assert helpers.effective_message_type(test_message) == "text" test_message.text = None test_message = build_test_message( - sticker=Sticker('sticker_id', 'unique_id', 50, 50, False) + sticker=Sticker("sticker_id", "unique_id", 50, 50, False) ) - assert helpers.effective_message_type(test_message) == 'sticker' + assert helpers.effective_message_type(test_message) == "sticker" test_message.sticker = None - test_message = build_test_message(new_chat_members=[User(55, 'new_user', False)]) - assert helpers.effective_message_type(test_message) == 'new_chat_members' + test_message = build_test_message(new_chat_members=[User(55, "new_user", False)]) + assert helpers.effective_message_type(test_message) == "new_chat_members" - test_message = build_test_message(left_chat_member=[User(55, 'new_user', False)]) - assert helpers.effective_message_type(test_message) == 'left_chat_member' + test_message = build_test_message(left_chat_member=[User(55, "new_user", False)]) + assert helpers.effective_message_type(test_message) == "left_chat_member" test_update = Update(1) - test_message = build_test_message(text='Test') + test_message = build_test_message(text="Test") test_update.message = test_message - assert helpers.effective_message_type(test_update) == 'text' + assert helpers.effective_message_type(test_update) == "text" empty_update = Update(2) assert helpers.effective_message_type(empty_update) is None @@ -294,28 +294,28 @@ def build_test_message(**kwargs): def test_mention_html(self): expected = 'the name' - assert expected == helpers.mention_html(1, 'the name') + assert expected == helpers.mention_html(1, "the name") def test_mention_markdown(self): - expected = '[the name](tg://user?id=1)' + expected = "[the name](tg://user?id=1)" - assert expected == helpers.mention_markdown(1, 'the name') + assert expected == helpers.mention_markdown(1, "the name") def test_mention_markdown_2(self): - expected = r'[the\_name](tg://user?id=1)' + expected = r"[the\_name](tg://user?id=1)" - assert expected == helpers.mention_markdown(1, 'the_name') + assert expected == helpers.mention_markdown(1, "the_name") @pytest.mark.parametrize( - 'string,expected', + "string,expected", [ - ('tests/data/game.gif', True), - ('tests/data', False), - (str(Path.cwd() / 'tests' / 'data' / 'game.gif'), True), - (str(Path.cwd() / 'tests' / 'data'), False), - (Path.cwd() / 'tests' / 'data' / 'game.gif', True), - (Path.cwd() / 'tests' / 'data', False), - ('https:/api.org/file/botTOKEN/document/file_3', False), + ("tests/data/game.gif", True), + ("tests/data", False), + (str(Path.cwd() / "tests" / "data" / "game.gif"), True), + (str(Path.cwd() / "tests" / "data"), False), + (Path.cwd() / "tests" / "data" / "game.gif", True), + (Path.cwd() / "tests" / "data", False), + ("https:/api.org/file/botTOKEN/document/file_3", False), (None, False), ], ) @@ -323,24 +323,24 @@ def test_is_local_file(self, string, expected): assert helpers.is_local_file(string) == expected @pytest.mark.parametrize( - 'string,expected', + "string,expected", [ - ('tests/data/game.gif', (Path.cwd() / 'tests' / 'data' / 'game.gif').as_uri()), - ('tests/data', 'tests/data'), - ('file://foobar', 'file://foobar'), + ("tests/data/game.gif", (Path.cwd() / "tests" / "data" / "game.gif").as_uri()), + ("tests/data", "tests/data"), + ("file://foobar", "file://foobar"), ( - str(Path.cwd() / 'tests' / 'data' / 'game.gif'), - (Path.cwd() / 'tests' / 'data' / 'game.gif').as_uri(), + str(Path.cwd() / "tests" / "data" / "game.gif"), + (Path.cwd() / "tests" / "data" / "game.gif").as_uri(), ), - (str(Path.cwd() / 'tests' / 'data'), str(Path.cwd() / 'tests' / 'data')), + (str(Path.cwd() / "tests" / "data"), str(Path.cwd() / "tests" / "data")), ( - Path.cwd() / 'tests' / 'data' / 'game.gif', - (Path.cwd() / 'tests' / 'data' / 'game.gif').as_uri(), + Path.cwd() / "tests" / "data" / "game.gif", + (Path.cwd() / "tests" / "data" / "game.gif").as_uri(), ), - (Path.cwd() / 'tests' / 'data', Path.cwd() / 'tests' / 'data'), + (Path.cwd() / "tests" / "data", Path.cwd() / "tests" / "data"), ( - 'https:/api.org/file/botTOKEN/document/file_3', - 'https:/api.org/file/botTOKEN/document/file_3', + "https:/api.org/file/botTOKEN/document/file_3", + "https:/api.org/file/botTOKEN/document/file_3", ), ], ) @@ -348,40 +348,40 @@ def test_parse_file_input_string(self, string, expected): assert helpers.parse_file_input(string) == expected def test_parse_file_input_file_like(self): - with open('tests/data/game.gif', 'rb') as file: + with open("tests/data/game.gif", "rb") as file: parsed = helpers.parse_file_input(file) assert isinstance(parsed, InputFile) assert not parsed.attach - assert parsed.filename == 'game.gif' + assert parsed.filename == "game.gif" - with open('tests/data/game.gif', 'rb') as file: - parsed = helpers.parse_file_input(file, attach=True, filename='test_file') + with open("tests/data/game.gif", "rb") as file: + parsed = helpers.parse_file_input(file, attach=True, filename="test_file") assert isinstance(parsed, InputFile) assert parsed.attach - assert parsed.filename == 'test_file' + assert parsed.filename == "test_file" def test_parse_file_input_bytes(self): - with open('tests/data/text_file.txt', 'rb') as file: + with open("tests/data/text_file.txt", "rb") as file: parsed = helpers.parse_file_input(file.read()) assert isinstance(parsed, InputFile) assert not parsed.attach - assert parsed.filename == 'application.octet-stream' + assert parsed.filename == "application.octet-stream" - with open('tests/data/text_file.txt', 'rb') as file: - parsed = helpers.parse_file_input(file.read(), attach=True, filename='test_file') + with open("tests/data/text_file.txt", "rb") as file: + parsed = helpers.parse_file_input(file.read(), attach=True, filename="test_file") assert isinstance(parsed, InputFile) assert parsed.attach - assert parsed.filename == 'test_file' + assert parsed.filename == "test_file" def test_parse_file_input_tg_object(self): - animation = Animation('file_id', 'unique_id', 1, 1, 1) - assert helpers.parse_file_input(animation, Animation) == 'file_id' + animation = Animation("file_id", "unique_id", 1, 1, 1) + assert helpers.parse_file_input(animation, Animation) == "file_id" assert helpers.parse_file_input(animation, MessageEntity) is animation - @pytest.mark.parametrize('obj', [{1: 2}, [1, 2], (1, 2)]) + @pytest.mark.parametrize("obj", [{1: 2}, [1, 2], (1, 2)]) def test_parse_file_input_other(self, obj): assert helpers.parse_file_input(obj) is obj diff --git a/tests/test_inlinekeyboardbutton.py b/tests/test_inlinekeyboardbutton.py index f60fced6d02..017db6b9558 100644 --- a/tests/test_inlinekeyboardbutton.py +++ b/tests/test_inlinekeyboardbutton.py @@ -22,7 +22,7 @@ from telegram import InlineKeyboardButton, LoginUrl -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_keyboard_button(): return InlineKeyboardButton( TestInlineKeyboardButton.text, @@ -37,23 +37,20 @@ def inline_keyboard_button(): class TestInlineKeyboardButton: - text = 'text' - url = 'url' - callback_data = 'callback data' - switch_inline_query = 'switch_inline_query' - switch_inline_query_current_chat = 'switch_inline_query_current_chat' - callback_game = 'callback_game' - pay = 'pay' + text = "text" + url = "url" + callback_data = "callback data" + switch_inline_query = "switch_inline_query" + switch_inline_query_current_chat = "switch_inline_query_current_chat" + callback_game = "callback_game" + pay = "pay" login_url = LoginUrl("http://google.com") - def test_slot_behaviour(self, inline_keyboard_button, recwarn, mro_slots): + def test_slot_behaviour(self, inline_keyboard_button, mro_slots): inst = inline_keyboard_button for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.text = 'should give warning', self.text - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_keyboard_button): assert inline_keyboard_button.text == self.text @@ -72,32 +69,32 @@ def test_to_dict(self, inline_keyboard_button): inline_keyboard_button_dict = inline_keyboard_button.to_dict() assert isinstance(inline_keyboard_button_dict, dict) - assert inline_keyboard_button_dict['text'] == inline_keyboard_button.text - assert inline_keyboard_button_dict['url'] == inline_keyboard_button.url - assert inline_keyboard_button_dict['callback_data'] == inline_keyboard_button.callback_data + assert inline_keyboard_button_dict["text"] == inline_keyboard_button.text + assert inline_keyboard_button_dict["url"] == inline_keyboard_button.url + assert inline_keyboard_button_dict["callback_data"] == inline_keyboard_button.callback_data assert ( - inline_keyboard_button_dict['switch_inline_query'] + inline_keyboard_button_dict["switch_inline_query"] == inline_keyboard_button.switch_inline_query ) assert ( - inline_keyboard_button_dict['switch_inline_query_current_chat'] + inline_keyboard_button_dict["switch_inline_query_current_chat"] == inline_keyboard_button.switch_inline_query_current_chat ) - assert inline_keyboard_button_dict['callback_game'] == inline_keyboard_button.callback_game - assert inline_keyboard_button_dict['pay'] == inline_keyboard_button.pay + assert inline_keyboard_button_dict["callback_game"] == inline_keyboard_button.callback_game + assert inline_keyboard_button_dict["pay"] == inline_keyboard_button.pay assert ( - inline_keyboard_button_dict['login_url'] == inline_keyboard_button.login_url.to_dict() + inline_keyboard_button_dict["login_url"] == inline_keyboard_button.login_url.to_dict() ) # NOQA: E127 def test_de_json(self, bot): json_dict = { - 'text': self.text, - 'url': self.url, - 'callback_data': self.callback_data, - 'switch_inline_query': self.switch_inline_query, - 'switch_inline_query_current_chat': self.switch_inline_query_current_chat, - 'callback_game': self.callback_game, - 'pay': self.pay, + "text": self.text, + "url": self.url, + "callback_data": self.callback_data, + "switch_inline_query": self.switch_inline_query, + "switch_inline_query_current_chat": self.switch_inline_query_current_chat, + "callback_game": self.callback_game, + "pay": self.pay, } inline_keyboard_button = InlineKeyboardButton.de_json(json_dict, None) @@ -113,11 +110,11 @@ def test_de_json(self, bot): assert inline_keyboard_button.pay == self.pay def test_equality(self): - a = InlineKeyboardButton('text', callback_data='data') - b = InlineKeyboardButton('text', callback_data='data') - c = InlineKeyboardButton('texts', callback_data='data') - d = InlineKeyboardButton('text', callback_data='info') - e = InlineKeyboardButton('text', url='http://google.com') + a = InlineKeyboardButton("text", callback_data="data") + b = InlineKeyboardButton("text", callback_data="data") + c = InlineKeyboardButton("texts", callback_data="data") + d = InlineKeyboardButton("text", callback_data="info") + e = InlineKeyboardButton("text", url="http://google.com") f = LoginUrl("http://google.com") assert a == b @@ -135,10 +132,10 @@ def test_equality(self): assert a != f assert hash(a) != hash(f) - @pytest.mark.parametrize('callback_data', ['foo', 1, ('da', 'ta'), object()]) + @pytest.mark.parametrize("callback_data", ["foo", 1, ("da", "ta"), object()]) def test_update_callback_data(self, callback_data): - button = InlineKeyboardButton(text='test', callback_data='data') - button_b = InlineKeyboardButton(text='test', callback_data='data') + button = InlineKeyboardButton(text="test", callback_data="data") + button_b = InlineKeyboardButton(text="test", callback_data="data") assert button == button_b assert hash(button) == hash(button_b) @@ -155,5 +152,5 @@ def test_update_callback_data(self, callback_data): button.update_callback_data({}) assert button.callback_data == {} - with pytest.raises(TypeError, match='unhashable'): + with pytest.raises(TypeError, match="unhashable"): hash(button) diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py index 719adaa4c04..9a6cd898035 100644 --- a/tests/test_inlinekeyboardmarkup.py +++ b/tests/test_inlinekeyboardmarkup.py @@ -23,7 +23,7 @@ from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ReplyMarkup, ReplyKeyboardMarkup -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_keyboard_markup(): return InlineKeyboardMarkup(TestInlineKeyboardMarkup.inline_keyboard) @@ -31,31 +31,28 @@ def inline_keyboard_markup(): class TestInlineKeyboardMarkup: inline_keyboard = [ [ - InlineKeyboardButton(text='button1', callback_data='data1'), - InlineKeyboardButton(text='button2', callback_data='data2'), + InlineKeyboardButton(text="button1", callback_data="data1"), + InlineKeyboardButton(text="button2", callback_data="data2"), ] ] - def test_slot_behaviour(self, inline_keyboard_markup, recwarn, mro_slots): + def test_slot_behaviour(self, inline_keyboard_markup, mro_slots): inst = inline_keyboard_markup for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.inline_keyboard = 'should give warning', self.inline_keyboard - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @flaky(3, 1) def test_send_message_with_inline_keyboard_markup(self, bot, chat_id, inline_keyboard_markup): message = bot.send_message( - chat_id, 'Testing InlineKeyboardMarkup', reply_markup=inline_keyboard_markup + chat_id, "Testing InlineKeyboardMarkup", reply_markup=inline_keyboard_markup ) - assert message.text == 'Testing InlineKeyboardMarkup' + assert message.text == "Testing InlineKeyboardMarkup" def test_from_button(self): inline_keyboard_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='button1', callback_data='data1') + InlineKeyboardButton(text="button1", callback_data="data1") ).inline_keyboard assert len(inline_keyboard_markup) == 1 assert len(inline_keyboard_markup[0]) == 1 @@ -63,8 +60,8 @@ def test_from_button(self): def test_from_row(self): inline_keyboard_markup = InlineKeyboardMarkup.from_row( [ - InlineKeyboardButton(text='button1', callback_data='data1'), - InlineKeyboardButton(text='button1', callback_data='data1'), + InlineKeyboardButton(text="button1", callback_data="data1"), + InlineKeyboardButton(text="button1", callback_data="data1"), ] ).inline_keyboard assert len(inline_keyboard_markup) == 1 @@ -73,8 +70,8 @@ def test_from_row(self): def test_from_column(self): inline_keyboard_markup = InlineKeyboardMarkup.from_column( [ - InlineKeyboardButton(text='button1', callback_data='data1'), - InlineKeyboardButton(text='button1', callback_data='data1'), + InlineKeyboardButton(text="button1", callback_data="data1"), + InlineKeyboardButton(text="button1", callback_data="data1"), ] ).inline_keyboard assert len(inline_keyboard_markup) == 2 @@ -96,37 +93,37 @@ def test( ): if reply_markup is not None: if isinstance(reply_markup, ReplyMarkup): - data['reply_markup'] = reply_markup.to_dict() + data["reply_markup"] = reply_markup.to_dict() else: - data['reply_markup'] = reply_markup + data["reply_markup"] = reply_markup - assert bool("'switch_inline_query': ''" in str(data['reply_markup'])) - assert bool("'switch_inline_query_current_chat': ''" in str(data['reply_markup'])) + assert bool("'switch_inline_query': ''" in str(data["reply_markup"])) + assert bool("'switch_inline_query_current_chat': ''" in str(data["reply_markup"])) inline_keyboard_markup.inline_keyboard[0][0].callback_data = None - inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = '' + inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = "" inline_keyboard_markup.inline_keyboard[0][1].callback_data = None - inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = '' + inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = "" - monkeypatch.setattr(bot, '_message', test) - bot.send_message(123, 'test', reply_markup=inline_keyboard_markup) + monkeypatch.setattr(bot, "_message", test) + bot.send_message(123, "test", reply_markup=inline_keyboard_markup) def test_to_dict(self, inline_keyboard_markup): inline_keyboard_markup_dict = inline_keyboard_markup.to_dict() assert isinstance(inline_keyboard_markup_dict, dict) - assert inline_keyboard_markup_dict['inline_keyboard'] == [ + assert inline_keyboard_markup_dict["inline_keyboard"] == [ [self.inline_keyboard[0][0].to_dict(), self.inline_keyboard[0][1].to_dict()] ] def test_de_json(self): json_dict = { - 'inline_keyboard': [ + "inline_keyboard": [ [ - {'text': 'start', 'url': 'http://google.com'}, - {'text': 'next', 'callback_data': 'abcd'}, + {"text": "start", "url": "http://google.com"}, + {"text": "next", "callback_data": "abcd"}, ], - [{'text': 'Cancel', 'callback_data': 'Cancel'}], + [{"text": "Cancel", "callback_data": "Cancel"}], ] } inline_keyboard_markup = InlineKeyboardMarkup.de_json(json_dict, None) @@ -141,51 +138,51 @@ def test_de_json(self): assert isinstance(keyboard[0][1], InlineKeyboardButton) assert isinstance(keyboard[1][0], InlineKeyboardButton) - assert keyboard[0][0].text == 'start' - assert keyboard[0][0].url == 'http://google.com' + assert keyboard[0][0].text == "start" + assert keyboard[0][0].url == "http://google.com" def test_equality(self): a = InlineKeyboardMarkup.from_column( [ - InlineKeyboardButton(label, callback_data='data') - for label in ['button1', 'button2', 'button3'] + InlineKeyboardButton(label, callback_data="data") + for label in ["button1", "button2", "button3"] ] ) b = InlineKeyboardMarkup.from_column( [ - InlineKeyboardButton(label, callback_data='data') - for label in ['button1', 'button2', 'button3'] + InlineKeyboardButton(label, callback_data="data") + for label in ["button1", "button2", "button3"] ] ) c = InlineKeyboardMarkup.from_column( - [InlineKeyboardButton(label, callback_data='data') for label in ['button1', 'button2']] + [InlineKeyboardButton(label, callback_data="data") for label in ["button1", "button2"]] ) d = InlineKeyboardMarkup.from_column( [ InlineKeyboardButton(label, callback_data=label) - for label in ['button1', 'button2', 'button3'] + for label in ["button1", "button2", "button3"] ] ) e = InlineKeyboardMarkup.from_column( - [InlineKeyboardButton(label, url=label) for label in ['button1', 'button2', 'button3']] + [InlineKeyboardButton(label, url=label) for label in ["button1", "button2", "button3"]] ) f = InlineKeyboardMarkup( [ [ - InlineKeyboardButton(label, callback_data='data') - for label in ['button1', 'button2'] + InlineKeyboardButton(label, callback_data="data") + for label in ["button1", "button2"] ], [ - InlineKeyboardButton(label, callback_data='data') - for label in ['button1', 'button2'] + InlineKeyboardButton(label, callback_data="data") + for label in ["button1", "button2"] ], [ - InlineKeyboardButton(label, callback_data='data') - for label in ['button1', 'button2'] + InlineKeyboardButton(label, callback_data="data") + for label in ["button1", "button2"] ], ] ) - g = ReplyKeyboardMarkup.from_column(['button1', 'button2', 'button3']) + g = ReplyKeyboardMarkup.from_column(["button1", "button2", "button3"]) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequery.py b/tests/test_inlinequery.py index 3e80b27c544..989879bc5e5 100644 --- a/tests/test_inlinequery.py +++ b/tests/test_inlinequery.py @@ -23,7 +23,7 @@ from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query(bot): return InlineQuery( TestInlineQuery.id_, @@ -38,28 +38,25 @@ def inline_query(bot): class TestInlineQuery: id_ = 1234 - from_user = User(1, 'First name', False) - query = 'query text' - offset = 'offset' + from_user = User(1, "First name", False) + query = "query text" + offset = "offset" location = Location(8.8, 53.1) chat_type = Chat.SENDER - def test_slot_behaviour(self, inline_query, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query, mro_slots): for attr in inline_query.__slots__: - assert getattr(inline_query, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inline_query.__dict__, f"got missing slot(s): {inline_query.__dict__}" + assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inline_query)) == len(set(mro_slots(inline_query))), "duplicate slot" - inline_query.custom, inline_query.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'id': self.id_, - 'from': self.from_user.to_dict(), - 'query': self.query, - 'offset': self.offset, - 'location': self.location.to_dict(), - 'chat_type': self.chat_type, + "id": self.id_, + "from": self.from_user.to_dict(), + "query": self.query, + "offset": self.offset, + "location": self.location.to_dict(), + "chat_type": self.chat_type, } inline_query_json = InlineQuery.de_json(json_dict, bot) @@ -74,44 +71,44 @@ def test_to_dict(self, inline_query): inline_query_dict = inline_query.to_dict() assert isinstance(inline_query_dict, dict) - assert inline_query_dict['id'] == inline_query.id - assert inline_query_dict['from'] == inline_query.from_user.to_dict() - assert inline_query_dict['location'] == inline_query.location.to_dict() - assert inline_query_dict['query'] == inline_query.query - assert inline_query_dict['offset'] == inline_query.offset - assert inline_query_dict['chat_type'] == inline_query.chat_type + assert inline_query_dict["id"] == inline_query.id + assert inline_query_dict["from"] == inline_query.from_user.to_dict() + assert inline_query_dict["location"] == inline_query.location.to_dict() + assert inline_query_dict["query"] == inline_query.query + assert inline_query_dict["offset"] == inline_query.offset + assert inline_query_dict["chat_type"] == inline_query.chat_type def test_answer(self, monkeypatch, inline_query): def make_assertion(*_, **kwargs): - return kwargs['inline_query_id'] == inline_query.id + return kwargs["inline_query_id"] == inline_query.id assert check_shortcut_signature( - InlineQuery.answer, Bot.answer_inline_query, ['inline_query_id'], ['auto_pagination'] + InlineQuery.answer, Bot.answer_inline_query, ["inline_query_id"], ["auto_pagination"] ) - assert check_shortcut_call(inline_query.answer, inline_query.bot, 'answer_inline_query') + assert check_shortcut_call(inline_query.answer, inline_query.bot, "answer_inline_query") assert check_defaults_handling(inline_query.answer, inline_query.bot) - monkeypatch.setattr(inline_query.bot, 'answer_inline_query', make_assertion) + monkeypatch.setattr(inline_query.bot, "answer_inline_query", make_assertion) assert inline_query.answer(results=[]) def test_answer_error(self, inline_query): - with pytest.raises(TypeError, match='mutually exclusive'): - inline_query.answer(results=[], auto_pagination=True, current_offset='foobar') + with pytest.raises(TypeError, match="mutually exclusive"): + inline_query.answer(results=[], auto_pagination=True, current_offset="foobar") def test_answer_auto_pagination(self, monkeypatch, inline_query): def make_assertion(*_, **kwargs): - inline_query_id_matches = kwargs['inline_query_id'] == inline_query.id - offset_matches = kwargs.get('current_offset') == inline_query.offset + inline_query_id_matches = kwargs["inline_query_id"] == inline_query.id + offset_matches = kwargs.get("current_offset") == inline_query.offset return offset_matches and inline_query_id_matches - monkeypatch.setattr(inline_query.bot, 'answer_inline_query', make_assertion) + monkeypatch.setattr(inline_query.bot, "answer_inline_query", make_assertion) assert inline_query.answer(results=[], auto_pagination=True) def test_equality(self): - a = InlineQuery(self.id_, User(1, '', False), '', '') - b = InlineQuery(self.id_, User(1, '', False), '', '') - c = InlineQuery(self.id_, User(0, '', False), '', '') - d = InlineQuery(0, User(1, '', False), '', '') + a = InlineQuery(self.id_, User(1, "", False), "", "") + b = InlineQuery(self.id_, User(1, "", False), "", "") + c = InlineQuery(self.id_, User(0, "", False), "", "") + d = InlineQuery(0, User(1, "", False), "", "") e = Update(self.id_) assert a == b diff --git a/tests/test_inlinequeryhandler.py b/tests/test_inlinequeryhandler.py index 4688a8004ea..e6bd7f259fc 100644 --- a/tests/test_inlinequeryhandler.py +++ b/tests/test_inlinequeryhandler.py @@ -35,47 +35,47 @@ ) from telegram.ext import InlineQueryHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def inline_query(bot): return Update( 0, inline_query=InlineQuery( - 'id', - User(2, 'test user', False), - 'test query', - offset='22', + "id", + User(2, "test user", False), + "test query", + offset="22", location=Location(latitude=-23.691288, longitude=-46.788279), ), ) @@ -84,42 +84,16 @@ def inline_query(bot): class TestInlineQueryHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): handler = InlineQueryHandler(self.callback_context) for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - - def callback_group(self, bot, update, groups=None, groupdict=None): - if groups is not None: - self.test_flag = groups == ('t', ' query') - if groupdict is not None: - self.test_flag = groupdict == {'begin': 't', 'end': ' query'} - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -135,134 +109,48 @@ def callback_context(self, update, context): def callback_context_pattern(self, update, context): if context.matches[0].groups(): - self.test_flag = context.matches[0].groups() == ('t', ' query') + self.test_flag = context.matches[0].groups() == ("t", " query") if context.matches[0].groupdict(): - self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' query'} - - def test_basic(self, dp, inline_query): - handler = InlineQueryHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(inline_query) - - dp.process_update(inline_query) - assert self.test_flag - - def test_with_pattern(self, inline_query): - handler = InlineQueryHandler(self.callback_basic, pattern='(?P.*)est(?P.*)') - - assert handler.check_update(inline_query) - - inline_query.inline_query.query = 'nothing here' - assert not handler.check_update(inline_query) - - def test_with_passing_group_dict(self, dp, inline_query): - handler = InlineQueryHandler( - self.callback_group, pattern='(?P.*)est(?P.*)', pass_groups=True - ) - dp.add_handler(handler) + self.test_flag = context.matches[0].groupdict() == {"begin": "t", "end": " query"} - dp.process_update(inline_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = InlineQueryHandler( - self.callback_group, pattern='(?P.*)est(?P.*)', pass_groupdict=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(inline_query) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, inline_query): - handler = InlineQueryHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(inline_query) - assert self.test_flag + def test_other_update_types(self, false_update): + handler = InlineQueryHandler(self.callback_context) + assert not handler.check_update(false_update) - dp.remove_handler(handler) - handler = InlineQueryHandler(self.callback_data_1, pass_chat_data=True) + def test_context(self, dp, inline_query): + handler = InlineQueryHandler(self.callback_context) dp.add_handler(handler) - self.test_flag = False dp.process_update(inline_query) assert self.test_flag - dp.remove_handler(handler) + def test_context_pattern(self, dp, inline_query): handler = InlineQueryHandler( - self.callback_data_2, pass_chat_data=True, pass_user_data=True + self.callback_context_pattern, pattern=r"(?P.*)est(?P.*)" ) dp.add_handler(handler) - self.test_flag = False - dp.process_update(inline_query) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, inline_query): - handler = InlineQueryHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(inline_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = InlineQueryHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False dp.process_update(inline_query) assert self.test_flag dp.remove_handler(handler) - handler = InlineQueryHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) + handler = InlineQueryHandler(self.callback_context_pattern, pattern=r"(t)est(.*)") dp.add_handler(handler) - self.test_flag = False dp.process_update(inline_query) assert self.test_flag - def test_other_update_types(self, false_update): - handler = InlineQueryHandler(self.callback_basic) - assert not handler.check_update(false_update) - - def test_context(self, cdp, inline_query): - handler = InlineQueryHandler(self.callback_context) - cdp.add_handler(handler) - - cdp.process_update(inline_query) - assert self.test_flag - - def test_context_pattern(self, cdp, inline_query): - handler = InlineQueryHandler( - self.callback_context_pattern, pattern=r'(?P.*)est(?P.*)' - ) - cdp.add_handler(handler) - - cdp.process_update(inline_query) - assert self.test_flag - - cdp.remove_handler(handler) - handler = InlineQueryHandler(self.callback_context_pattern, pattern=r'(t)est(.*)') - cdp.add_handler(handler) - - cdp.process_update(inline_query) - assert self.test_flag - - @pytest.mark.parametrize('chat_types', [[Chat.SENDER], [Chat.SENDER, Chat.SUPERGROUP], []]) + @pytest.mark.parametrize("chat_types", [[Chat.SENDER], [Chat.SENDER, Chat.SUPERGROUP], []]) @pytest.mark.parametrize( - 'chat_type,result', [(Chat.SENDER, True), (Chat.CHANNEL, False), (None, False)] + "chat_type,result", [(Chat.SENDER, True), (Chat.CHANNEL, False), (None, False)] ) - def test_chat_types(self, cdp, inline_query, chat_types, chat_type, result): + def test_chat_types(self, dp, inline_query, chat_types, chat_type, result): try: inline_query.inline_query.chat_type = chat_type handler = InlineQueryHandler(self.callback_context, chat_types=chat_types) - cdp.add_handler(handler) - cdp.process_update(inline_query) + dp.add_handler(handler) + dp.process_update(inline_query) if not chat_types: assert self.test_flag is False @@ -270,4 +158,4 @@ def test_chat_types(self, cdp, inline_query, chat_types, chat_type, result): assert self.test_flag == result finally: - inline_query.chat_type = None + inline_query.inline_query.chat_type = None diff --git a/tests/test_inlinequeryresultarticle.py b/tests/test_inlinequeryresultarticle.py index a5a383d1d35..fdb451980d4 100644 --- a/tests/test_inlinequeryresultarticle.py +++ b/tests/test_inlinequeryresultarticle.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_article(): return InlineQueryResultArticle( TestInlineQueryResultArticle.id_, @@ -45,26 +45,23 @@ def inline_query_result_article(): class TestInlineQueryResultArticle: - id_ = 'id' - type_ = 'article' - title = 'title' - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) - url = 'url' + id_ = "id" + type_ = "article" + title = "title" + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) + url = "url" hide_url = True - description = 'description' - thumb_url = 'thumb url' + description = "description" + thumb_url = "thumb url" thumb_height = 10 thumb_width = 15 def test_slot_behaviour(self, inline_query_result_article, mro_slots, recwarn): inst = inline_query_result_article for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_article): assert inline_query_result_article.type == self.type_ @@ -86,41 +83,41 @@ def test_to_dict(self, inline_query_result_article): inline_query_result_article_dict = inline_query_result_article.to_dict() assert isinstance(inline_query_result_article_dict, dict) - assert inline_query_result_article_dict['type'] == inline_query_result_article.type - assert inline_query_result_article_dict['id'] == inline_query_result_article.id - assert inline_query_result_article_dict['title'] == inline_query_result_article.title + assert inline_query_result_article_dict["type"] == inline_query_result_article.type + assert inline_query_result_article_dict["id"] == inline_query_result_article.id + assert inline_query_result_article_dict["title"] == inline_query_result_article.title assert ( - inline_query_result_article_dict['input_message_content'] + inline_query_result_article_dict["input_message_content"] == inline_query_result_article.input_message_content.to_dict() ) assert ( - inline_query_result_article_dict['reply_markup'] + inline_query_result_article_dict["reply_markup"] == inline_query_result_article.reply_markup.to_dict() ) - assert inline_query_result_article_dict['url'] == inline_query_result_article.url - assert inline_query_result_article_dict['hide_url'] == inline_query_result_article.hide_url + assert inline_query_result_article_dict["url"] == inline_query_result_article.url + assert inline_query_result_article_dict["hide_url"] == inline_query_result_article.hide_url assert ( - inline_query_result_article_dict['description'] + inline_query_result_article_dict["description"] == inline_query_result_article.description ) assert ( - inline_query_result_article_dict['thumb_url'] == inline_query_result_article.thumb_url + inline_query_result_article_dict["thumb_url"] == inline_query_result_article.thumb_url ) assert ( - inline_query_result_article_dict['thumb_height'] + inline_query_result_article_dict["thumb_height"] == inline_query_result_article.thumb_height ) assert ( - inline_query_result_article_dict['thumb_width'] + inline_query_result_article_dict["thumb_width"] == inline_query_result_article.thumb_width ) def test_equality(self): a = InlineQueryResultArticle(self.id_, self.title, self.input_message_content) b = InlineQueryResultArticle(self.id_, self.title, self.input_message_content) - c = InlineQueryResultArticle(self.id_, '', self.input_message_content) - d = InlineQueryResultArticle('', self.title, self.input_message_content) - e = InlineQueryResultAudio(self.id_, '', '') + c = InlineQueryResultArticle(self.id_, "", self.input_message_content) + d = InlineQueryResultArticle("", self.title, self.input_message_content) + e = InlineQueryResultAudio(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultaudio.py b/tests/test_inlinequeryresultaudio.py index 5071a49a169..1b980795c79 100644 --- a/tests/test_inlinequeryresultaudio.py +++ b/tests/test_inlinequeryresultaudio.py @@ -29,7 +29,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_audio(): return InlineQueryResultAudio( TestInlineQueryResultAudio.id_, @@ -46,26 +46,23 @@ def inline_query_result_audio(): class TestInlineQueryResultAudio: - id_ = 'id' - type_ = 'audio' - audio_url = 'audio url' - title = 'title' - performer = 'performer' - audio_duration = 'audio_duration' - caption = 'caption' - parse_mode = 'Markdown' + id_ = "id" + type_ = "audio" + audio_url = "audio url" + title = "title" + performer = "performer" + audio_duration = "audio_duration" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_audio, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_audio, mro_slots): inst = inline_query_result_audio for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_audio): assert inline_query_result_audio.type == self.type_ @@ -87,35 +84,35 @@ def test_to_dict(self, inline_query_result_audio): inline_query_result_audio_dict = inline_query_result_audio.to_dict() assert isinstance(inline_query_result_audio_dict, dict) - assert inline_query_result_audio_dict['type'] == inline_query_result_audio.type - assert inline_query_result_audio_dict['id'] == inline_query_result_audio.id - assert inline_query_result_audio_dict['audio_url'] == inline_query_result_audio.audio_url - assert inline_query_result_audio_dict['title'] == inline_query_result_audio.title - assert inline_query_result_audio_dict['performer'] == inline_query_result_audio.performer + assert inline_query_result_audio_dict["type"] == inline_query_result_audio.type + assert inline_query_result_audio_dict["id"] == inline_query_result_audio.id + assert inline_query_result_audio_dict["audio_url"] == inline_query_result_audio.audio_url + assert inline_query_result_audio_dict["title"] == inline_query_result_audio.title + assert inline_query_result_audio_dict["performer"] == inline_query_result_audio.performer assert ( - inline_query_result_audio_dict['audio_duration'] + inline_query_result_audio_dict["audio_duration"] == inline_query_result_audio.audio_duration ) - assert inline_query_result_audio_dict['caption'] == inline_query_result_audio.caption - assert inline_query_result_audio_dict['parse_mode'] == inline_query_result_audio.parse_mode - assert inline_query_result_audio_dict['caption_entities'] == [ + assert inline_query_result_audio_dict["caption"] == inline_query_result_audio.caption + assert inline_query_result_audio_dict["parse_mode"] == inline_query_result_audio.parse_mode + assert inline_query_result_audio_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_audio.caption_entities ] assert ( - inline_query_result_audio_dict['input_message_content'] + inline_query_result_audio_dict["input_message_content"] == inline_query_result_audio.input_message_content.to_dict() ) assert ( - inline_query_result_audio_dict['reply_markup'] + inline_query_result_audio_dict["reply_markup"] == inline_query_result_audio.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultAudio(self.id_, self.audio_url, self.title) b = InlineQueryResultAudio(self.id_, self.title, self.title) - c = InlineQueryResultAudio(self.id_, '', self.title) - d = InlineQueryResultAudio('', self.audio_url, self.title) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultAudio(self.id_, "", self.title) + d = InlineQueryResultAudio("", self.audio_url, self.title) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedaudio.py b/tests/test_inlinequeryresultcachedaudio.py index 33ee9b858bb..93e750a8f92 100644 --- a/tests/test_inlinequeryresultcachedaudio.py +++ b/tests/test_inlinequeryresultcachedaudio.py @@ -29,7 +29,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_audio(): return InlineQueryResultCachedAudio( TestInlineQueryResultCachedAudio.id_, @@ -43,23 +43,20 @@ def inline_query_result_cached_audio(): class TestInlineQueryResultCachedAudio: - id_ = 'id' - type_ = 'audio' - audio_file_id = 'audio file id' - caption = 'caption' - parse_mode = 'HTML' + id_ = "id" + type_ = "audio" + audio_file_id = "audio file id" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_audio, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_cached_audio, mro_slots): inst = inline_query_result_cached_audio for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_audio): assert inline_query_result_cached_audio.type == self.type_ @@ -81,39 +78,39 @@ def test_to_dict(self, inline_query_result_cached_audio): assert isinstance(inline_query_result_cached_audio_dict, dict) assert ( - inline_query_result_cached_audio_dict['type'] == inline_query_result_cached_audio.type + inline_query_result_cached_audio_dict["type"] == inline_query_result_cached_audio.type ) - assert inline_query_result_cached_audio_dict['id'] == inline_query_result_cached_audio.id + assert inline_query_result_cached_audio_dict["id"] == inline_query_result_cached_audio.id assert ( - inline_query_result_cached_audio_dict['audio_file_id'] + inline_query_result_cached_audio_dict["audio_file_id"] == inline_query_result_cached_audio.audio_file_id ) assert ( - inline_query_result_cached_audio_dict['caption'] + inline_query_result_cached_audio_dict["caption"] == inline_query_result_cached_audio.caption ) assert ( - inline_query_result_cached_audio_dict['parse_mode'] + inline_query_result_cached_audio_dict["parse_mode"] == inline_query_result_cached_audio.parse_mode ) - assert inline_query_result_cached_audio_dict['caption_entities'] == [ + assert inline_query_result_cached_audio_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_audio.caption_entities ] assert ( - inline_query_result_cached_audio_dict['input_message_content'] + inline_query_result_cached_audio_dict["input_message_content"] == inline_query_result_cached_audio.input_message_content.to_dict() ) assert ( - inline_query_result_cached_audio_dict['reply_markup'] + inline_query_result_cached_audio_dict["reply_markup"] == inline_query_result_cached_audio.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedAudio(self.id_, self.audio_file_id) b = InlineQueryResultCachedAudio(self.id_, self.audio_file_id) - c = InlineQueryResultCachedAudio(self.id_, '') - d = InlineQueryResultCachedAudio('', self.audio_file_id) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedAudio(self.id_, "") + d = InlineQueryResultCachedAudio("", self.audio_file_id) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcacheddocument.py b/tests/test_inlinequeryresultcacheddocument.py index a25d089df91..da023aceaaa 100644 --- a/tests/test_inlinequeryresultcacheddocument.py +++ b/tests/test_inlinequeryresultcacheddocument.py @@ -29,7 +29,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_document(): return InlineQueryResultCachedDocument( TestInlineQueryResultCachedDocument.id_, @@ -45,25 +45,22 @@ def inline_query_result_cached_document(): class TestInlineQueryResultCachedDocument: - id_ = 'id' - type_ = 'document' - document_file_id = 'document file id' - title = 'title' - caption = 'caption' - parse_mode = 'Markdown' + id_ = "id" + type_ = "document" + document_file_id = "document file id" + title = "title" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - description = 'description' - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + description = "description" + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_document, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_cached_document, mro_slots): inst = inline_query_result_cached_document for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_document): assert inline_query_result_cached_document.id == self.id_ @@ -88,51 +85,51 @@ def test_to_dict(self, inline_query_result_cached_document): assert isinstance(inline_query_result_cached_document_dict, dict) assert ( - inline_query_result_cached_document_dict['id'] + inline_query_result_cached_document_dict["id"] == inline_query_result_cached_document.id ) assert ( - inline_query_result_cached_document_dict['type'] + inline_query_result_cached_document_dict["type"] == inline_query_result_cached_document.type ) assert ( - inline_query_result_cached_document_dict['document_file_id'] + inline_query_result_cached_document_dict["document_file_id"] == inline_query_result_cached_document.document_file_id ) assert ( - inline_query_result_cached_document_dict['title'] + inline_query_result_cached_document_dict["title"] == inline_query_result_cached_document.title ) assert ( - inline_query_result_cached_document_dict['caption'] + inline_query_result_cached_document_dict["caption"] == inline_query_result_cached_document.caption ) assert ( - inline_query_result_cached_document_dict['parse_mode'] + inline_query_result_cached_document_dict["parse_mode"] == inline_query_result_cached_document.parse_mode ) - assert inline_query_result_cached_document_dict['caption_entities'] == [ + assert inline_query_result_cached_document_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_document.caption_entities ] assert ( - inline_query_result_cached_document_dict['description'] + inline_query_result_cached_document_dict["description"] == inline_query_result_cached_document.description ) assert ( - inline_query_result_cached_document_dict['input_message_content'] + inline_query_result_cached_document_dict["input_message_content"] == inline_query_result_cached_document.input_message_content.to_dict() ) assert ( - inline_query_result_cached_document_dict['reply_markup'] + inline_query_result_cached_document_dict["reply_markup"] == inline_query_result_cached_document.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedDocument(self.id_, self.title, self.document_file_id) b = InlineQueryResultCachedDocument(self.id_, self.title, self.document_file_id) - c = InlineQueryResultCachedDocument(self.id_, self.title, '') - d = InlineQueryResultCachedDocument('', self.title, self.document_file_id) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedDocument(self.id_, self.title, "") + d = InlineQueryResultCachedDocument("", self.title, self.document_file_id) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedgif.py b/tests/test_inlinequeryresultcachedgif.py index 83bf386dd03..019e208fd14 100644 --- a/tests/test_inlinequeryresultcachedgif.py +++ b/tests/test_inlinequeryresultcachedgif.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_gif(): return InlineQueryResultCachedGif( TestInlineQueryResultCachedGif.id_, @@ -43,24 +43,21 @@ def inline_query_result_cached_gif(): class TestInlineQueryResultCachedGif: - id_ = 'id' - type_ = 'gif' - gif_file_id = 'gif file id' - title = 'title' - caption = 'caption' - parse_mode = 'HTML' + id_ = "id" + type_ = "gif" + gif_file_id = "gif file id" + title = "title" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_gif, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_cached_gif, mro_slots): inst = inline_query_result_cached_gif for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_gif): assert inline_query_result_cached_gif.type == self.type_ @@ -80,39 +77,39 @@ def test_to_dict(self, inline_query_result_cached_gif): inline_query_result_cached_gif_dict = inline_query_result_cached_gif.to_dict() assert isinstance(inline_query_result_cached_gif_dict, dict) - assert inline_query_result_cached_gif_dict['type'] == inline_query_result_cached_gif.type - assert inline_query_result_cached_gif_dict['id'] == inline_query_result_cached_gif.id + assert inline_query_result_cached_gif_dict["type"] == inline_query_result_cached_gif.type + assert inline_query_result_cached_gif_dict["id"] == inline_query_result_cached_gif.id assert ( - inline_query_result_cached_gif_dict['gif_file_id'] + inline_query_result_cached_gif_dict["gif_file_id"] == inline_query_result_cached_gif.gif_file_id ) - assert inline_query_result_cached_gif_dict['title'] == inline_query_result_cached_gif.title + assert inline_query_result_cached_gif_dict["title"] == inline_query_result_cached_gif.title assert ( - inline_query_result_cached_gif_dict['caption'] + inline_query_result_cached_gif_dict["caption"] == inline_query_result_cached_gif.caption ) assert ( - inline_query_result_cached_gif_dict['parse_mode'] + inline_query_result_cached_gif_dict["parse_mode"] == inline_query_result_cached_gif.parse_mode ) - assert inline_query_result_cached_gif_dict['caption_entities'] == [ + assert inline_query_result_cached_gif_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_gif.caption_entities ] assert ( - inline_query_result_cached_gif_dict['input_message_content'] + inline_query_result_cached_gif_dict["input_message_content"] == inline_query_result_cached_gif.input_message_content.to_dict() ) assert ( - inline_query_result_cached_gif_dict['reply_markup'] + inline_query_result_cached_gif_dict["reply_markup"] == inline_query_result_cached_gif.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedGif(self.id_, self.gif_file_id) b = InlineQueryResultCachedGif(self.id_, self.gif_file_id) - c = InlineQueryResultCachedGif(self.id_, '') - d = InlineQueryResultCachedGif('', self.gif_file_id) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedGif(self.id_, "") + d = InlineQueryResultCachedGif("", self.gif_file_id) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedmpeg4gif.py b/tests/test_inlinequeryresultcachedmpeg4gif.py index edd48538888..719df242d00 100644 --- a/tests/test_inlinequeryresultcachedmpeg4gif.py +++ b/tests/test_inlinequeryresultcachedmpeg4gif.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_mpeg4_gif(): return InlineQueryResultCachedMpeg4Gif( TestInlineQueryResultCachedMpeg4Gif.id_, @@ -43,24 +43,21 @@ def inline_query_result_cached_mpeg4_gif(): class TestInlineQueryResultCachedMpeg4Gif: - id_ = 'id' - type_ = 'mpeg4_gif' - mpeg4_file_id = 'mpeg4 file id' - title = 'title' - caption = 'caption' - parse_mode = 'Markdown' + id_ = "id" + type_ = "mpeg4_gif" + mpeg4_file_id = "mpeg4 file id" + title = "title" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif, mro_slots): inst = inline_query_result_cached_mpeg4_gif for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_mpeg4_gif): assert inline_query_result_cached_mpeg4_gif.type == self.type_ @@ -84,47 +81,47 @@ def test_to_dict(self, inline_query_result_cached_mpeg4_gif): assert isinstance(inline_query_result_cached_mpeg4_gif_dict, dict) assert ( - inline_query_result_cached_mpeg4_gif_dict['type'] + inline_query_result_cached_mpeg4_gif_dict["type"] == inline_query_result_cached_mpeg4_gif.type ) assert ( - inline_query_result_cached_mpeg4_gif_dict['id'] + inline_query_result_cached_mpeg4_gif_dict["id"] == inline_query_result_cached_mpeg4_gif.id ) assert ( - inline_query_result_cached_mpeg4_gif_dict['mpeg4_file_id'] + inline_query_result_cached_mpeg4_gif_dict["mpeg4_file_id"] == inline_query_result_cached_mpeg4_gif.mpeg4_file_id ) assert ( - inline_query_result_cached_mpeg4_gif_dict['title'] + inline_query_result_cached_mpeg4_gif_dict["title"] == inline_query_result_cached_mpeg4_gif.title ) assert ( - inline_query_result_cached_mpeg4_gif_dict['caption'] + inline_query_result_cached_mpeg4_gif_dict["caption"] == inline_query_result_cached_mpeg4_gif.caption ) assert ( - inline_query_result_cached_mpeg4_gif_dict['parse_mode'] + inline_query_result_cached_mpeg4_gif_dict["parse_mode"] == inline_query_result_cached_mpeg4_gif.parse_mode ) - assert inline_query_result_cached_mpeg4_gif_dict['caption_entities'] == [ + assert inline_query_result_cached_mpeg4_gif_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_mpeg4_gif.caption_entities ] assert ( - inline_query_result_cached_mpeg4_gif_dict['input_message_content'] + inline_query_result_cached_mpeg4_gif_dict["input_message_content"] == inline_query_result_cached_mpeg4_gif.input_message_content.to_dict() ) assert ( - inline_query_result_cached_mpeg4_gif_dict['reply_markup'] + inline_query_result_cached_mpeg4_gif_dict["reply_markup"] == inline_query_result_cached_mpeg4_gif.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id) b = InlineQueryResultCachedMpeg4Gif(self.id_, self.mpeg4_file_id) - c = InlineQueryResultCachedMpeg4Gif(self.id_, '') - d = InlineQueryResultCachedMpeg4Gif('', self.mpeg4_file_id) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedMpeg4Gif(self.id_, "") + d = InlineQueryResultCachedMpeg4Gif("", self.mpeg4_file_id) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedphoto.py b/tests/test_inlinequeryresultcachedphoto.py index 30f6b6c0485..21a868a4237 100644 --- a/tests/test_inlinequeryresultcachedphoto.py +++ b/tests/test_inlinequeryresultcachedphoto.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_photo(): return InlineQueryResultCachedPhoto( TestInlineQueryResultCachedPhoto.id_, @@ -44,25 +44,22 @@ def inline_query_result_cached_photo(): class TestInlineQueryResultCachedPhoto: - id_ = 'id' - type_ = 'photo' - photo_file_id = 'photo file id' - title = 'title' - description = 'description' - caption = 'caption' - parse_mode = 'HTML' + id_ = "id" + type_ = "photo" + photo_file_id = "photo file id" + title = "title" + description = "description" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_photo, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_cached_photo, mro_slots): inst = inline_query_result_cached_photo for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_photo): assert inline_query_result_cached_photo.type == self.type_ @@ -86,47 +83,47 @@ def test_to_dict(self, inline_query_result_cached_photo): assert isinstance(inline_query_result_cached_photo_dict, dict) assert ( - inline_query_result_cached_photo_dict['type'] == inline_query_result_cached_photo.type + inline_query_result_cached_photo_dict["type"] == inline_query_result_cached_photo.type ) - assert inline_query_result_cached_photo_dict['id'] == inline_query_result_cached_photo.id + assert inline_query_result_cached_photo_dict["id"] == inline_query_result_cached_photo.id assert ( - inline_query_result_cached_photo_dict['photo_file_id'] + inline_query_result_cached_photo_dict["photo_file_id"] == inline_query_result_cached_photo.photo_file_id ) assert ( - inline_query_result_cached_photo_dict['title'] + inline_query_result_cached_photo_dict["title"] == inline_query_result_cached_photo.title ) assert ( - inline_query_result_cached_photo_dict['description'] + inline_query_result_cached_photo_dict["description"] == inline_query_result_cached_photo.description ) assert ( - inline_query_result_cached_photo_dict['caption'] + inline_query_result_cached_photo_dict["caption"] == inline_query_result_cached_photo.caption ) assert ( - inline_query_result_cached_photo_dict['parse_mode'] + inline_query_result_cached_photo_dict["parse_mode"] == inline_query_result_cached_photo.parse_mode ) - assert inline_query_result_cached_photo_dict['caption_entities'] == [ + assert inline_query_result_cached_photo_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_photo.caption_entities ] assert ( - inline_query_result_cached_photo_dict['input_message_content'] + inline_query_result_cached_photo_dict["input_message_content"] == inline_query_result_cached_photo.input_message_content.to_dict() ) assert ( - inline_query_result_cached_photo_dict['reply_markup'] + inline_query_result_cached_photo_dict["reply_markup"] == inline_query_result_cached_photo.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id) b = InlineQueryResultCachedPhoto(self.id_, self.photo_file_id) - c = InlineQueryResultCachedPhoto(self.id_, '') - d = InlineQueryResultCachedPhoto('', self.photo_file_id) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedPhoto(self.id_, "") + d = InlineQueryResultCachedPhoto("", self.photo_file_id) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedsticker.py b/tests/test_inlinequeryresultcachedsticker.py index 42615fc66f3..cefe325343b 100644 --- a/tests/test_inlinequeryresultcachedsticker.py +++ b/tests/test_inlinequeryresultcachedsticker.py @@ -27,7 +27,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_sticker(): return InlineQueryResultCachedSticker( TestInlineQueryResultCachedSticker.id_, @@ -38,20 +38,17 @@ def inline_query_result_cached_sticker(): class TestInlineQueryResultCachedSticker: - id_ = 'id' - type_ = 'sticker' - sticker_file_id = 'sticker file id' - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + id_ = "id" + type_ = "sticker" + sticker_file_id = "sticker file id" + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_sticker, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_cached_sticker, mro_slots): inst = inline_query_result_cached_sticker for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_sticker): assert inline_query_result_cached_sticker.type == self.type_ @@ -71,31 +68,31 @@ def test_to_dict(self, inline_query_result_cached_sticker): assert isinstance(inline_query_result_cached_sticker_dict, dict) assert ( - inline_query_result_cached_sticker_dict['type'] + inline_query_result_cached_sticker_dict["type"] == inline_query_result_cached_sticker.type ) assert ( - inline_query_result_cached_sticker_dict['id'] == inline_query_result_cached_sticker.id + inline_query_result_cached_sticker_dict["id"] == inline_query_result_cached_sticker.id ) assert ( - inline_query_result_cached_sticker_dict['sticker_file_id'] + inline_query_result_cached_sticker_dict["sticker_file_id"] == inline_query_result_cached_sticker.sticker_file_id ) assert ( - inline_query_result_cached_sticker_dict['input_message_content'] + inline_query_result_cached_sticker_dict["input_message_content"] == inline_query_result_cached_sticker.input_message_content.to_dict() ) assert ( - inline_query_result_cached_sticker_dict['reply_markup'] + inline_query_result_cached_sticker_dict["reply_markup"] == inline_query_result_cached_sticker.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedSticker(self.id_, self.sticker_file_id) b = InlineQueryResultCachedSticker(self.id_, self.sticker_file_id) - c = InlineQueryResultCachedSticker(self.id_, '') - d = InlineQueryResultCachedSticker('', self.sticker_file_id) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedSticker(self.id_, "") + d = InlineQueryResultCachedSticker("", self.sticker_file_id) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedvideo.py b/tests/test_inlinequeryresultcachedvideo.py index 7a933e279e7..518d5c50d18 100644 --- a/tests/test_inlinequeryresultcachedvideo.py +++ b/tests/test_inlinequeryresultcachedvideo.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_video(): return InlineQueryResultCachedVideo( TestInlineQueryResultCachedVideo.id_, @@ -44,25 +44,22 @@ def inline_query_result_cached_video(): class TestInlineQueryResultCachedVideo: - id_ = 'id' - type_ = 'video' - video_file_id = 'video file id' - title = 'title' - caption = 'caption' - parse_mode = 'Markdown' + id_ = "id" + type_ = "video" + video_file_id = "video file id" + title = "title" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - description = 'description' - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + description = "description" + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_video, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_cached_video, mro_slots): inst = inline_query_result_cached_video for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_video): assert inline_query_result_cached_video.type == self.type_ @@ -86,47 +83,47 @@ def test_to_dict(self, inline_query_result_cached_video): assert isinstance(inline_query_result_cached_video_dict, dict) assert ( - inline_query_result_cached_video_dict['type'] == inline_query_result_cached_video.type + inline_query_result_cached_video_dict["type"] == inline_query_result_cached_video.type ) - assert inline_query_result_cached_video_dict['id'] == inline_query_result_cached_video.id + assert inline_query_result_cached_video_dict["id"] == inline_query_result_cached_video.id assert ( - inline_query_result_cached_video_dict['video_file_id'] + inline_query_result_cached_video_dict["video_file_id"] == inline_query_result_cached_video.video_file_id ) assert ( - inline_query_result_cached_video_dict['title'] + inline_query_result_cached_video_dict["title"] == inline_query_result_cached_video.title ) assert ( - inline_query_result_cached_video_dict['description'] + inline_query_result_cached_video_dict["description"] == inline_query_result_cached_video.description ) assert ( - inline_query_result_cached_video_dict['caption'] + inline_query_result_cached_video_dict["caption"] == inline_query_result_cached_video.caption ) assert ( - inline_query_result_cached_video_dict['parse_mode'] + inline_query_result_cached_video_dict["parse_mode"] == inline_query_result_cached_video.parse_mode ) - assert inline_query_result_cached_video_dict['caption_entities'] == [ + assert inline_query_result_cached_video_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_video.caption_entities ] assert ( - inline_query_result_cached_video_dict['input_message_content'] + inline_query_result_cached_video_dict["input_message_content"] == inline_query_result_cached_video.input_message_content.to_dict() ) assert ( - inline_query_result_cached_video_dict['reply_markup'] + inline_query_result_cached_video_dict["reply_markup"] == inline_query_result_cached_video.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title) b = InlineQueryResultCachedVideo(self.id_, self.video_file_id, self.title) - c = InlineQueryResultCachedVideo(self.id_, '', self.title) - d = InlineQueryResultCachedVideo('', self.video_file_id, self.title) - e = InlineQueryResultCachedVoice(self.id_, '', '') + c = InlineQueryResultCachedVideo(self.id_, "", self.title) + d = InlineQueryResultCachedVideo("", self.video_file_id, self.title) + e = InlineQueryResultCachedVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcachedvoice.py b/tests/test_inlinequeryresultcachedvoice.py index a87239bd9e8..4c0f63a5372 100644 --- a/tests/test_inlinequeryresultcachedvoice.py +++ b/tests/test_inlinequeryresultcachedvoice.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_cached_voice(): return InlineQueryResultCachedVoice( TestInlineQueryResultCachedVoice.id_, @@ -43,24 +43,21 @@ def inline_query_result_cached_voice(): class TestInlineQueryResultCachedVoice: - id_ = 'id' - type_ = 'voice' - voice_file_id = 'voice file id' - title = 'title' - caption = 'caption' - parse_mode = 'HTML' + id_ = "id" + type_ = "voice" + voice_file_id = "voice file id" + title = "title" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_cached_voice, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_cached_voice, mro_slots): inst = inline_query_result_cached_voice for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_cached_voice): assert inline_query_result_cached_voice.type == self.type_ @@ -83,43 +80,43 @@ def test_to_dict(self, inline_query_result_cached_voice): assert isinstance(inline_query_result_cached_voice_dict, dict) assert ( - inline_query_result_cached_voice_dict['type'] == inline_query_result_cached_voice.type + inline_query_result_cached_voice_dict["type"] == inline_query_result_cached_voice.type ) - assert inline_query_result_cached_voice_dict['id'] == inline_query_result_cached_voice.id + assert inline_query_result_cached_voice_dict["id"] == inline_query_result_cached_voice.id assert ( - inline_query_result_cached_voice_dict['voice_file_id'] + inline_query_result_cached_voice_dict["voice_file_id"] == inline_query_result_cached_voice.voice_file_id ) assert ( - inline_query_result_cached_voice_dict['title'] + inline_query_result_cached_voice_dict["title"] == inline_query_result_cached_voice.title ) assert ( - inline_query_result_cached_voice_dict['caption'] + inline_query_result_cached_voice_dict["caption"] == inline_query_result_cached_voice.caption ) assert ( - inline_query_result_cached_voice_dict['parse_mode'] + inline_query_result_cached_voice_dict["parse_mode"] == inline_query_result_cached_voice.parse_mode ) - assert inline_query_result_cached_voice_dict['caption_entities'] == [ + assert inline_query_result_cached_voice_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_cached_voice.caption_entities ] assert ( - inline_query_result_cached_voice_dict['input_message_content'] + inline_query_result_cached_voice_dict["input_message_content"] == inline_query_result_cached_voice.input_message_content.to_dict() ) assert ( - inline_query_result_cached_voice_dict['reply_markup'] + inline_query_result_cached_voice_dict["reply_markup"] == inline_query_result_cached_voice.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultCachedVoice(self.id_, self.voice_file_id, self.title) b = InlineQueryResultCachedVoice(self.id_, self.voice_file_id, self.title) - c = InlineQueryResultCachedVoice(self.id_, '', self.title) - d = InlineQueryResultCachedVoice('', self.voice_file_id, self.title) - e = InlineQueryResultCachedAudio(self.id_, '', '') + c = InlineQueryResultCachedVoice(self.id_, "", self.title) + d = InlineQueryResultCachedVoice("", self.voice_file_id, self.title) + e = InlineQueryResultCachedAudio(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultcontact.py b/tests/test_inlinequeryresultcontact.py index c8f74e2b095..83f2f9bd7e2 100644 --- a/tests/test_inlinequeryresultcontact.py +++ b/tests/test_inlinequeryresultcontact.py @@ -27,7 +27,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_contact(): return InlineQueryResultContact( TestInlineQueryResultContact.id_, @@ -43,25 +43,22 @@ def inline_query_result_contact(): class TestInlineQueryResultContact: - id_ = 'id' - type_ = 'contact' - phone_number = 'phone_number' - first_name = 'first_name' - last_name = 'last_name' - thumb_url = 'thumb url' + id_ = "id" + type_ = "contact" + phone_number = "phone_number" + first_name = "first_name" + last_name = "last_name" + thumb_url = "thumb url" thumb_width = 10 thumb_height = 15 - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_contact, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_contact, mro_slots): inst = inline_query_result_contact for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_contact): assert inline_query_result_contact.id == self.id_ @@ -82,45 +79,45 @@ def test_to_dict(self, inline_query_result_contact): inline_query_result_contact_dict = inline_query_result_contact.to_dict() assert isinstance(inline_query_result_contact_dict, dict) - assert inline_query_result_contact_dict['id'] == inline_query_result_contact.id - assert inline_query_result_contact_dict['type'] == inline_query_result_contact.type + assert inline_query_result_contact_dict["id"] == inline_query_result_contact.id + assert inline_query_result_contact_dict["type"] == inline_query_result_contact.type assert ( - inline_query_result_contact_dict['phone_number'] + inline_query_result_contact_dict["phone_number"] == inline_query_result_contact.phone_number ) assert ( - inline_query_result_contact_dict['first_name'] + inline_query_result_contact_dict["first_name"] == inline_query_result_contact.first_name ) assert ( - inline_query_result_contact_dict['last_name'] == inline_query_result_contact.last_name + inline_query_result_contact_dict["last_name"] == inline_query_result_contact.last_name ) assert ( - inline_query_result_contact_dict['thumb_url'] == inline_query_result_contact.thumb_url + inline_query_result_contact_dict["thumb_url"] == inline_query_result_contact.thumb_url ) assert ( - inline_query_result_contact_dict['thumb_width'] + inline_query_result_contact_dict["thumb_width"] == inline_query_result_contact.thumb_width ) assert ( - inline_query_result_contact_dict['thumb_height'] + inline_query_result_contact_dict["thumb_height"] == inline_query_result_contact.thumb_height ) assert ( - inline_query_result_contact_dict['input_message_content'] + inline_query_result_contact_dict["input_message_content"] == inline_query_result_contact.input_message_content.to_dict() ) assert ( - inline_query_result_contact_dict['reply_markup'] + inline_query_result_contact_dict["reply_markup"] == inline_query_result_contact.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultContact(self.id_, self.phone_number, self.first_name) b = InlineQueryResultContact(self.id_, self.phone_number, self.first_name) - c = InlineQueryResultContact(self.id_, '', self.first_name) - d = InlineQueryResultContact('', self.phone_number, self.first_name) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultContact(self.id_, "", self.first_name) + d = InlineQueryResultContact("", self.phone_number, self.first_name) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultdocument.py b/tests/test_inlinequeryresultdocument.py index 983ddbab87d..318b59887e7 100644 --- a/tests/test_inlinequeryresultdocument.py +++ b/tests/test_inlinequeryresultdocument.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_document(): return InlineQueryResultDocument( TestInlineQueryResultDocument.id_, @@ -48,29 +48,26 @@ def inline_query_result_document(): class TestInlineQueryResultDocument: - id_ = 'id' - type_ = 'document' - document_url = 'document url' - title = 'title' - caption = 'caption' - parse_mode = 'Markdown' + id_ = "id" + type_ = "document" + document_url = "document url" + title = "title" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - mime_type = 'mime type' - description = 'description' - thumb_url = 'thumb url' + mime_type = "mime type" + description = "description" + thumb_url = "thumb url" thumb_width = 10 thumb_height = 15 - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_document, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_document, mro_slots): inst = inline_query_result_document for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_document): assert inline_query_result_document.id == self.id_ @@ -95,56 +92,56 @@ def test_to_dict(self, inline_query_result_document): inline_query_result_document_dict = inline_query_result_document.to_dict() assert isinstance(inline_query_result_document_dict, dict) - assert inline_query_result_document_dict['id'] == inline_query_result_document.id - assert inline_query_result_document_dict['type'] == inline_query_result_document.type + assert inline_query_result_document_dict["id"] == inline_query_result_document.id + assert inline_query_result_document_dict["type"] == inline_query_result_document.type assert ( - inline_query_result_document_dict['document_url'] + inline_query_result_document_dict["document_url"] == inline_query_result_document.document_url ) - assert inline_query_result_document_dict['title'] == inline_query_result_document.title - assert inline_query_result_document_dict['caption'] == inline_query_result_document.caption + assert inline_query_result_document_dict["title"] == inline_query_result_document.title + assert inline_query_result_document_dict["caption"] == inline_query_result_document.caption assert ( - inline_query_result_document_dict['parse_mode'] + inline_query_result_document_dict["parse_mode"] == inline_query_result_document.parse_mode ) - assert inline_query_result_document_dict['caption_entities'] == [ + assert inline_query_result_document_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_document.caption_entities ] assert ( - inline_query_result_document_dict['mime_type'] + inline_query_result_document_dict["mime_type"] == inline_query_result_document.mime_type ) assert ( - inline_query_result_document_dict['description'] + inline_query_result_document_dict["description"] == inline_query_result_document.description ) assert ( - inline_query_result_document_dict['thumb_url'] + inline_query_result_document_dict["thumb_url"] == inline_query_result_document.thumb_url ) assert ( - inline_query_result_document_dict['thumb_width'] + inline_query_result_document_dict["thumb_width"] == inline_query_result_document.thumb_width ) assert ( - inline_query_result_document_dict['thumb_height'] + inline_query_result_document_dict["thumb_height"] == inline_query_result_document.thumb_height ) assert ( - inline_query_result_document_dict['input_message_content'] + inline_query_result_document_dict["input_message_content"] == inline_query_result_document.input_message_content.to_dict() ) assert ( - inline_query_result_document_dict['reply_markup'] + inline_query_result_document_dict["reply_markup"] == inline_query_result_document.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultDocument(self.id_, self.document_url, self.title, self.mime_type) b = InlineQueryResultDocument(self.id_, self.document_url, self.title, self.mime_type) - c = InlineQueryResultDocument(self.id_, '', self.title, self.mime_type) - d = InlineQueryResultDocument('', self.document_url, self.title, self.mime_type) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultDocument(self.id_, "", self.title, self.mime_type) + d = InlineQueryResultDocument("", self.document_url, self.title, self.mime_type) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultgame.py b/tests/test_inlinequeryresultgame.py index 11fe9528015..820c37ad330 100644 --- a/tests/test_inlinequeryresultgame.py +++ b/tests/test_inlinequeryresultgame.py @@ -26,7 +26,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_game(): return InlineQueryResultGame( TestInlineQueryResultGame.id_, @@ -36,19 +36,16 @@ def inline_query_result_game(): class TestInlineQueryResultGame: - id_ = 'id' - type_ = 'game' - game_short_name = 'game short name' - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + id_ = "id" + type_ = "game" + game_short_name = "game short name" + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_game, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_game, mro_slots): inst = inline_query_result_game for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_game): assert inline_query_result_game.type == self.type_ @@ -60,23 +57,23 @@ def test_to_dict(self, inline_query_result_game): inline_query_result_game_dict = inline_query_result_game.to_dict() assert isinstance(inline_query_result_game_dict, dict) - assert inline_query_result_game_dict['type'] == inline_query_result_game.type - assert inline_query_result_game_dict['id'] == inline_query_result_game.id + assert inline_query_result_game_dict["type"] == inline_query_result_game.type + assert inline_query_result_game_dict["id"] == inline_query_result_game.id assert ( - inline_query_result_game_dict['game_short_name'] + inline_query_result_game_dict["game_short_name"] == inline_query_result_game.game_short_name ) assert ( - inline_query_result_game_dict['reply_markup'] + inline_query_result_game_dict["reply_markup"] == inline_query_result_game.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultGame(self.id_, self.game_short_name) b = InlineQueryResultGame(self.id_, self.game_short_name) - c = InlineQueryResultGame(self.id_, '') - d = InlineQueryResultGame('', self.game_short_name) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultGame(self.id_, "") + d = InlineQueryResultGame("", self.game_short_name) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultgif.py b/tests/test_inlinequeryresultgif.py index a5e25168547..0a6249a20ce 100644 --- a/tests/test_inlinequeryresultgif.py +++ b/tests/test_inlinequeryresultgif.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_gif(): return InlineQueryResultGif( TestInlineQueryResultGif.id_, @@ -48,29 +48,26 @@ def inline_query_result_gif(): class TestInlineQueryResultGif: - id_ = 'id' - type_ = 'gif' - gif_url = 'gif url' + id_ = "id" + type_ = "gif" + gif_url = "gif url" gif_width = 10 gif_height = 15 gif_duration = 1 - thumb_url = 'thumb url' - thumb_mime_type = 'image/jpeg' - title = 'title' - caption = 'caption' - parse_mode = 'HTML' + thumb_url = "thumb url" + thumb_mime_type = "image/jpeg" + title = "title" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_gif, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_gif, mro_slots): inst = inline_query_result_gif for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_gif): assert inline_query_result_gif.type == self.type_ @@ -95,38 +92,38 @@ def test_to_dict(self, inline_query_result_gif): inline_query_result_gif_dict = inline_query_result_gif.to_dict() assert isinstance(inline_query_result_gif_dict, dict) - assert inline_query_result_gif_dict['type'] == inline_query_result_gif.type - assert inline_query_result_gif_dict['id'] == inline_query_result_gif.id - assert inline_query_result_gif_dict['gif_url'] == inline_query_result_gif.gif_url - assert inline_query_result_gif_dict['gif_width'] == inline_query_result_gif.gif_width - assert inline_query_result_gif_dict['gif_height'] == inline_query_result_gif.gif_height - assert inline_query_result_gif_dict['gif_duration'] == inline_query_result_gif.gif_duration - assert inline_query_result_gif_dict['thumb_url'] == inline_query_result_gif.thumb_url + assert inline_query_result_gif_dict["type"] == inline_query_result_gif.type + assert inline_query_result_gif_dict["id"] == inline_query_result_gif.id + assert inline_query_result_gif_dict["gif_url"] == inline_query_result_gif.gif_url + assert inline_query_result_gif_dict["gif_width"] == inline_query_result_gif.gif_width + assert inline_query_result_gif_dict["gif_height"] == inline_query_result_gif.gif_height + assert inline_query_result_gif_dict["gif_duration"] == inline_query_result_gif.gif_duration + assert inline_query_result_gif_dict["thumb_url"] == inline_query_result_gif.thumb_url assert ( - inline_query_result_gif_dict['thumb_mime_type'] + inline_query_result_gif_dict["thumb_mime_type"] == inline_query_result_gif.thumb_mime_type ) - assert inline_query_result_gif_dict['title'] == inline_query_result_gif.title - assert inline_query_result_gif_dict['caption'] == inline_query_result_gif.caption - assert inline_query_result_gif_dict['parse_mode'] == inline_query_result_gif.parse_mode - assert inline_query_result_gif_dict['caption_entities'] == [ + assert inline_query_result_gif_dict["title"] == inline_query_result_gif.title + assert inline_query_result_gif_dict["caption"] == inline_query_result_gif.caption + assert inline_query_result_gif_dict["parse_mode"] == inline_query_result_gif.parse_mode + assert inline_query_result_gif_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_gif.caption_entities ] assert ( - inline_query_result_gif_dict['input_message_content'] + inline_query_result_gif_dict["input_message_content"] == inline_query_result_gif.input_message_content.to_dict() ) assert ( - inline_query_result_gif_dict['reply_markup'] + inline_query_result_gif_dict["reply_markup"] == inline_query_result_gif.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultGif(self.id_, self.gif_url, self.thumb_url) b = InlineQueryResultGif(self.id_, self.gif_url, self.thumb_url) - c = InlineQueryResultGif(self.id_, '', self.thumb_url) - d = InlineQueryResultGif('', self.gif_url, self.thumb_url) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultGif(self.id_, "", self.thumb_url) + d = InlineQueryResultGif("", self.gif_url, self.thumb_url) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultlocation.py b/tests/test_inlinequeryresultlocation.py index 5b4142eee23..ccf25b8638e 100644 --- a/tests/test_inlinequeryresultlocation.py +++ b/tests/test_inlinequeryresultlocation.py @@ -27,7 +27,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_location(): return InlineQueryResultLocation( TestInlineQueryResultLocation.id_, @@ -47,29 +47,26 @@ def inline_query_result_location(): class TestInlineQueryResultLocation: - id_ = 'id' - type_ = 'location' + id_ = "id" + type_ = "location" latitude = 0.0 longitude = 1.0 - title = 'title' + title = "title" horizontal_accuracy = 999 live_period = 70 heading = 90 proximity_alert_radius = 1000 - thumb_url = 'thumb url' + thumb_url = "thumb url" thumb_width = 10 thumb_height = 15 - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_location, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_location, mro_slots): inst = inline_query_result_location for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_location): assert inline_query_result_location.id == self.id_ @@ -94,47 +91,47 @@ def test_to_dict(self, inline_query_result_location): inline_query_result_location_dict = inline_query_result_location.to_dict() assert isinstance(inline_query_result_location_dict, dict) - assert inline_query_result_location_dict['id'] == inline_query_result_location.id - assert inline_query_result_location_dict['type'] == inline_query_result_location.type + assert inline_query_result_location_dict["id"] == inline_query_result_location.id + assert inline_query_result_location_dict["type"] == inline_query_result_location.type assert ( - inline_query_result_location_dict['latitude'] == inline_query_result_location.latitude + inline_query_result_location_dict["latitude"] == inline_query_result_location.latitude ) assert ( - inline_query_result_location_dict['longitude'] + inline_query_result_location_dict["longitude"] == inline_query_result_location.longitude ) - assert inline_query_result_location_dict['title'] == inline_query_result_location.title + assert inline_query_result_location_dict["title"] == inline_query_result_location.title assert ( - inline_query_result_location_dict['live_period'] + inline_query_result_location_dict["live_period"] == inline_query_result_location.live_period ) assert ( - inline_query_result_location_dict['thumb_url'] + inline_query_result_location_dict["thumb_url"] == inline_query_result_location.thumb_url ) assert ( - inline_query_result_location_dict['thumb_width'] + inline_query_result_location_dict["thumb_width"] == inline_query_result_location.thumb_width ) assert ( - inline_query_result_location_dict['thumb_height'] + inline_query_result_location_dict["thumb_height"] == inline_query_result_location.thumb_height ) assert ( - inline_query_result_location_dict['input_message_content'] + inline_query_result_location_dict["input_message_content"] == inline_query_result_location.input_message_content.to_dict() ) assert ( - inline_query_result_location_dict['reply_markup'] + inline_query_result_location_dict["reply_markup"] == inline_query_result_location.reply_markup.to_dict() ) assert ( - inline_query_result_location_dict['horizontal_accuracy'] + inline_query_result_location_dict["horizontal_accuracy"] == inline_query_result_location.horizontal_accuracy ) - assert inline_query_result_location_dict['heading'] == inline_query_result_location.heading + assert inline_query_result_location_dict["heading"] == inline_query_result_location.heading assert ( - inline_query_result_location_dict['proximity_alert_radius'] + inline_query_result_location_dict["proximity_alert_radius"] == inline_query_result_location.proximity_alert_radius ) @@ -142,8 +139,8 @@ def test_equality(self): a = InlineQueryResultLocation(self.id_, self.longitude, self.latitude, self.title) b = InlineQueryResultLocation(self.id_, self.longitude, self.latitude, self.title) c = InlineQueryResultLocation(self.id_, 0, self.latitude, self.title) - d = InlineQueryResultLocation('', self.longitude, self.latitude, self.title) - e = InlineQueryResultVoice(self.id_, '', '') + d = InlineQueryResultLocation("", self.longitude, self.latitude, self.title) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultmpeg4gif.py b/tests/test_inlinequeryresultmpeg4gif.py index cd5d2ec3b0c..89dd7bc22b7 100644 --- a/tests/test_inlinequeryresultmpeg4gif.py +++ b/tests/test_inlinequeryresultmpeg4gif.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_mpeg4_gif(): return InlineQueryResultMpeg4Gif( TestInlineQueryResultMpeg4Gif.id_, @@ -48,29 +48,26 @@ def inline_query_result_mpeg4_gif(): class TestInlineQueryResultMpeg4Gif: - id_ = 'id' - type_ = 'mpeg4_gif' - mpeg4_url = 'mpeg4 url' + id_ = "id" + type_ = "mpeg4_gif" + mpeg4_url = "mpeg4 url" mpeg4_width = 10 mpeg4_height = 15 mpeg4_duration = 1 - thumb_url = 'thumb url' - thumb_mime_type = 'image/jpeg' - title = 'title' - caption = 'caption' - parse_mode = 'Markdown' + thumb_url = "thumb url" + thumb_mime_type = "image/jpeg" + title = "title" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_mpeg4_gif, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_mpeg4_gif, mro_slots): inst = inline_query_result_mpeg4_gif for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_mpeg4_gif): assert inline_query_result_mpeg4_gif.type == self.type_ @@ -95,58 +92,58 @@ def test_to_dict(self, inline_query_result_mpeg4_gif): inline_query_result_mpeg4_gif_dict = inline_query_result_mpeg4_gif.to_dict() assert isinstance(inline_query_result_mpeg4_gif_dict, dict) - assert inline_query_result_mpeg4_gif_dict['type'] == inline_query_result_mpeg4_gif.type - assert inline_query_result_mpeg4_gif_dict['id'] == inline_query_result_mpeg4_gif.id + assert inline_query_result_mpeg4_gif_dict["type"] == inline_query_result_mpeg4_gif.type + assert inline_query_result_mpeg4_gif_dict["id"] == inline_query_result_mpeg4_gif.id assert ( - inline_query_result_mpeg4_gif_dict['mpeg4_url'] + inline_query_result_mpeg4_gif_dict["mpeg4_url"] == inline_query_result_mpeg4_gif.mpeg4_url ) assert ( - inline_query_result_mpeg4_gif_dict['mpeg4_width'] + inline_query_result_mpeg4_gif_dict["mpeg4_width"] == inline_query_result_mpeg4_gif.mpeg4_width ) assert ( - inline_query_result_mpeg4_gif_dict['mpeg4_height'] + inline_query_result_mpeg4_gif_dict["mpeg4_height"] == inline_query_result_mpeg4_gif.mpeg4_height ) assert ( - inline_query_result_mpeg4_gif_dict['mpeg4_duration'] + inline_query_result_mpeg4_gif_dict["mpeg4_duration"] == inline_query_result_mpeg4_gif.mpeg4_duration ) assert ( - inline_query_result_mpeg4_gif_dict['thumb_url'] + inline_query_result_mpeg4_gif_dict["thumb_url"] == inline_query_result_mpeg4_gif.thumb_url ) assert ( - inline_query_result_mpeg4_gif_dict['thumb_mime_type'] + inline_query_result_mpeg4_gif_dict["thumb_mime_type"] == inline_query_result_mpeg4_gif.thumb_mime_type ) - assert inline_query_result_mpeg4_gif_dict['title'] == inline_query_result_mpeg4_gif.title + assert inline_query_result_mpeg4_gif_dict["title"] == inline_query_result_mpeg4_gif.title assert ( - inline_query_result_mpeg4_gif_dict['caption'] == inline_query_result_mpeg4_gif.caption + inline_query_result_mpeg4_gif_dict["caption"] == inline_query_result_mpeg4_gif.caption ) assert ( - inline_query_result_mpeg4_gif_dict['parse_mode'] + inline_query_result_mpeg4_gif_dict["parse_mode"] == inline_query_result_mpeg4_gif.parse_mode ) - assert inline_query_result_mpeg4_gif_dict['caption_entities'] == [ + assert inline_query_result_mpeg4_gif_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_mpeg4_gif.caption_entities ] assert ( - inline_query_result_mpeg4_gif_dict['input_message_content'] + inline_query_result_mpeg4_gif_dict["input_message_content"] == inline_query_result_mpeg4_gif.input_message_content.to_dict() ) assert ( - inline_query_result_mpeg4_gif_dict['reply_markup'] + inline_query_result_mpeg4_gif_dict["reply_markup"] == inline_query_result_mpeg4_gif.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumb_url) b = InlineQueryResultMpeg4Gif(self.id_, self.mpeg4_url, self.thumb_url) - c = InlineQueryResultMpeg4Gif(self.id_, '', self.thumb_url) - d = InlineQueryResultMpeg4Gif('', self.mpeg4_url, self.thumb_url) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultMpeg4Gif(self.id_, "", self.thumb_url) + d = InlineQueryResultMpeg4Gif("", self.mpeg4_url, self.thumb_url) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultphoto.py b/tests/test_inlinequeryresultphoto.py index 5fd21bd63ef..5fa37705ea3 100644 --- a/tests/test_inlinequeryresultphoto.py +++ b/tests/test_inlinequeryresultphoto.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_photo(): return InlineQueryResultPhoto( TestInlineQueryResultPhoto.id_, @@ -47,29 +47,26 @@ def inline_query_result_photo(): class TestInlineQueryResultPhoto: - id_ = 'id' - type_ = 'photo' - photo_url = 'photo url' + id_ = "id" + type_ = "photo" + photo_url = "photo url" photo_width = 10 photo_height = 15 - thumb_url = 'thumb url' - title = 'title' - description = 'description' - caption = 'caption' - parse_mode = 'HTML' + thumb_url = "thumb url" + title = "title" + description = "description" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_photo, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_photo, mro_slots): inst = inline_query_result_photo for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_photo): assert inline_query_result_photo.type == self.type_ @@ -93,41 +90,41 @@ def test_to_dict(self, inline_query_result_photo): inline_query_result_photo_dict = inline_query_result_photo.to_dict() assert isinstance(inline_query_result_photo_dict, dict) - assert inline_query_result_photo_dict['type'] == inline_query_result_photo.type - assert inline_query_result_photo_dict['id'] == inline_query_result_photo.id - assert inline_query_result_photo_dict['photo_url'] == inline_query_result_photo.photo_url + assert inline_query_result_photo_dict["type"] == inline_query_result_photo.type + assert inline_query_result_photo_dict["id"] == inline_query_result_photo.id + assert inline_query_result_photo_dict["photo_url"] == inline_query_result_photo.photo_url assert ( - inline_query_result_photo_dict['photo_width'] == inline_query_result_photo.photo_width + inline_query_result_photo_dict["photo_width"] == inline_query_result_photo.photo_width ) assert ( - inline_query_result_photo_dict['photo_height'] + inline_query_result_photo_dict["photo_height"] == inline_query_result_photo.photo_height ) - assert inline_query_result_photo_dict['thumb_url'] == inline_query_result_photo.thumb_url - assert inline_query_result_photo_dict['title'] == inline_query_result_photo.title + assert inline_query_result_photo_dict["thumb_url"] == inline_query_result_photo.thumb_url + assert inline_query_result_photo_dict["title"] == inline_query_result_photo.title assert ( - inline_query_result_photo_dict['description'] == inline_query_result_photo.description + inline_query_result_photo_dict["description"] == inline_query_result_photo.description ) - assert inline_query_result_photo_dict['caption'] == inline_query_result_photo.caption - assert inline_query_result_photo_dict['parse_mode'] == inline_query_result_photo.parse_mode - assert inline_query_result_photo_dict['caption_entities'] == [ + assert inline_query_result_photo_dict["caption"] == inline_query_result_photo.caption + assert inline_query_result_photo_dict["parse_mode"] == inline_query_result_photo.parse_mode + assert inline_query_result_photo_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_photo.caption_entities ] assert ( - inline_query_result_photo_dict['input_message_content'] + inline_query_result_photo_dict["input_message_content"] == inline_query_result_photo.input_message_content.to_dict() ) assert ( - inline_query_result_photo_dict['reply_markup'] + inline_query_result_photo_dict["reply_markup"] == inline_query_result_photo.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumb_url) b = InlineQueryResultPhoto(self.id_, self.photo_url, self.thumb_url) - c = InlineQueryResultPhoto(self.id_, '', self.thumb_url) - d = InlineQueryResultPhoto('', self.photo_url, self.thumb_url) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultPhoto(self.id_, "", self.thumb_url) + d = InlineQueryResultPhoto("", self.photo_url, self.thumb_url) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultvenue.py b/tests/test_inlinequeryresultvenue.py index b6144657091..21c6606d1ff 100644 --- a/tests/test_inlinequeryresultvenue.py +++ b/tests/test_inlinequeryresultvenue.py @@ -27,7 +27,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_venue(): return InlineQueryResultVenue( TestInlineQueryResultVenue.id_, @@ -48,30 +48,27 @@ def inline_query_result_venue(): class TestInlineQueryResultVenue: - id_ = 'id' - type_ = 'venue' - latitude = 'latitude' - longitude = 'longitude' - title = 'title' - address = 'address' - foursquare_id = 'foursquare id' - foursquare_type = 'foursquare type' - google_place_id = 'google place id' - google_place_type = 'google place type' - thumb_url = 'thumb url' + id_ = "id" + type_ = "venue" + latitude = "latitude" + longitude = "longitude" + title = "title" + address = "address" + foursquare_id = "foursquare id" + foursquare_type = "foursquare type" + google_place_id = "google place id" + google_place_type = "google place type" + thumb_url = "thumb url" thumb_width = 10 thumb_height = 15 - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_venue, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_venue, mro_slots): inst = inline_query_result_venue for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_venue): assert inline_query_result_venue.id == self.id_ @@ -97,42 +94,42 @@ def test_to_dict(self, inline_query_result_venue): inline_query_result_venue_dict = inline_query_result_venue.to_dict() assert isinstance(inline_query_result_venue_dict, dict) - assert inline_query_result_venue_dict['id'] == inline_query_result_venue.id - assert inline_query_result_venue_dict['type'] == inline_query_result_venue.type - assert inline_query_result_venue_dict['latitude'] == inline_query_result_venue.latitude - assert inline_query_result_venue_dict['longitude'] == inline_query_result_venue.longitude - assert inline_query_result_venue_dict['title'] == inline_query_result_venue.title - assert inline_query_result_venue_dict['address'] == inline_query_result_venue.address + assert inline_query_result_venue_dict["id"] == inline_query_result_venue.id + assert inline_query_result_venue_dict["type"] == inline_query_result_venue.type + assert inline_query_result_venue_dict["latitude"] == inline_query_result_venue.latitude + assert inline_query_result_venue_dict["longitude"] == inline_query_result_venue.longitude + assert inline_query_result_venue_dict["title"] == inline_query_result_venue.title + assert inline_query_result_venue_dict["address"] == inline_query_result_venue.address assert ( - inline_query_result_venue_dict['foursquare_id'] + inline_query_result_venue_dict["foursquare_id"] == inline_query_result_venue.foursquare_id ) assert ( - inline_query_result_venue_dict['foursquare_type'] + inline_query_result_venue_dict["foursquare_type"] == inline_query_result_venue.foursquare_type ) assert ( - inline_query_result_venue_dict['google_place_id'] + inline_query_result_venue_dict["google_place_id"] == inline_query_result_venue.google_place_id ) assert ( - inline_query_result_venue_dict['google_place_type'] + inline_query_result_venue_dict["google_place_type"] == inline_query_result_venue.google_place_type ) - assert inline_query_result_venue_dict['thumb_url'] == inline_query_result_venue.thumb_url + assert inline_query_result_venue_dict["thumb_url"] == inline_query_result_venue.thumb_url assert ( - inline_query_result_venue_dict['thumb_width'] == inline_query_result_venue.thumb_width + inline_query_result_venue_dict["thumb_width"] == inline_query_result_venue.thumb_width ) assert ( - inline_query_result_venue_dict['thumb_height'] + inline_query_result_venue_dict["thumb_height"] == inline_query_result_venue.thumb_height ) assert ( - inline_query_result_venue_dict['input_message_content'] + inline_query_result_venue_dict["input_message_content"] == inline_query_result_venue.input_message_content.to_dict() ) assert ( - inline_query_result_venue_dict['reply_markup'] + inline_query_result_venue_dict["reply_markup"] == inline_query_result_venue.reply_markup.to_dict() ) @@ -143,9 +140,9 @@ def test_equality(self): b = InlineQueryResultVenue( self.id_, self.longitude, self.latitude, self.title, self.address ) - c = InlineQueryResultVenue(self.id_, '', self.latitude, self.title, self.address) - d = InlineQueryResultVenue('', self.longitude, self.latitude, self.title, self.address) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultVenue(self.id_, "", self.latitude, self.title, self.address) + d = InlineQueryResultVenue("", self.longitude, self.latitude, self.title, self.address) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultvideo.py b/tests/test_inlinequeryresultvideo.py index 5e9442a1c2f..264162891b0 100644 --- a/tests/test_inlinequeryresultvideo.py +++ b/tests/test_inlinequeryresultvideo.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_video(): return InlineQueryResultVideo( TestInlineQueryResultVideo.id_, @@ -49,30 +49,27 @@ def inline_query_result_video(): class TestInlineQueryResultVideo: - id_ = 'id' - type_ = 'video' - video_url = 'video url' - mime_type = 'mime type' + id_ = "id" + type_ = "video" + video_url = "video url" + mime_type = "mime type" video_width = 10 video_height = 15 video_duration = 15 - thumb_url = 'thumb url' - title = 'title' - caption = 'caption' - parse_mode = 'Markdown' + thumb_url = "thumb url" + title = "title" + caption = "caption" + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - description = 'description' - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + description = "description" + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_video, recwarn, mro_slots): + def test_slot_behaviour(self, inline_query_result_video, mro_slots): inst = inline_query_result_video for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_video): assert inline_query_result_video.type == self.type_ @@ -98,37 +95,37 @@ def test_to_dict(self, inline_query_result_video): inline_query_result_video_dict = inline_query_result_video.to_dict() assert isinstance(inline_query_result_video_dict, dict) - assert inline_query_result_video_dict['type'] == inline_query_result_video.type - assert inline_query_result_video_dict['id'] == inline_query_result_video.id - assert inline_query_result_video_dict['video_url'] == inline_query_result_video.video_url - assert inline_query_result_video_dict['mime_type'] == inline_query_result_video.mime_type + assert inline_query_result_video_dict["type"] == inline_query_result_video.type + assert inline_query_result_video_dict["id"] == inline_query_result_video.id + assert inline_query_result_video_dict["video_url"] == inline_query_result_video.video_url + assert inline_query_result_video_dict["mime_type"] == inline_query_result_video.mime_type assert ( - inline_query_result_video_dict['video_width'] == inline_query_result_video.video_width + inline_query_result_video_dict["video_width"] == inline_query_result_video.video_width ) assert ( - inline_query_result_video_dict['video_height'] + inline_query_result_video_dict["video_height"] == inline_query_result_video.video_height ) assert ( - inline_query_result_video_dict['video_duration'] + inline_query_result_video_dict["video_duration"] == inline_query_result_video.video_duration ) - assert inline_query_result_video_dict['thumb_url'] == inline_query_result_video.thumb_url - assert inline_query_result_video_dict['title'] == inline_query_result_video.title + assert inline_query_result_video_dict["thumb_url"] == inline_query_result_video.thumb_url + assert inline_query_result_video_dict["title"] == inline_query_result_video.title assert ( - inline_query_result_video_dict['description'] == inline_query_result_video.description + inline_query_result_video_dict["description"] == inline_query_result_video.description ) - assert inline_query_result_video_dict['caption'] == inline_query_result_video.caption - assert inline_query_result_video_dict['parse_mode'] == inline_query_result_video.parse_mode - assert inline_query_result_video_dict['caption_entities'] == [ + assert inline_query_result_video_dict["caption"] == inline_query_result_video.caption + assert inline_query_result_video_dict["parse_mode"] == inline_query_result_video.parse_mode + assert inline_query_result_video_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_video.caption_entities ] assert ( - inline_query_result_video_dict['input_message_content'] + inline_query_result_video_dict["input_message_content"] == inline_query_result_video.input_message_content.to_dict() ) assert ( - inline_query_result_video_dict['reply_markup'] + inline_query_result_video_dict["reply_markup"] == inline_query_result_video.reply_markup.to_dict() ) @@ -139,9 +136,9 @@ def test_equality(self): b = InlineQueryResultVideo( self.id_, self.video_url, self.mime_type, self.thumb_url, self.title ) - c = InlineQueryResultVideo(self.id_, '', self.mime_type, self.thumb_url, self.title) - d = InlineQueryResultVideo('', self.video_url, self.mime_type, self.thumb_url, self.title) - e = InlineQueryResultVoice(self.id_, '', '') + c = InlineQueryResultVideo(self.id_, "", self.mime_type, self.thumb_url, self.title) + d = InlineQueryResultVideo("", self.video_url, self.mime_type, self.thumb_url, self.title) + e = InlineQueryResultVoice(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inlinequeryresultvoice.py b/tests/test_inlinequeryresultvoice.py index ae86a48fb74..6559da5abf0 100644 --- a/tests/test_inlinequeryresultvoice.py +++ b/tests/test_inlinequeryresultvoice.py @@ -28,7 +28,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def inline_query_result_voice(): return InlineQueryResultVoice( type=TestInlineQueryResultVoice.type_, @@ -45,25 +45,22 @@ def inline_query_result_voice(): class TestInlineQueryResultVoice: - id_ = 'id' - type_ = 'voice' - voice_url = 'voice url' - title = 'title' - voice_duration = 'voice_duration' - caption = 'caption' - parse_mode = 'HTML' + id_ = "id" + type_ = "voice" + voice_url = "voice url" + title = "title" + voice_duration = "voice_duration" + caption = "caption" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] - input_message_content = InputTextMessageContent('input_message_content') - reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton('reply_markup')]]) + input_message_content = InputTextMessageContent("input_message_content") + reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]]) - def test_slot_behaviour(self, inline_query_result_voice, mro_slots, recwarn): + def test_slot_behaviour(self, inline_query_result_voice, mro_slots): inst = inline_query_result_voice for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, inline_query_result_voice): assert inline_query_result_voice.type == self.type_ @@ -84,34 +81,34 @@ def test_to_dict(self, inline_query_result_voice): inline_query_result_voice_dict = inline_query_result_voice.to_dict() assert isinstance(inline_query_result_voice_dict, dict) - assert inline_query_result_voice_dict['type'] == inline_query_result_voice.type - assert inline_query_result_voice_dict['id'] == inline_query_result_voice.id - assert inline_query_result_voice_dict['voice_url'] == inline_query_result_voice.voice_url - assert inline_query_result_voice_dict['title'] == inline_query_result_voice.title + assert inline_query_result_voice_dict["type"] == inline_query_result_voice.type + assert inline_query_result_voice_dict["id"] == inline_query_result_voice.id + assert inline_query_result_voice_dict["voice_url"] == inline_query_result_voice.voice_url + assert inline_query_result_voice_dict["title"] == inline_query_result_voice.title assert ( - inline_query_result_voice_dict['voice_duration'] + inline_query_result_voice_dict["voice_duration"] == inline_query_result_voice.voice_duration ) - assert inline_query_result_voice_dict['caption'] == inline_query_result_voice.caption - assert inline_query_result_voice_dict['parse_mode'] == inline_query_result_voice.parse_mode - assert inline_query_result_voice_dict['caption_entities'] == [ + assert inline_query_result_voice_dict["caption"] == inline_query_result_voice.caption + assert inline_query_result_voice_dict["parse_mode"] == inline_query_result_voice.parse_mode + assert inline_query_result_voice_dict["caption_entities"] == [ ce.to_dict() for ce in inline_query_result_voice.caption_entities ] assert ( - inline_query_result_voice_dict['input_message_content'] + inline_query_result_voice_dict["input_message_content"] == inline_query_result_voice.input_message_content.to_dict() ) assert ( - inline_query_result_voice_dict['reply_markup'] + inline_query_result_voice_dict["reply_markup"] == inline_query_result_voice.reply_markup.to_dict() ) def test_equality(self): a = InlineQueryResultVoice(self.id_, self.voice_url, self.title) b = InlineQueryResultVoice(self.id_, self.voice_url, self.title) - c = InlineQueryResultVoice(self.id_, '', self.title) - d = InlineQueryResultVoice('', self.voice_url, self.title) - e = InlineQueryResultAudio(self.id_, '', '') + c = InlineQueryResultVoice(self.id_, "", self.title) + d = InlineQueryResultVoice("", self.voice_url, self.title) + e = InlineQueryResultAudio(self.id_, "", "") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inputcontactmessagecontent.py b/tests/test_inputcontactmessagecontent.py index b577059a63b..2c2e396b630 100644 --- a/tests/test_inputcontactmessagecontent.py +++ b/tests/test_inputcontactmessagecontent.py @@ -21,7 +21,7 @@ from telegram import InputContactMessageContent, User -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_contact_message_content(): return InputContactMessageContent( TestInputContactMessageContent.phone_number, @@ -31,18 +31,15 @@ def input_contact_message_content(): class TestInputContactMessageContent: - phone_number = 'phone number' - first_name = 'first name' - last_name = 'last name' + phone_number = "phone number" + first_name = "first name" + last_name = "last name" - def test_slot_behaviour(self, input_contact_message_content, mro_slots, recwarn): + def test_slot_behaviour(self, input_contact_message_content, mro_slots): inst = input_contact_message_content for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.first_name = 'should give warning', self.first_name - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_contact_message_content): assert input_contact_message_content.first_name == self.first_name @@ -54,23 +51,23 @@ def test_to_dict(self, input_contact_message_content): assert isinstance(input_contact_message_content_dict, dict) assert ( - input_contact_message_content_dict['phone_number'] + input_contact_message_content_dict["phone_number"] == input_contact_message_content.phone_number ) assert ( - input_contact_message_content_dict['first_name'] + input_contact_message_content_dict["first_name"] == input_contact_message_content.first_name ) assert ( - input_contact_message_content_dict['last_name'] + input_contact_message_content_dict["last_name"] == input_contact_message_content.last_name ) def test_equality(self): - a = InputContactMessageContent('phone', 'first', last_name='last') - b = InputContactMessageContent('phone', 'first_name', vcard='vcard') - c = InputContactMessageContent('phone_number', 'first', vcard='vcard') - d = User(123, 'first', False) + a = InputContactMessageContent("phone", "first", last_name="last") + b = InputContactMessageContent("phone", "first_name", vcard="vcard") + c = InputContactMessageContent("phone_number", "first", vcard="vcard") + d = User(123, "first", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inputfile.py b/tests/test_inputfile.py index 3b0b4ebd24c..25a0fbe0464 100644 --- a/tests/test_inputfile.py +++ b/tests/test_inputfile.py @@ -26,29 +26,26 @@ class TestInputFile: - png = os.path.join('tests', 'data', 'game.png') + png = os.path.join("tests", "data", "game.png") - def test_slot_behaviour(self, recwarn, mro_slots): - inst = InputFile(BytesIO(b'blah'), filename='tg.jpg') + def test_slot_behaviour(self, mro_slots): + inst = InputFile(BytesIO(b"blah"), filename="tg.jpg") for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.filename = 'should give warning', inst.filename - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_subprocess_pipe(self): - if sys.platform == 'win32': - cmd = ['type', self.png] + if sys.platform == "win32": + cmd = ["type", self.png] else: - cmd = ['cat', self.png] + cmd = ["cat", self.png] - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=(sys.platform == 'win32')) + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=(sys.platform == "win32")) in_file = InputFile(proc.stdout) - assert in_file.input_file_content == open(self.png, 'rb').read() - assert in_file.mimetype == 'image/png' - assert in_file.filename == 'image.png' + assert in_file.input_file_content == open(self.png, "rb").read() + assert in_file.mimetype == "image/png" + assert in_file.filename == "image.png" try: proc.kill() @@ -59,78 +56,78 @@ def test_subprocess_pipe(self): def test_mimetypes(self, caplog): # Only test a few to make sure logic works okay - assert InputFile(open('tests/data/telegram.jpg', 'rb')).mimetype == 'image/jpeg' - assert InputFile(open('tests/data/telegram.webp', 'rb')).mimetype == 'image/webp' - assert InputFile(open('tests/data/telegram.mp3', 'rb')).mimetype == 'audio/mpeg' + assert InputFile(open("tests/data/telegram.jpg", "rb")).mimetype == "image/jpeg" + assert InputFile(open("tests/data/telegram.webp", "rb")).mimetype == "image/webp" + assert InputFile(open("tests/data/telegram.mp3", "rb")).mimetype == "audio/mpeg" # Test guess from file - assert InputFile(BytesIO(b'blah'), filename='tg.jpg').mimetype == 'image/jpeg' - assert InputFile(BytesIO(b'blah'), filename='tg.mp3').mimetype == 'audio/mpeg' + assert InputFile(BytesIO(b"blah"), filename="tg.jpg").mimetype == "image/jpeg" + assert InputFile(BytesIO(b"blah"), filename="tg.mp3").mimetype == "audio/mpeg" # Test fallback assert ( - InputFile(BytesIO(b'blah'), filename='tg.notaproperext').mimetype - == 'application/octet-stream' + InputFile(BytesIO(b"blah"), filename="tg.notaproperext").mimetype + == "application/octet-stream" ) - assert InputFile(BytesIO(b'blah')).mimetype == 'application/octet-stream' + assert InputFile(BytesIO(b"blah")).mimetype == "application/octet-stream" # Test string file with caplog.at_level(logging.DEBUG): - assert InputFile(open('tests/data/text_file.txt')).mimetype == 'text/plain' + assert InputFile(open("tests/data/text_file.txt")).mimetype == "text/plain" assert len(caplog.records) == 1 - assert caplog.records[0].getMessage().startswith('Could not parse file content') + assert caplog.records[0].getMessage().startswith("Could not parse file content") def test_filenames(self): - assert InputFile(open('tests/data/telegram.jpg', 'rb')).filename == 'telegram.jpg' - assert InputFile(open('tests/data/telegram.jpg', 'rb'), filename='blah').filename == 'blah' + assert InputFile(open("tests/data/telegram.jpg", "rb")).filename == "telegram.jpg" + assert InputFile(open("tests/data/telegram.jpg", "rb"), filename="blah").filename == "blah" assert ( - InputFile(open('tests/data/telegram.jpg', 'rb'), filename='blah.jpg').filename - == 'blah.jpg' + InputFile(open("tests/data/telegram.jpg", "rb"), filename="blah.jpg").filename + == "blah.jpg" ) - assert InputFile(open('tests/data/telegram', 'rb')).filename == 'telegram' - assert InputFile(open('tests/data/telegram', 'rb'), filename='blah').filename == 'blah' + assert InputFile(open("tests/data/telegram", "rb")).filename == "telegram" + assert InputFile(open("tests/data/telegram", "rb"), filename="blah").filename == "blah" assert ( - InputFile(open('tests/data/telegram', 'rb'), filename='blah.jpg').filename - == 'blah.jpg' + InputFile(open("tests/data/telegram", "rb"), filename="blah.jpg").filename + == "blah.jpg" ) class MockedFileobject: # A open(?, 'rb') without a .name def __init__(self, f): - self.f = open(f, 'rb') + self.f = open(f, "rb") def read(self): return self.f.read() - assert InputFile(MockedFileobject('tests/data/telegram.jpg')).filename == 'image.jpeg' + assert InputFile(MockedFileobject("tests/data/telegram.jpg")).filename == "image.jpeg" assert ( - InputFile(MockedFileobject('tests/data/telegram.jpg'), filename='blah').filename - == 'blah' + InputFile(MockedFileobject("tests/data/telegram.jpg"), filename="blah").filename + == "blah" ) assert ( - InputFile(MockedFileobject('tests/data/telegram.jpg'), filename='blah.jpg').filename - == 'blah.jpg' + InputFile(MockedFileobject("tests/data/telegram.jpg"), filename="blah.jpg").filename + == "blah.jpg" ) assert ( - InputFile(MockedFileobject('tests/data/telegram')).filename - == 'application.octet-stream' + InputFile(MockedFileobject("tests/data/telegram")).filename + == "application.octet-stream" ) assert ( - InputFile(MockedFileobject('tests/data/telegram'), filename='blah').filename == 'blah' + InputFile(MockedFileobject("tests/data/telegram"), filename="blah").filename == "blah" ) assert ( - InputFile(MockedFileobject('tests/data/telegram'), filename='blah.jpg').filename - == 'blah.jpg' + InputFile(MockedFileobject("tests/data/telegram"), filename="blah.jpg").filename + == "blah.jpg" ) def test_send_bytes(self, bot, chat_id): # We test this here and not at the respective test modules because it's not worth # duplicating the test for the different methods - with open('tests/data/text_file.txt', 'rb') as file: + with open("tests/data/text_file.txt", "rb") as file: message = bot.send_document(chat_id, file.read()) out = BytesIO() assert message.document.get_file().download(out=out) out.seek(0) - assert out.read().decode('utf-8') == 'PTB Rocks!' + assert out.read().decode("utf-8") == "PTB Rocks!" diff --git a/tests/test_inputinvoicemessagecontent.py b/tests/test_inputinvoicemessagecontent.py index 40b0ce0be61..7c10e83a12c 100644 --- a/tests/test_inputinvoicemessagecontent.py +++ b/tests/test_inputinvoicemessagecontent.py @@ -26,7 +26,7 @@ ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_invoice_message_content(): return InputInvoiceMessageContent( title=TestInputInvoiceMessageContent.title, @@ -53,19 +53,19 @@ def input_invoice_message_content(): class TestInputInvoiceMessageContent: - title = 'invoice title' - description = 'invoice description' - payload = 'invoice payload' - provider_token = 'provider token' - currency = 'PTBCoin' - prices = [LabeledPrice('label1', 42), LabeledPrice('label2', 314)] + title = "invoice title" + description = "invoice description" + payload = "invoice payload" + provider_token = "provider token" + currency = "PTBCoin" + prices = [LabeledPrice("label1", 42), LabeledPrice("label2", 314)] max_tip_amount = 420 - suggested_tip_amounts = ['314', '256'] - provider_data = 'provider data' - photo_url = 'photo_url' - photo_size = '314' - photo_width = '420' - photo_height = '256' + suggested_tip_amounts = ["314", "256"] + provider_data = "provider data" + photo_url = "photo_url" + photo_size = "314" + photo_width = "420" + photo_height = "256" need_name = True need_phone_number = True need_email = True @@ -74,14 +74,11 @@ class TestInputInvoiceMessageContent: send_email_to_provider = True is_flexible = True - def test_slot_behaviour(self, input_invoice_message_content, recwarn, mro_slots): + def test_slot_behaviour(self, input_invoice_message_content, mro_slots): inst = input_invoice_message_content for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.title = 'should give warning', self.title - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_invoice_message_content): assert input_invoice_message_content.title == self.title @@ -114,79 +111,79 @@ def test_to_dict(self, input_invoice_message_content): input_invoice_message_content_dict = input_invoice_message_content.to_dict() assert isinstance(input_invoice_message_content_dict, dict) - assert input_invoice_message_content_dict['title'] == input_invoice_message_content.title + assert input_invoice_message_content_dict["title"] == input_invoice_message_content.title assert ( - input_invoice_message_content_dict['description'] + input_invoice_message_content_dict["description"] == input_invoice_message_content.description ) assert ( - input_invoice_message_content_dict['payload'] == input_invoice_message_content.payload + input_invoice_message_content_dict["payload"] == input_invoice_message_content.payload ) assert ( - input_invoice_message_content_dict['provider_token'] + input_invoice_message_content_dict["provider_token"] == input_invoice_message_content.provider_token ) assert ( - input_invoice_message_content_dict['currency'] + input_invoice_message_content_dict["currency"] == input_invoice_message_content.currency ) - assert input_invoice_message_content_dict['prices'] == [ + assert input_invoice_message_content_dict["prices"] == [ price.to_dict() for price in input_invoice_message_content.prices ] assert ( - input_invoice_message_content_dict['max_tip_amount'] + input_invoice_message_content_dict["max_tip_amount"] == input_invoice_message_content.max_tip_amount ) assert ( - input_invoice_message_content_dict['suggested_tip_amounts'] + input_invoice_message_content_dict["suggested_tip_amounts"] == input_invoice_message_content.suggested_tip_amounts ) assert ( - input_invoice_message_content_dict['provider_data'] + input_invoice_message_content_dict["provider_data"] == input_invoice_message_content.provider_data ) assert ( - input_invoice_message_content_dict['photo_url'] + input_invoice_message_content_dict["photo_url"] == input_invoice_message_content.photo_url ) assert ( - input_invoice_message_content_dict['photo_size'] + input_invoice_message_content_dict["photo_size"] == input_invoice_message_content.photo_size ) assert ( - input_invoice_message_content_dict['photo_width'] + input_invoice_message_content_dict["photo_width"] == input_invoice_message_content.photo_width ) assert ( - input_invoice_message_content_dict['photo_height'] + input_invoice_message_content_dict["photo_height"] == input_invoice_message_content.photo_height ) assert ( - input_invoice_message_content_dict['need_name'] + input_invoice_message_content_dict["need_name"] == input_invoice_message_content.need_name ) assert ( - input_invoice_message_content_dict['need_phone_number'] + input_invoice_message_content_dict["need_phone_number"] == input_invoice_message_content.need_phone_number ) assert ( - input_invoice_message_content_dict['need_email'] + input_invoice_message_content_dict["need_email"] == input_invoice_message_content.need_email ) assert ( - input_invoice_message_content_dict['need_shipping_address'] + input_invoice_message_content_dict["need_shipping_address"] == input_invoice_message_content.need_shipping_address ) assert ( - input_invoice_message_content_dict['send_phone_number_to_provider'] + input_invoice_message_content_dict["send_phone_number_to_provider"] == input_invoice_message_content.send_phone_number_to_provider ) assert ( - input_invoice_message_content_dict['send_email_to_provider'] + input_invoice_message_content_dict["send_email_to_provider"] == input_invoice_message_content.send_email_to_provider ) assert ( - input_invoice_message_content_dict['is_flexible'] + input_invoice_message_content_dict["is_flexible"] == input_invoice_message_content.is_flexible ) @@ -194,26 +191,26 @@ def test_de_json(self, bot): assert InputInvoiceMessageContent.de_json({}, bot=bot) is None json_dict = { - 'title': self.title, - 'description': self.description, - 'payload': self.payload, - 'provider_token': self.provider_token, - 'currency': self.currency, - 'prices': [price.to_dict() for price in self.prices], - 'max_tip_amount': self.max_tip_amount, - 'suggested_tip_amounts': self.suggested_tip_amounts, - 'provider_data': self.provider_data, - 'photo_url': self.photo_url, - 'photo_size': self.photo_size, - 'photo_width': self.photo_width, - 'photo_height': self.photo_height, - 'need_name': self.need_name, - 'need_phone_number': self.need_phone_number, - 'need_email': self.need_email, - 'need_shipping_address': self.need_shipping_address, - 'send_phone_number_to_provider': self.send_phone_number_to_provider, - 'send_email_to_provider': self.send_email_to_provider, - 'is_flexible': self.is_flexible, + "title": self.title, + "description": self.description, + "payload": self.payload, + "provider_token": self.provider_token, + "currency": self.currency, + "prices": [price.to_dict() for price in self.prices], + "max_tip_amount": self.max_tip_amount, + "suggested_tip_amounts": self.suggested_tip_amounts, + "provider_data": self.provider_data, + "photo_url": self.photo_url, + "photo_size": self.photo_size, + "photo_width": self.photo_width, + "photo_height": self.photo_height, + "need_name": self.need_name, + "need_phone_number": self.need_phone_number, + "need_email": self.need_email, + "need_shipping_address": self.need_shipping_address, + "send_phone_number_to_provider": self.send_phone_number_to_provider, + "send_email_to_provider": self.send_email_to_provider, + "is_flexible": self.is_flexible, } input_invoice_message_content = InputInvoiceMessageContent.de_json(json_dict, bot=bot) @@ -261,7 +258,7 @@ def test_equality(self): self.currency, self.prices, max_tip_amount=100, - provider_data='foobar', + provider_data="foobar", ) c = InputInvoiceMessageContent( self.title, @@ -270,17 +267,17 @@ def test_equality(self): self.provider_token, self.currency, # the first prices amount & the second lebal changed - [LabeledPrice('label1', 24), LabeledPrice('label22', 314)], + [LabeledPrice("label1", 24), LabeledPrice("label22", 314)], ) d = InputInvoiceMessageContent( self.title, self.description, - 'different_payload', + "different_payload", self.provider_token, self.currency, self.prices, ) - e = InputTextMessageContent('text') + e = InputTextMessageContent("text") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_inputlocationmessagecontent.py b/tests/test_inputlocationmessagecontent.py index 11f679c04ee..b8168a538ad 100644 --- a/tests/test_inputlocationmessagecontent.py +++ b/tests/test_inputlocationmessagecontent.py @@ -21,7 +21,7 @@ from telegram import InputLocationMessageContent, Location -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_location_message_content(): return InputLocationMessageContent( TestInputLocationMessageContent.latitude, @@ -41,14 +41,11 @@ class TestInputLocationMessageContent: heading = 90 proximity_alert_radius = 999 - def test_slot_behaviour(self, input_location_message_content, mro_slots, recwarn): + def test_slot_behaviour(self, input_location_message_content, mro_slots): inst = input_location_message_content for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.heading = 'should give warning', self.heading - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_location_message_content): assert input_location_message_content.longitude == self.longitude @@ -63,27 +60,27 @@ def test_to_dict(self, input_location_message_content): assert isinstance(input_location_message_content_dict, dict) assert ( - input_location_message_content_dict['latitude'] + input_location_message_content_dict["latitude"] == input_location_message_content.latitude ) assert ( - input_location_message_content_dict['longitude'] + input_location_message_content_dict["longitude"] == input_location_message_content.longitude ) assert ( - input_location_message_content_dict['live_period'] + input_location_message_content_dict["live_period"] == input_location_message_content.live_period ) assert ( - input_location_message_content_dict['horizontal_accuracy'] + input_location_message_content_dict["horizontal_accuracy"] == input_location_message_content.horizontal_accuracy ) assert ( - input_location_message_content_dict['heading'] + input_location_message_content_dict["heading"] == input_location_message_content.heading ) assert ( - input_location_message_content_dict['proximity_alert_radius'] + input_location_message_content_dict["proximity_alert_radius"] == input_location_message_content.proximity_alert_radius ) diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py index a23d9698731..5e2a7158148 100644 --- a/tests/test_inputmedia.py +++ b/tests/test_inputmedia.py @@ -51,7 +51,7 @@ from tests.conftest import expect_bad_request -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_media_video(class_thumb_file): return InputMediaVideo( media=TestInputMediaVideo.media, @@ -66,7 +66,7 @@ def input_media_video(class_thumb_file): ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_media_photo(class_thumb_file): return InputMediaPhoto( media=TestInputMediaPhoto.media, @@ -76,7 +76,7 @@ def input_media_photo(class_thumb_file): ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_media_animation(class_thumb_file): return InputMediaAnimation( media=TestInputMediaAnimation.media, @@ -90,7 +90,7 @@ def input_media_animation(class_thumb_file): ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_media_audio(class_thumb_file): return InputMediaAudio( media=TestInputMediaAudio.media, @@ -104,7 +104,7 @@ def input_media_audio(class_thumb_file): ) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_media_document(class_thumb_file): return InputMediaDocument( media=TestInputMediaDocument.media, @@ -123,18 +123,15 @@ class TestInputMediaVideo: width = 3 height = 4 duration = 5 - parse_mode = 'HTML' + parse_mode = "HTML" supports_streaming = True caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] - def test_slot_behaviour(self, input_media_video, recwarn, mro_slots): + def test_slot_behaviour(self, input_media_video, mro_slots): inst = input_media_video for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_media_video): assert input_media_video.type == self.type_ @@ -150,17 +147,17 @@ def test_expected_values(self, input_media_video): def test_to_dict(self, input_media_video): input_media_video_dict = input_media_video.to_dict() - assert input_media_video_dict['type'] == input_media_video.type - assert input_media_video_dict['media'] == input_media_video.media - assert input_media_video_dict['caption'] == input_media_video.caption - assert input_media_video_dict['width'] == input_media_video.width - assert input_media_video_dict['height'] == input_media_video.height - assert input_media_video_dict['duration'] == input_media_video.duration - assert input_media_video_dict['parse_mode'] == input_media_video.parse_mode - assert input_media_video_dict['caption_entities'] == [ + assert input_media_video_dict["type"] == input_media_video.type + assert input_media_video_dict["media"] == input_media_video.media + assert input_media_video_dict["caption"] == input_media_video.caption + assert input_media_video_dict["width"] == input_media_video.width + assert input_media_video_dict["height"] == input_media_video.height + assert input_media_video_dict["duration"] == input_media_video.duration + assert input_media_video_dict["parse_mode"] == input_media_video.parse_mode + assert input_media_video_dict["caption_entities"] == [ ce.to_dict() for ce in input_media_video.caption_entities ] - assert input_media_video_dict['supports_streaming'] == input_media_video.supports_streaming + assert input_media_video_dict["supports_streaming"] == input_media_video.supports_streaming def test_with_video(self, video): # noqa: F811 # fixture found in test_video @@ -181,27 +178,24 @@ def test_with_video_file(self, video_file): # noqa: F811 def test_with_local_files(self): input_media_video = InputMediaVideo( - 'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg' + "tests/data/telegram.mp4", thumb="tests/data/telegram.jpg" ) - assert input_media_video.media == (Path.cwd() / 'tests/data/telegram.mp4/').as_uri() - assert input_media_video.thumb == (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() + assert input_media_video.media == (Path.cwd() / "tests/data/telegram.mp4/").as_uri() + assert input_media_video.thumb == (Path.cwd() / "tests/data/telegram.jpg/").as_uri() class TestInputMediaPhoto: type_ = "photo" media = "NOTAREALFILEID" caption = "My Caption" - parse_mode = 'Markdown' + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] - def test_slot_behaviour(self, input_media_photo, recwarn, mro_slots): + def test_slot_behaviour(self, input_media_photo, mro_slots): inst = input_media_photo for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_media_photo): assert input_media_photo.type == self.type_ @@ -212,11 +206,11 @@ def test_expected_values(self, input_media_photo): def test_to_dict(self, input_media_photo): input_media_photo_dict = input_media_photo.to_dict() - assert input_media_photo_dict['type'] == input_media_photo.type - assert input_media_photo_dict['media'] == input_media_photo.media - assert input_media_photo_dict['caption'] == input_media_photo.caption - assert input_media_photo_dict['parse_mode'] == input_media_photo.parse_mode - assert input_media_photo_dict['caption_entities'] == [ + assert input_media_photo_dict["type"] == input_media_photo.type + assert input_media_photo_dict["media"] == input_media_photo.media + assert input_media_photo_dict["caption"] == input_media_photo.caption + assert input_media_photo_dict["parse_mode"] == input_media_photo.parse_mode + assert input_media_photo_dict["caption_entities"] == [ ce.to_dict() for ce in input_media_photo.caption_entities ] @@ -235,28 +229,25 @@ def test_with_photo_file(self, photo_file): # noqa: F811 assert input_media_photo.caption == "test 2" def test_with_local_files(self): - input_media_photo = InputMediaPhoto('tests/data/telegram.mp4') - assert input_media_photo.media == (Path.cwd() / 'tests/data/telegram.mp4/').as_uri() + input_media_photo = InputMediaPhoto("tests/data/telegram.mp4") + assert input_media_photo.media == (Path.cwd() / "tests/data/telegram.mp4/").as_uri() class TestInputMediaAnimation: type_ = "animation" media = "NOTAREALFILEID" caption = "My Caption" - parse_mode = 'Markdown' + parse_mode = "Markdown" caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] width = 30 height = 30 duration = 1 - def test_slot_behaviour(self, input_media_animation, recwarn, mro_slots): + def test_slot_behaviour(self, input_media_animation, mro_slots): inst = input_media_animation for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_media_animation): assert input_media_animation.type == self.type_ @@ -268,16 +259,16 @@ def test_expected_values(self, input_media_animation): def test_to_dict(self, input_media_animation): input_media_animation_dict = input_media_animation.to_dict() - assert input_media_animation_dict['type'] == input_media_animation.type - assert input_media_animation_dict['media'] == input_media_animation.media - assert input_media_animation_dict['caption'] == input_media_animation.caption - assert input_media_animation_dict['parse_mode'] == input_media_animation.parse_mode - assert input_media_animation_dict['caption_entities'] == [ + assert input_media_animation_dict["type"] == input_media_animation.type + assert input_media_animation_dict["media"] == input_media_animation.media + assert input_media_animation_dict["caption"] == input_media_animation.caption + assert input_media_animation_dict["parse_mode"] == input_media_animation.parse_mode + assert input_media_animation_dict["caption_entities"] == [ ce.to_dict() for ce in input_media_animation.caption_entities ] - assert input_media_animation_dict['width'] == input_media_animation.width - assert input_media_animation_dict['height'] == input_media_animation.height - assert input_media_animation_dict['duration'] == input_media_animation.duration + assert input_media_animation_dict["width"] == input_media_animation.width + assert input_media_animation_dict["height"] == input_media_animation.height + assert input_media_animation_dict["duration"] == input_media_animation.duration def test_with_animation(self, animation): # noqa: F811 # fixture found in test_animation @@ -295,10 +286,10 @@ def test_with_animation_file(self, animation_file): # noqa: F811 def test_with_local_files(self): input_media_animation = InputMediaAnimation( - 'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg' + "tests/data/telegram.mp4", thumb="tests/data/telegram.jpg" ) - assert input_media_animation.media == (Path.cwd() / 'tests/data/telegram.mp4').as_uri() - assert input_media_animation.thumb == (Path.cwd() / 'tests/data/telegram.jpg').as_uri() + assert input_media_animation.media == (Path.cwd() / "tests/data/telegram.mp4").as_uri() + assert input_media_animation.thumb == (Path.cwd() / "tests/data/telegram.jpg").as_uri() class TestInputMediaAudio: @@ -306,19 +297,16 @@ class TestInputMediaAudio: media = "NOTAREALFILEID" caption = "My Caption" duration = 3 - performer = 'performer' - title = 'title' - parse_mode = 'HTML' + performer = "performer" + title = "title" + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] - def test_slot_behaviour(self, input_media_audio, recwarn, mro_slots): + def test_slot_behaviour(self, input_media_audio, mro_slots): inst = input_media_audio for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_media_audio): assert input_media_audio.type == self.type_ @@ -333,14 +321,14 @@ def test_expected_values(self, input_media_audio): def test_to_dict(self, input_media_audio): input_media_audio_dict = input_media_audio.to_dict() - assert input_media_audio_dict['type'] == input_media_audio.type - assert input_media_audio_dict['media'] == input_media_audio.media - assert input_media_audio_dict['caption'] == input_media_audio.caption - assert input_media_audio_dict['duration'] == input_media_audio.duration - assert input_media_audio_dict['performer'] == input_media_audio.performer - assert input_media_audio_dict['title'] == input_media_audio.title - assert input_media_audio_dict['parse_mode'] == input_media_audio.parse_mode - assert input_media_audio_dict['caption_entities'] == [ + assert input_media_audio_dict["type"] == input_media_audio.type + assert input_media_audio_dict["media"] == input_media_audio.media + assert input_media_audio_dict["caption"] == input_media_audio.caption + assert input_media_audio_dict["duration"] == input_media_audio.duration + assert input_media_audio_dict["performer"] == input_media_audio.performer + assert input_media_audio_dict["title"] == input_media_audio.title + assert input_media_audio_dict["parse_mode"] == input_media_audio.parse_mode + assert input_media_audio_dict["caption_entities"] == [ ce.to_dict() for ce in input_media_audio.caption_entities ] @@ -363,28 +351,25 @@ def test_with_audio_file(self, audio_file): # noqa: F811 def test_with_local_files(self): input_media_audio = InputMediaAudio( - 'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg' + "tests/data/telegram.mp4", thumb="tests/data/telegram.jpg" ) - assert input_media_audio.media == (Path.cwd() / 'tests/data/telegram.mp4/').as_uri() - assert input_media_audio.thumb == (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() + assert input_media_audio.media == (Path.cwd() / "tests/data/telegram.mp4/").as_uri() + assert input_media_audio.thumb == (Path.cwd() / "tests/data/telegram.jpg/").as_uri() class TestInputMediaDocument: type_ = "document" media = "NOTAREALFILEID" caption = "My Caption" - parse_mode = 'HTML' + parse_mode = "HTML" caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)] disable_content_type_detection = True - def test_slot_behaviour(self, input_media_document, recwarn, mro_slots): + def test_slot_behaviour(self, input_media_document, mro_slots): inst = input_media_document for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_media_document): assert input_media_document.type == self.type_ @@ -400,15 +385,15 @@ def test_expected_values(self, input_media_document): def test_to_dict(self, input_media_document): input_media_document_dict = input_media_document.to_dict() - assert input_media_document_dict['type'] == input_media_document.type - assert input_media_document_dict['media'] == input_media_document.media - assert input_media_document_dict['caption'] == input_media_document.caption - assert input_media_document_dict['parse_mode'] == input_media_document.parse_mode - assert input_media_document_dict['caption_entities'] == [ + assert input_media_document_dict["type"] == input_media_document.type + assert input_media_document_dict["media"] == input_media_document.media + assert input_media_document_dict["caption"] == input_media_document.caption + assert input_media_document_dict["parse_mode"] == input_media_document.parse_mode + assert input_media_document_dict["caption_entities"] == [ ce.to_dict() for ce in input_media_document.caption_entities ] assert ( - input_media_document['disable_content_type_detection'] + input_media_document["disable_content_type_detection"] == input_media_document.disable_content_type_detection ) @@ -428,19 +413,19 @@ def test_with_document_file(self, document_file): # noqa: F811 def test_with_local_files(self): input_media_document = InputMediaDocument( - 'tests/data/telegram.mp4', thumb='tests/data/telegram.jpg' + "tests/data/telegram.mp4", thumb="tests/data/telegram.jpg" ) - assert input_media_document.media == (Path.cwd() / 'tests/data/telegram.mp4').as_uri() - assert input_media_document.thumb == (Path.cwd() / 'tests/data/telegram.jpg').as_uri() + assert input_media_document.media == (Path.cwd() / "tests/data/telegram.mp4").as_uri() + assert input_media_document.thumb == (Path.cwd() / "tests/data/telegram.jpg").as_uri() -@pytest.fixture(scope='function') # noqa: F811 +@pytest.fixture(scope="function") # noqa: F811 def media_group(photo, thumb): # noqa: F811 return [ - InputMediaPhoto(photo, caption='*photo* 1', parse_mode='Markdown'), - InputMediaPhoto(thumb, caption='photo 2', parse_mode='HTML'), + InputMediaPhoto(photo, caption="*photo* 1", parse_mode="Markdown"), + InputMediaPhoto(thumb, caption="photo 2", parse_mode="HTML"), InputMediaPhoto( - photo, caption='photo 3', caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)] + photo, caption="photo 3", caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)] ), ] @@ -453,7 +438,7 @@ def test_send_media_group_photo(self, bot, chat_id, media_group): 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(mes.caption == f'photo {idx+1}' for idx, mes in enumerate(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 ) @@ -468,7 +453,7 @@ def test_send_media_group_all_args(self, bot, chat_id, media_group): 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(mes.caption == f'photo {idx+1}' for idx, mes in enumerate(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 ) @@ -485,17 +470,17 @@ def test_send_media_group_custom_filename( monkeypatch, ): def make_assertion(url, data, **kwargs): - result = all(im.media.filename == 'custom_filename' for im in data['media']) + result = all(im.media.filename == "custom_filename" for im in data["media"]) # We are a bit hacky here b/c Bot.send_media_group expects a list of Message-dicts return [Message(0, None, None, text=result).to_dict()] - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) media = [ - InputMediaAnimation(animation_file, filename='custom_filename'), - InputMediaAudio(audio_file, filename='custom_filename'), - InputMediaPhoto(photo_file, filename='custom_filename'), - InputMediaVideo(video_file, filename='custom_filename'), + InputMediaAnimation(animation_file, filename="custom_filename"), + InputMediaAudio(audio_file, filename="custom_filename"), + InputMediaPhoto(photo_file, filename="custom_filename"), + InputMediaVideo(video_file, filename="custom_filename"), ] assert bot.send_media_group(chat_id, media)[0].text is True @@ -504,15 +489,15 @@ def test_send_media_group_with_thumbs( self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811 ): def test(*args, **kwargs): - data = kwargs['fields'] + data = kwargs["fields"] video_check = data[input_video.media.attach] == input_video.media.field_tuple thumb_check = data[input_video.thumb.attach] == input_video.thumb.field_tuple result = video_check and thumb_check raise Exception(f"Test was {'successful' if result else 'failing'}") - monkeypatch.setattr('telegram.utils.request.Request._request_wrapper', test) + monkeypatch.setattr("telegram.utils.request.Request._request_wrapper", test) input_video = InputMediaVideo(video_file, thumb=photo_file) - with pytest.raises(Exception, match='Test was successful'): + with pytest.raises(Exception, match="Test was successful"): bot.send_media_group(chat_id, [input_video, input_video]) @flaky(3, 1) # noqa: F811 @@ -520,7 +505,7 @@ def test_send_media_group_new_files( self, bot, chat_id, video_file, photo_file, animation_file # noqa: F811 ): # noqa: F811 def func(): - with open('tests/data/telegram.jpg', 'rb') as file: + with open("tests/data/telegram.jpg", "rb") as file: return bot.send_media_group( chat_id, [ @@ -531,7 +516,7 @@ def func(): ) messages = expect_bad_request( - func, 'Type of file mismatch', 'Telegram did not accept the file.' + func, "Type of file mismatch", "Telegram did not accept the file." ) assert isinstance(messages, list) @@ -541,18 +526,18 @@ def func(): @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_media_group_default_allow_sending_without_reply( self, default_bot, chat_id, media_group, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: messages = default_bot.send_media_group( @@ -568,7 +553,7 @@ def test_send_media_group_default_allow_sending_without_reply( ) assert [m.reply_to_message is None for m in messages] else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_media_group( chat_id, media_group, reply_to_message_id=reply_to_message.message_id ) @@ -595,22 +580,22 @@ def test_edit_message_media_with_thumb( self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811 ): def test(*args, **kwargs): - data = kwargs['fields'] + data = kwargs["fields"] video_check = data[input_video.media.attach] == input_video.media.field_tuple thumb_check = data[input_video.thumb.attach] == input_video.thumb.field_tuple result = video_check and thumb_check raise Exception(f"Test was {'successful' if result else 'failing'}") - monkeypatch.setattr('telegram.utils.request.Request._request_wrapper', test) + monkeypatch.setattr("telegram.utils.request.Request._request_wrapper", test) input_video = InputMediaVideo(video_file, thumb=photo_file) - with pytest.raises(Exception, match='Test was successful'): + with pytest.raises(Exception, match="Test was successful"): bot.edit_message_media(chat_id=chat_id, message_id=123, media=input_video) @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot', [{'parse_mode': ParseMode.HTML}], indirect=True, ids=['HTML-Bot'] + "default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True, ids=["HTML-Bot"] ) - @pytest.mark.parametrize('media_type', ['animation', 'document', 'audio', 'photo', 'video']) + @pytest.mark.parametrize("media_type", ["animation", "document", "audio", "photo", "video"]) def test_edit_message_media_default_parse_mode( self, chat_id, @@ -622,9 +607,9 @@ def test_edit_message_media_default_parse_mode( photo, # noqa: F811 video, # noqa: F811 ): - html_caption = 'bold italic code' - markdown_caption = '*bold* _italic_ `code`' - test_caption = 'bold italic code' + html_caption = "bold italic code" + markdown_caption = "*bold* _italic_ `code`" + test_caption = "bold italic code" test_entities = [ MessageEntity(MessageEntity.BOLD, 0, 4), MessageEntity(MessageEntity.ITALIC, 5, 6), @@ -634,28 +619,28 @@ def test_edit_message_media_default_parse_mode( def build_media(parse_mode, med_type): kwargs = {} if parse_mode != ParseMode.HTML: - kwargs['parse_mode'] = parse_mode - kwargs['caption'] = markdown_caption + kwargs["parse_mode"] = parse_mode + kwargs["caption"] = markdown_caption else: - kwargs['caption'] = html_caption + kwargs["caption"] = html_caption - if med_type == 'animation': + if med_type == "animation": return InputMediaAnimation(animation, **kwargs) - if med_type == 'document': + if med_type == "document": return InputMediaDocument(document, **kwargs) - if med_type == 'audio': + if med_type == "audio": return InputMediaAudio(audio, **kwargs) - if med_type == 'photo': + if med_type == "photo": return InputMediaPhoto(photo, **kwargs) - if med_type == 'video': + if med_type == "video": return InputMediaVideo(video, **kwargs) message = default_bot.send_photo(chat_id, photo) message = default_bot.edit_message_media( + build_media(parse_mode=ParseMode.HTML, med_type=media_type), message.chat_id, message.message_id, - media=build_media(parse_mode=ParseMode.HTML, med_type=media_type), ) assert message.caption == test_caption assert message.caption_entities == test_entities @@ -664,9 +649,9 @@ def build_media(parse_mode, med_type): message.edit_caption() message = default_bot.edit_message_media( + build_media(parse_mode=ParseMode.MARKDOWN_V2, med_type=media_type), message.chat_id, message.message_id, - media=build_media(parse_mode=ParseMode.MARKDOWN_V2, med_type=media_type), ) assert message.caption == test_caption assert message.caption_entities == test_entities @@ -675,9 +660,9 @@ def build_media(parse_mode, med_type): message.edit_caption() message = default_bot.edit_message_media( + build_media(parse_mode=None, med_type=media_type), message.chat_id, message.message_id, - media=build_media(parse_mode=None, med_type=media_type), ) assert message.caption == markdown_caption assert message.caption_entities == [] diff --git a/tests/test_inputtextmessagecontent.py b/tests/test_inputtextmessagecontent.py index c996d8fe3f9..ebef88ddad4 100644 --- a/tests/test_inputtextmessagecontent.py +++ b/tests/test_inputtextmessagecontent.py @@ -21,7 +21,7 @@ from telegram import InputTextMessageContent, ParseMode, MessageEntity -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_text_message_content(): return InputTextMessageContent( TestInputTextMessageContent.message_text, @@ -32,19 +32,16 @@ def input_text_message_content(): class TestInputTextMessageContent: - message_text = '*message text*' + message_text = "*message text*" parse_mode = ParseMode.MARKDOWN entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)] disable_web_page_preview = True - def test_slot_behaviour(self, input_text_message_content, mro_slots, recwarn): + def test_slot_behaviour(self, input_text_message_content, mro_slots): inst = input_text_message_content for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.message_text = 'should give warning', self.message_text - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_text_message_content): assert input_text_message_content.parse_mode == self.parse_mode @@ -57,24 +54,24 @@ def test_to_dict(self, input_text_message_content): assert isinstance(input_text_message_content_dict, dict) assert ( - input_text_message_content_dict['message_text'] + input_text_message_content_dict["message_text"] == input_text_message_content.message_text ) assert ( - input_text_message_content_dict['parse_mode'] == input_text_message_content.parse_mode + input_text_message_content_dict["parse_mode"] == input_text_message_content.parse_mode ) - assert input_text_message_content_dict['entities'] == [ + assert input_text_message_content_dict["entities"] == [ ce.to_dict() for ce in input_text_message_content.entities ] assert ( - input_text_message_content_dict['disable_web_page_preview'] + input_text_message_content_dict["disable_web_page_preview"] == input_text_message_content.disable_web_page_preview ) def test_equality(self): - a = InputTextMessageContent('text') - b = InputTextMessageContent('text', parse_mode=ParseMode.HTML) - c = InputTextMessageContent('label') + a = InputTextMessageContent("text") + b = InputTextMessageContent("text", parse_mode=ParseMode.HTML) + c = InputTextMessageContent("label") d = ParseMode.HTML assert a == b diff --git a/tests/test_inputvenuemessagecontent.py b/tests/test_inputvenuemessagecontent.py index 1168b91e20c..86711340040 100644 --- a/tests/test_inputvenuemessagecontent.py +++ b/tests/test_inputvenuemessagecontent.py @@ -21,7 +21,7 @@ from telegram import InputVenueMessageContent, Location -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def input_venue_message_content(): return InputVenueMessageContent( TestInputVenueMessageContent.latitude, @@ -38,21 +38,18 @@ def input_venue_message_content(): class TestInputVenueMessageContent: latitude = 1.0 longitude = 2.0 - title = 'title' - address = 'address' - foursquare_id = 'foursquare id' - foursquare_type = 'foursquare type' - google_place_id = 'google place id' - google_place_type = 'google place type' + title = "title" + address = "address" + foursquare_id = "foursquare id" + foursquare_type = "foursquare type" + google_place_id = "google place id" + google_place_type = "google place type" - def test_slot_behaviour(self, input_venue_message_content, recwarn, mro_slots): + def test_slot_behaviour(self, input_venue_message_content, mro_slots): inst = input_venue_message_content for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.title = 'should give warning', self.title - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, input_venue_message_content): assert input_venue_message_content.longitude == self.longitude @@ -68,34 +65,34 @@ def test_to_dict(self, input_venue_message_content): input_venue_message_content_dict = input_venue_message_content.to_dict() assert isinstance(input_venue_message_content_dict, dict) - assert input_venue_message_content_dict['latitude'] == input_venue_message_content.latitude + assert input_venue_message_content_dict["latitude"] == input_venue_message_content.latitude assert ( - input_venue_message_content_dict['longitude'] == input_venue_message_content.longitude + input_venue_message_content_dict["longitude"] == input_venue_message_content.longitude ) - assert input_venue_message_content_dict['title'] == input_venue_message_content.title - assert input_venue_message_content_dict['address'] == input_venue_message_content.address + assert input_venue_message_content_dict["title"] == input_venue_message_content.title + assert input_venue_message_content_dict["address"] == input_venue_message_content.address assert ( - input_venue_message_content_dict['foursquare_id'] + input_venue_message_content_dict["foursquare_id"] == input_venue_message_content.foursquare_id ) assert ( - input_venue_message_content_dict['foursquare_type'] + input_venue_message_content_dict["foursquare_type"] == input_venue_message_content.foursquare_type ) assert ( - input_venue_message_content_dict['google_place_id'] + input_venue_message_content_dict["google_place_id"] == input_venue_message_content.google_place_id ) assert ( - input_venue_message_content_dict['google_place_type'] + input_venue_message_content_dict["google_place_type"] == input_venue_message_content.google_place_type ) def test_equality(self): - a = InputVenueMessageContent(123, 456, 'title', 'address') - b = InputVenueMessageContent(123, 456, 'title', '') - c = InputVenueMessageContent(123, 456, 'title', 'address', foursquare_id=123) - d = InputVenueMessageContent(456, 123, 'title', 'address', foursquare_id=123) + a = InputVenueMessageContent(123, 456, "title", "address") + b = InputVenueMessageContent(123, 456, "title", "") + c = InputVenueMessageContent(123, 456, "title", "address", foursquare_id=123) + d = InputVenueMessageContent(456, 123, "title", "address", foursquare_id=123) e = Location(123, 456) assert a == b diff --git a/tests/test_invoice.py b/tests/test_invoice.py index 92377f40d11..434d11024d7 100644 --- a/tests/test_invoice.py +++ b/tests/test_invoice.py @@ -23,7 +23,7 @@ from telegram.error import BadRequest -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def invoice(): return Invoice( TestInvoice.title, @@ -35,33 +35,30 @@ def invoice(): class TestInvoice: - payload = 'payload' - prices = [LabeledPrice('Fish', 100), LabeledPrice('Fish Tax', 1000)] + payload = "payload" + prices = [LabeledPrice("Fish", 100), LabeledPrice("Fish Tax", 1000)] provider_data = """{"test":"test"}""" - title = 'title' - description = 'description' - start_parameter = 'start_parameter' - currency = 'EUR' + title = "title" + description = "description" + start_parameter = "start_parameter" + currency = "EUR" total_amount = sum(p.amount for p in prices) max_tip_amount = 42 suggested_tip_amounts = [13, 42] - def test_slot_behaviour(self, invoice, mro_slots, recwarn): + def test_slot_behaviour(self, invoice, mro_slots): for attr in invoice.__slots__: - assert getattr(invoice, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not invoice.__dict__, f"got missing slot(s): {invoice.__dict__}" + assert getattr(invoice, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(invoice)) == len(set(mro_slots(invoice))), "duplicate slot" - invoice.custom, invoice.title = 'should give warning', self.title - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): invoice_json = Invoice.de_json( { - 'title': TestInvoice.title, - 'description': TestInvoice.description, - 'start_parameter': TestInvoice.start_parameter, - 'currency': TestInvoice.currency, - 'total_amount': TestInvoice.total_amount, + "title": TestInvoice.title, + "description": TestInvoice.description, + "start_parameter": TestInvoice.start_parameter, + "currency": TestInvoice.currency, + "total_amount": TestInvoice.total_amount, }, bot, ) @@ -76,11 +73,11 @@ def test_to_dict(self, invoice): invoice_dict = invoice.to_dict() assert isinstance(invoice_dict, dict) - assert invoice_dict['title'] == invoice.title - assert invoice_dict['description'] == invoice.description - assert invoice_dict['start_parameter'] == invoice.start_parameter - assert invoice_dict['currency'] == invoice.currency - assert invoice_dict['total_amount'] == invoice.total_amount + assert invoice_dict["title"] == invoice.title + assert invoice_dict["description"] == invoice.description + assert invoice_dict["start_parameter"] == invoice.start_parameter + assert invoice_dict["currency"] == invoice.currency + assert invoice_dict["total_amount"] == invoice.total_amount @flaky(3, 1) def test_send_required_args_only(self, bot, chat_id, provider_token): @@ -95,7 +92,7 @@ def test_send_required_args_only(self, bot, chat_id, provider_token): ) assert message.invoice.currency == self.currency - assert message.invoice.start_parameter == '' + assert message.invoice.start_parameter == "" assert message.invoice.description == self.description assert message.invoice.title == self.title assert message.invoice.total_amount == self.total_amount @@ -114,9 +111,9 @@ def test_send_all_args(self, bot, chat_id, provider_token, monkeypatch): suggested_tip_amounts=self.suggested_tip_amounts, start_parameter=self.start_parameter, provider_data=self.provider_data, - photo_url='https://raw.githubusercontent.com/' - 'python-telegram-bot/logos/master/' - 'logo/png/ptb-logo_240.png', + photo_url="https://raw.githubusercontent.com/" + "python-telegram-bot/logos/master/" + "logo/png/ptb-logo_240.png", photo_size=240, photo_width=240, photo_height=240, @@ -140,62 +137,62 @@ def test_send_all_args(self, bot, chat_id, provider_token, monkeypatch): def make_assertion(*args, **_): kwargs = args[1] return ( - kwargs['chat_id'] == 'chat_id' - and kwargs['title'] == 'title' - and kwargs['description'] == 'description' - and kwargs['payload'] == 'payload' - and kwargs['provider_token'] == 'provider_token' - and kwargs['currency'] == 'currency' - and kwargs['prices'] == [p.to_dict() for p in self.prices] - and kwargs['max_tip_amount'] == 'max_tip_amount' - and kwargs['suggested_tip_amounts'] == 'suggested_tip_amounts' - and kwargs['start_parameter'] == 'start_parameter' - and kwargs['provider_data'] == 'provider_data' - and kwargs['photo_url'] == 'photo_url' - and kwargs['photo_size'] == 'photo_size' - and kwargs['photo_width'] == 'photo_width' - and kwargs['photo_height'] == 'photo_height' - and kwargs['need_name'] == 'need_name' - and kwargs['need_phone_number'] == 'need_phone_number' - and kwargs['need_email'] == 'need_email' - and kwargs['need_shipping_address'] == 'need_shipping_address' - and kwargs['send_phone_number_to_provider'] == 'send_phone_number_to_provider' - and kwargs['send_email_to_provider'] == 'send_email_to_provider' - and kwargs['is_flexible'] == 'is_flexible' + kwargs["chat_id"] == "chat_id" + and kwargs["title"] == "title" + and kwargs["description"] == "description" + and kwargs["payload"] == "payload" + and kwargs["provider_token"] == "provider_token" + and kwargs["currency"] == "currency" + and kwargs["prices"] == [p.to_dict() for p in self.prices] + and kwargs["max_tip_amount"] == "max_tip_amount" + and kwargs["suggested_tip_amounts"] == "suggested_tip_amounts" + and kwargs["start_parameter"] == "start_parameter" + and kwargs["provider_data"] == "provider_data" + and kwargs["photo_url"] == "photo_url" + and kwargs["photo_size"] == "photo_size" + and kwargs["photo_width"] == "photo_width" + and kwargs["photo_height"] == "photo_height" + and kwargs["need_name"] == "need_name" + and kwargs["need_phone_number"] == "need_phone_number" + and kwargs["need_email"] == "need_email" + and kwargs["need_shipping_address"] == "need_shipping_address" + and kwargs["send_phone_number_to_provider"] == "send_phone_number_to_provider" + and kwargs["send_email_to_provider"] == "send_email_to_provider" + and kwargs["is_flexible"] == "is_flexible" ) - monkeypatch.setattr(bot, '_message', make_assertion) + monkeypatch.setattr(bot, "_message", make_assertion) assert bot.send_invoice( - chat_id='chat_id', - title='title', - description='description', - payload='payload', - provider_token='provider_token', - currency='currency', + chat_id="chat_id", + title="title", + description="description", + payload="payload", + provider_token="provider_token", + currency="currency", prices=self.prices, - max_tip_amount='max_tip_amount', - suggested_tip_amounts='suggested_tip_amounts', - start_parameter='start_parameter', - provider_data='provider_data', - photo_url='photo_url', - photo_size='photo_size', - photo_width='photo_width', - photo_height='photo_height', - need_name='need_name', - need_phone_number='need_phone_number', - need_email='need_email', - need_shipping_address='need_shipping_address', - send_phone_number_to_provider='send_phone_number_to_provider', - send_email_to_provider='send_email_to_provider', - is_flexible='is_flexible', + max_tip_amount="max_tip_amount", + suggested_tip_amounts="suggested_tip_amounts", + start_parameter="start_parameter", + provider_data="provider_data", + photo_url="photo_url", + photo_size="photo_size", + photo_width="photo_width", + photo_height="photo_height", + need_name="need_name", + need_phone_number="need_phone_number", + need_email="need_email", + need_shipping_address="need_shipping_address", + send_phone_number_to_provider="send_phone_number_to_provider", + send_email_to_provider="send_email_to_provider", + is_flexible="is_flexible", ) def test_send_object_as_provider_data(self, monkeypatch, bot, chat_id, provider_token): def test(url, data, **kwargs): # depends on whether we're using ujson - return data['provider_data'] in ['{"test_data": 123456789}', '{"test_data":123456789}'] + return data["provider_data"] in ['{"test_data": 123456789}', '{"test_data":123456789}'] - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.send_invoice( chat_id, @@ -205,24 +202,24 @@ def test(url, data, **kwargs): provider_token, self.currency, self.prices, - provider_data={'test_data': 123456789}, + provider_data={"test_data": 123456789}, start_parameter=self.start_parameter, ) @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_invoice_default_allow_sending_without_reply( self, default_bot, chat_id, custom, provider_token ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_invoice( @@ -250,7 +247,7 @@ def test_send_invoice_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_invoice( chat_id, self.title, @@ -263,10 +260,10 @@ def test_send_invoice_default_allow_sending_without_reply( ) def test_equality(self): - a = Invoice('invoice', 'desc', 'start', 'EUR', 7) - b = Invoice('invoice', 'desc', 'start', 'EUR', 7) - c = Invoice('invoices', 'description', 'stop', 'USD', 8) - d = LabeledPrice('label', 5) + a = Invoice("invoice", "desc", "start", "EUR", 7) + b = Invoice("invoice", "desc", "start", "EUR", 7) + c = Invoice("invoices", "description", "stop", "USD", 8) + d = LabeledPrice("label", 5) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index 2851827dc63..69ac922cb27 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -36,7 +36,7 @@ class CustomContext(CallbackContext): pass -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def job_queue(bot, _dp): jq = JobQueue() jq.set_dispatcher(_dp) @@ -46,7 +46,7 @@ def job_queue(bot, _dp): @pytest.mark.skipif( - os.getenv('GITHUB_ACTIONS', False) and platform.system() in ['Windows', 'Darwin'], + os.getenv("GITHUB_ACTIONS", False) and platform.system() in ["Windows", "Darwin"], reason="On Windows & MacOS precise timings are not accurate.", ) @flaky(10, 1) # Timings aren't quite perfect @@ -55,13 +55,10 @@ class TestJobQueue: job_time = 0 received_error = None - def test_slot_behaviour(self, job_queue, recwarn, mro_slots, _dp): + def test_slot_behaviour(self, job_queue, mro_slots, _dp): for attr in job_queue.__slots__: - assert getattr(job_queue, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not job_queue.__dict__, f"got missing slot(s): {job_queue.__dict__}" + assert getattr(job_queue, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(job_queue)) == len(set(mro_slots(job_queue))), "duplicate slot" - job_queue.custom, job_queue._dispatcher = 'should give warning', _dp - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): @@ -69,20 +66,20 @@ def reset(self): self.job_time = 0 self.received_error = None - def job_run_once(self, bot, job): + def job_run_once(self, context): self.result += 1 - def job_with_exception(self, bot, job=None): - raise Exception('Test Error') + def job_with_exception(self, context): + raise Exception("Test Error") - def job_remove_self(self, bot, job): + def job_remove_self(self, context): self.result += 1 - job.schedule_removal() + context.job.schedule_removal() - def job_run_once_with_context(self, bot, job): - self.result += job.context + def job_run_once_with_context(self, context): + self.result += context.job.context - def job_datetime_tests(self, bot, job): + def job_datetime_tests(self, context): self.job_time = time.time() def job_context_based_callback(self, context): @@ -98,14 +95,11 @@ def job_context_based_callback(self, context): ): self.result += 1 - def error_handler(self, bot, update, error): - self.received_error = str(error) - def error_handler_context(self, update, context): self.received_error = str(context.error) def error_handler_raise_error(self, *args): - raise Exception('Failing bigly') + raise Exception("Failing bigly") def test_run_once(self, job_queue): job_queue.run_once(self.job_run_once, 0.01) @@ -173,7 +167,7 @@ def test_run_repeating_timedelta(self, job_queue): assert self.result == 2 def test_run_custom(self, job_queue): - job_queue.run_custom(self.job_run_once, {'trigger': 'interval', 'seconds': 0.02}) + job_queue.run_custom(self.job_run_once, {"trigger": "interval", "seconds": 0.02}) sleep(0.05) assert self.result == 2 @@ -236,7 +230,7 @@ def test_error(self, job_queue): assert self.result == 1 def test_in_updater(self, bot): - u = Updater(bot=bot, use_context=False) + u = Updater(bot=bot) u.job_queue.start() try: u.job_queue.run_repeating(self.job_run_once, 0.02) @@ -357,7 +351,7 @@ def test_run_monthly_non_strict_day(self, job_queue, timezone): ) expected_reschedule_time = expected_reschedule_time.timestamp() - job_queue.run_monthly(self.job_run_once, time_of_day, 31, day_is_strict=False) + job_queue.run_monthly(self.job_run_once, time_of_day, -1) scheduled_time = job_queue.jobs()[0].next_t.timestamp() assert scheduled_time == pytest.approx(expected_reschedule_time) @@ -380,40 +374,21 @@ def test_default_tzinfo(self, _dp, tz_bot): finally: _dp.bot = original_bot - @pytest.mark.parametrize('use_context', [True, False]) - def test_get_jobs(self, job_queue, use_context): - job_queue._dispatcher.use_context = use_context - if use_context: - callback = self.job_context_based_callback - else: - callback = self.job_run_once + def test_get_jobs(self, job_queue): + callback = self.job_context_based_callback - job1 = job_queue.run_once(callback, 10, name='name1') - job2 = job_queue.run_once(callback, 10, name='name1') - job3 = job_queue.run_once(callback, 10, name='name2') + job1 = job_queue.run_once(callback, 10, name="name1") + job2 = job_queue.run_once(callback, 10, name="name1") + job3 = job_queue.run_once(callback, 10, name="name2") assert job_queue.jobs() == (job1, job2, job3) - assert job_queue.get_jobs_by_name('name1') == (job1, job2) - assert job_queue.get_jobs_by_name('name2') == (job3,) - - def test_context_based_callback(self, job_queue): - job_queue._dispatcher.use_context = True - - job_queue.run_once(self.job_context_based_callback, 0.01, context=2) - sleep(0.03) + assert job_queue.get_jobs_by_name("name1") == (job1, job2) + assert job_queue.get_jobs_by_name("name2") == (job3,) - assert self.result == 1 - job_queue._dispatcher.use_context = False - - @pytest.mark.parametrize('use_context', [True, False]) - def test_job_run(self, _dp, use_context): - _dp.use_context = use_context + def test_job_run(self, _dp): job_queue = JobQueue() job_queue.set_dispatcher(_dp) - if use_context: - job = job_queue.run_repeating(self.job_context_based_callback, 0.02, context=2) - else: - job = job_queue.run_repeating(self.job_run_once, 0.02, context=2) + job = job_queue.run_repeating(self.job_context_based_callback, 0.02, context=2) assert self.result == 0 job.run(_dp) assert self.result == 1 @@ -446,18 +421,18 @@ def test_job_lt_eq(self, job_queue): assert not job == job_queue assert not job < job - def test_dispatch_error(self, job_queue, dp): - dp.add_error_handler(self.error_handler) + def test_dispatch_error_context(self, job_queue, dp): + dp.add_error_handler(self.error_handler_context) job = job_queue.run_once(self.job_with_exception, 0.05) sleep(0.1) - assert self.received_error == 'Test Error' + assert self.received_error == "Test Error" self.received_error = None job.run(dp) - assert self.received_error == 'Test Error' + assert self.received_error == "Test Error" # Remove handler - dp.remove_error_handler(self.error_handler) + dp.remove_error_handler(self.error_handler_context) self.received_error = None job = job_queue.run_once(self.job_with_exception, 0.05) @@ -466,26 +441,6 @@ def test_dispatch_error(self, job_queue, dp): job.run(dp) assert self.received_error is None - def test_dispatch_error_context(self, job_queue, cdp): - cdp.add_error_handler(self.error_handler_context) - - job = job_queue.run_once(self.job_with_exception, 0.05) - sleep(0.1) - assert self.received_error == 'Test Error' - self.received_error = None - job.run(cdp) - assert self.received_error == 'Test Error' - - # Remove handler - cdp.remove_error_handler(self.error_handler_context) - self.received_error = None - - job = job_queue.run_once(self.job_with_exception, 0.05) - sleep(0.1) - assert self.received_error is None - job.run(cdp) - assert self.received_error is None - def test_dispatch_error_that_raises_errors(self, job_queue, dp, caplog): dp.add_error_handler(self.error_handler_raise_error) @@ -494,16 +449,16 @@ def test_dispatch_error_that_raises_errors(self, job_queue, dp, caplog): sleep(0.1) assert len(caplog.records) == 1 rec = caplog.records[-1] - assert 'processing the job' in rec.getMessage() - assert 'uncaught error was raised while handling' in rec.getMessage() + assert "processing the job" in rec.getMessage() + assert "uncaught error was raised while handling" in rec.getMessage() caplog.clear() with caplog.at_level(logging.ERROR): job.run(dp) assert len(caplog.records) == 1 rec = caplog.records[-1] - assert 'processing the job' in rec.getMessage() - assert 'uncaught error was raised while handling' in rec.getMessage() + assert "processing the job" in rec.getMessage() + assert "uncaught error was raised while handling" in rec.getMessage() caplog.clear() # Remove handler @@ -515,14 +470,14 @@ def test_dispatch_error_that_raises_errors(self, job_queue, dp, caplog): sleep(0.1) assert len(caplog.records) == 1 rec = caplog.records[-1] - assert 'No error handlers are registered' in rec.getMessage() + assert "No error handlers are registered" in rec.getMessage() caplog.clear() with caplog.at_level(logging.ERROR): job.run(dp) assert len(caplog.records) == 1 rec = caplog.records[-1] - assert 'No error handlers are registered' in rec.getMessage() + assert "No error handlers are registered" in rec.getMessage() def test_custom_context(self, bot, job_queue): dispatcher = Dispatcher( diff --git a/tests/test_keyboardbutton.py b/tests/test_keyboardbutton.py index 3c3fd4c04f0..32ecd925d56 100644 --- a/tests/test_keyboardbutton.py +++ b/tests/test_keyboardbutton.py @@ -22,7 +22,7 @@ from telegram.keyboardbuttonpolltype import KeyboardButtonPollType -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def keyboard_button(): return KeyboardButton( TestKeyboardButton.text, @@ -33,19 +33,16 @@ def keyboard_button(): class TestKeyboardButton: - text = 'text' + text = "text" request_location = True request_contact = True request_poll = KeyboardButtonPollType("quiz") - def test_slot_behaviour(self, keyboard_button, recwarn, mro_slots): + def test_slot_behaviour(self, keyboard_button, mro_slots): inst = keyboard_button for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.text = 'should give warning', self.text - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, keyboard_button): assert keyboard_button.text == self.text @@ -57,16 +54,16 @@ def test_to_dict(self, keyboard_button): keyboard_button_dict = keyboard_button.to_dict() assert isinstance(keyboard_button_dict, dict) - assert keyboard_button_dict['text'] == keyboard_button.text - assert keyboard_button_dict['request_location'] == keyboard_button.request_location - assert keyboard_button_dict['request_contact'] == keyboard_button.request_contact - assert keyboard_button_dict['request_poll'] == keyboard_button.request_poll.to_dict() + assert keyboard_button_dict["text"] == keyboard_button.text + assert keyboard_button_dict["request_location"] == keyboard_button.request_location + assert keyboard_button_dict["request_contact"] == keyboard_button.request_contact + assert keyboard_button_dict["request_poll"] == keyboard_button.request_poll.to_dict() def test_equality(self): - a = KeyboardButton('test', request_contact=True) - b = KeyboardButton('test', request_contact=True) - c = KeyboardButton('Test', request_location=True) - d = InlineKeyboardButton('test', callback_data='test') + a = KeyboardButton("test", request_contact=True) + b = KeyboardButton("test", request_contact=True) + c = KeyboardButton("Test", request_location=True) + d = InlineKeyboardButton("test", callback_data="test") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_keyboardbuttonpolltype.py b/tests/test_keyboardbuttonpolltype.py index dafe0d9f344..64a1d9df486 100644 --- a/tests/test_keyboardbuttonpolltype.py +++ b/tests/test_keyboardbuttonpolltype.py @@ -21,7 +21,7 @@ from telegram import KeyboardButtonPollType, Poll -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def keyboard_button_poll_type(): return KeyboardButtonPollType(TestKeyboardButtonPollType.type) @@ -29,19 +29,16 @@ def keyboard_button_poll_type(): class TestKeyboardButtonPollType: type = Poll.QUIZ - def test_slot_behaviour(self, keyboard_button_poll_type, recwarn, mro_slots): + def test_slot_behaviour(self, keyboard_button_poll_type, mro_slots): inst = keyboard_button_poll_type for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_to_dict(self, keyboard_button_poll_type): keyboard_button_poll_type_dict = keyboard_button_poll_type.to_dict() assert isinstance(keyboard_button_poll_type_dict, dict) - assert keyboard_button_poll_type_dict['type'] == self.type + assert keyboard_button_poll_type_dict["type"] == self.type def test_equality(self): a = KeyboardButtonPollType(Poll.QUIZ) diff --git a/tests/test_labeledprice.py b/tests/test_labeledprice.py index bfcd72edda2..b2e7633ad80 100644 --- a/tests/test_labeledprice.py +++ b/tests/test_labeledprice.py @@ -21,23 +21,20 @@ from telegram import LabeledPrice, Location -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def labeled_price(): return LabeledPrice(TestLabeledPrice.label, TestLabeledPrice.amount) class TestLabeledPrice: - label = 'label' + label = "label" amount = 100 - def test_slot_behaviour(self, labeled_price, recwarn, mro_slots): + def test_slot_behaviour(self, labeled_price, mro_slots): inst = labeled_price for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.label = 'should give warning', self.label - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, labeled_price): assert labeled_price.label == self.label @@ -47,13 +44,13 @@ def test_to_dict(self, labeled_price): labeled_price_dict = labeled_price.to_dict() assert isinstance(labeled_price_dict, dict) - assert labeled_price_dict['label'] == labeled_price.label - assert labeled_price_dict['amount'] == labeled_price.amount + assert labeled_price_dict["label"] == labeled_price.label + assert labeled_price_dict["amount"] == labeled_price.amount def test_equality(self): - a = LabeledPrice('label', 100) - b = LabeledPrice('label', 100) - c = LabeledPrice('Label', 101) + a = LabeledPrice("label", 100) + b = LabeledPrice("label", 100) + c = LabeledPrice("Label", 101) d = Location(123, 456) assert a == b diff --git a/tests/test_location.py b/tests/test_location.py index 20cd46a1192..9799e27bcec 100644 --- a/tests/test_location.py +++ b/tests/test_location.py @@ -23,7 +23,7 @@ from telegram.error import BadRequest -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def location(): return Location( latitude=TestLocation.latitude, @@ -43,22 +43,19 @@ class TestLocation: heading = 90 proximity_alert_radius = 50 - def test_slot_behaviour(self, location, recwarn, mro_slots): + def test_slot_behaviour(self, location, mro_slots): for attr in location.__slots__: - assert getattr(location, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not location.__dict__, f"got missing slot(s): {location.__dict__}" + assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(location)) == len(set(mro_slots(location))), "duplicate slot" - location.custom, location.heading = 'should give warning', self.heading - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'latitude': TestLocation.latitude, - 'longitude': TestLocation.longitude, - 'horizontal_accuracy': TestLocation.horizontal_accuracy, - 'live_period': TestLocation.live_period, - 'heading': TestLocation.heading, - 'proximity_alert_radius': TestLocation.proximity_alert_radius, + "latitude": TestLocation.latitude, + "longitude": TestLocation.longitude, + "horizontal_accuracy": TestLocation.horizontal_accuracy, + "live_period": TestLocation.live_period, + "heading": TestLocation.heading, + "proximity_alert_radius": TestLocation.proximity_alert_radius, } location = Location.de_json(json_dict, bot) @@ -114,15 +111,15 @@ def test_send_live_location(self, bot, chat_id): # TODO: Needs improvement with in inline sent live location. def test_edit_live_inline_message(self, monkeypatch, bot, location): def make_assertion(url, data, **kwargs): - lat = data['latitude'] == location.latitude - lon = data['longitude'] == location.longitude - id_ = data['inline_message_id'] == 1234 - ha = data['horizontal_accuracy'] == 50 - heading = data['heading'] == 90 - prox_alert = data['proximity_alert_radius'] == 1000 + lat = data["latitude"] == location.latitude + lon = data["longitude"] == location.longitude + id_ = data["inline_message_id"] == 1234 + ha = data["horizontal_accuracy"] == 50 + heading = data["heading"] == 90 + prox_alert = data["proximity_alert_radius"] == 1000 return lat and lon and id_ and ha and heading and prox_alert - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) assert bot.edit_message_live_location( inline_message_id=1234, location=location, @@ -134,35 +131,35 @@ def make_assertion(url, data, **kwargs): # TODO: Needs improvement with in inline sent live location. def test_stop_live_inline_message(self, monkeypatch, bot): def test(url, data, **kwargs): - id_ = data['inline_message_id'] == 1234 + id_ = data["inline_message_id"] == 1234 return id_ - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.stop_message_live_location(inline_message_id=1234) def test_send_with_location(self, monkeypatch, bot, chat_id, location): def test(url, data, **kwargs): - lat = data['latitude'] == location.latitude - lon = data['longitude'] == location.longitude + lat = data["latitude"] == location.latitude + lon = data["longitude"] == location.longitude return lat and lon - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.send_location(location=location, chat_id=chat_id) @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_location_default_allow_sending_without_reply( self, default_bot, chat_id, location, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_location( @@ -178,34 +175,34 @@ def test_send_location_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_location( chat_id, location=location, reply_to_message_id=reply_to_message.message_id ) def test_edit_live_location_with_location(self, monkeypatch, bot, location): def test(url, data, **kwargs): - lat = data['latitude'] == location.latitude - lon = data['longitude'] == location.longitude + lat = data["latitude"] == location.latitude + lon = data["longitude"] == location.longitude return lat and lon - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) assert bot.edit_message_live_location(None, None, location=location) def test_send_location_without_required(self, bot, chat_id): - with pytest.raises(ValueError, match='Either location or latitude and longitude'): + with pytest.raises(ValueError, match="Either location or latitude and longitude"): bot.send_location(chat_id=chat_id) def test_edit_location_without_required(self, bot): - with pytest.raises(ValueError, match='Either location or latitude and longitude'): + with pytest.raises(ValueError, match="Either location or latitude and longitude"): bot.edit_message_live_location(chat_id=2, message_id=3) def test_send_location_with_all_args(self, bot, location): - with pytest.raises(ValueError, match='Not both'): + with pytest.raises(ValueError, match="Not both"): bot.send_location(chat_id=1, latitude=2.5, longitude=4.6, location=location) def test_edit_location_with_all_args(self, bot, location): - with pytest.raises(ValueError, match='Not both'): + with pytest.raises(ValueError, match="Not both"): bot.edit_message_live_location( chat_id=1, message_id=7, latitude=2.5, longitude=4.6, location=location ) @@ -213,12 +210,12 @@ def test_edit_location_with_all_args(self, bot, location): def test_to_dict(self, location): location_dict = location.to_dict() - assert location_dict['latitude'] == location.latitude - assert location_dict['longitude'] == location.longitude - assert location_dict['horizontal_accuracy'] == location.horizontal_accuracy - assert location_dict['live_period'] == location.live_period - assert location['heading'] == location.heading - assert location['proximity_alert_radius'] == location.proximity_alert_radius + assert location_dict["latitude"] == location.latitude + assert location_dict["longitude"] == location.longitude + assert location_dict["horizontal_accuracy"] == location.horizontal_accuracy + assert location_dict["live_period"] == location.live_period + assert location["heading"] == location.heading + assert location["proximity_alert_radius"] == location.proximity_alert_radius def test_equality(self): a = Location(self.longitude, self.latitude) diff --git a/tests/test_loginurl.py b/tests/test_loginurl.py index c638c9234d5..61154a194b2 100644 --- a/tests/test_loginurl.py +++ b/tests/test_loginurl.py @@ -21,7 +21,7 @@ from telegram import LoginUrl -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def login_url(): return LoginUrl( url=TestLoginUrl.url, @@ -37,22 +37,19 @@ class TestLoginUrl: bot_username = "botname" request_write_access = True - def test_slot_behaviour(self, login_url, recwarn, mro_slots): + def test_slot_behaviour(self, login_url, mro_slots): for attr in login_url.__slots__: - assert getattr(login_url, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not login_url.__dict__, f"got missing slot(s): {login_url.__dict__}" + assert getattr(login_url, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(login_url)) == len(set(mro_slots(login_url))), "duplicate slot" - login_url.custom, login_url.url = 'should give warning', self.url - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_to_dict(self, login_url): login_url_dict = login_url.to_dict() assert isinstance(login_url_dict, dict) - assert login_url_dict['url'] == self.url - assert login_url_dict['forward_text'] == self.forward_text - assert login_url_dict['bot_username'] == self.bot_username - assert login_url_dict['request_write_access'] == self.request_write_access + assert login_url_dict["url"] == self.url + assert login_url_dict["forward_text"] == self.forward_text + assert login_url_dict["bot_username"] == self.bot_username + assert login_url_dict["request_write_access"] == self.request_write_access def test_equality(self): a = LoginUrl(self.url, self.forward_text, self.bot_username, self.request_write_access) diff --git a/tests/test_message.py b/tests/test_message.py index 5ed66b4dcb7..64de8fe150e 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -59,7 +59,7 @@ from tests.test_passport import RAW_PASSPORT_DATA -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def message(bot): return Message( TestMessage.id_, @@ -71,79 +71,79 @@ def message(bot): @pytest.fixture( - scope='function', + scope="function", params=[ - {'forward_from': User(99, 'forward_user', False), 'forward_date': datetime.utcnow()}, + {"forward_from": User(99, "forward_user", False), "forward_date": datetime.utcnow()}, { - 'forward_from_chat': Chat(-23, 'channel'), - 'forward_from_message_id': 101, - 'forward_date': datetime.utcnow(), + "forward_from_chat": Chat(-23, "channel"), + "forward_from_message_id": 101, + "forward_date": datetime.utcnow(), }, - {'reply_to_message': Message(50, None, None, None)}, - {'edit_date': datetime.utcnow()}, + {"reply_to_message": Message(50, None, None, None)}, + {"edit_date": datetime.utcnow()}, { - 'text': 'a text message', - 'enitites': [MessageEntity('bold', 10, 4), MessageEntity('italic', 16, 7)], + "text": "a text message", + "enitites": [MessageEntity("bold", 10, 4), MessageEntity("italic", 16, 7)], }, { - 'caption': 'A message caption', - 'caption_entities': [MessageEntity('bold', 1, 1), MessageEntity('text_link', 4, 3)], + "caption": "A message caption", + "caption_entities": [MessageEntity("bold", 1, 1), MessageEntity("text_link", 4, 3)], }, - {'audio': Audio('audio_id', 'unique_id', 12), 'caption': 'audio_file'}, - {'document': Document('document_id', 'unique_id'), 'caption': 'document_file'}, + {"audio": Audio("audio_id", "unique_id", 12), "caption": "audio_file"}, + {"document": Document("document_id", "unique_id"), "caption": "document_file"}, { - 'animation': Animation('animation_id', 'unique_id', 30, 30, 1), - 'caption': 'animation_file', + "animation": Animation("animation_id", "unique_id", 30, 30, 1), + "caption": "animation_file", }, { - 'game': Game( - 'my_game', - 'just my game', + "game": Game( + "my_game", + "just my game", [ - PhotoSize('game_photo_id', 'unique_id', 30, 30), + PhotoSize("game_photo_id", "unique_id", 30, 30), ], ) }, - {'photo': [PhotoSize('photo_id', 'unique_id', 50, 50)], 'caption': 'photo_file'}, - {'sticker': Sticker('sticker_id', 'unique_id', 50, 50, True)}, - {'video': Video('video_id', 'unique_id', 12, 12, 12), 'caption': 'video_file'}, - {'voice': Voice('voice_id', 'unique_id', 5)}, - {'video_note': VideoNote('video_note_id', 'unique_id', 20, 12)}, - {'new_chat_members': [User(55, 'new_user', False)]}, - {'contact': Contact('phone_numner', 'contact_name')}, - {'location': Location(-23.691288, 46.788279)}, - {'venue': Venue(Location(-23.691288, 46.788279), 'some place', 'right here')}, - {'left_chat_member': User(33, 'kicked', False)}, - {'new_chat_title': 'new title'}, - {'new_chat_photo': [PhotoSize('photo_id', 'unique_id', 50, 50)]}, - {'delete_chat_photo': True}, - {'group_chat_created': True}, - {'supergroup_chat_created': True}, - {'channel_chat_created': True}, - {'message_auto_delete_timer_changed': MessageAutoDeleteTimerChanged(42)}, - {'migrate_to_chat_id': -12345}, - {'migrate_from_chat_id': -54321}, - {'pinned_message': Message(7, None, None, None)}, - {'invoice': Invoice('my invoice', 'invoice', 'start', 'EUR', 243)}, + {"photo": [PhotoSize("photo_id", "unique_id", 50, 50)], "caption": "photo_file"}, + {"sticker": Sticker("sticker_id", "unique_id", 50, 50, True)}, + {"video": Video("video_id", "unique_id", 12, 12, 12), "caption": "video_file"}, + {"voice": Voice("voice_id", "unique_id", 5)}, + {"video_note": VideoNote("video_note_id", "unique_id", 20, 12)}, + {"new_chat_members": [User(55, "new_user", False)]}, + {"contact": Contact("phone_numner", "contact_name")}, + {"location": Location(-23.691288, 46.788279)}, + {"venue": Venue(Location(-23.691288, 46.788279), "some place", "right here")}, + {"left_chat_member": User(33, "kicked", False)}, + {"new_chat_title": "new title"}, + {"new_chat_photo": [PhotoSize("photo_id", "unique_id", 50, 50)]}, + {"delete_chat_photo": True}, + {"group_chat_created": True}, + {"supergroup_chat_created": True}, + {"channel_chat_created": True}, + {"message_auto_delete_timer_changed": MessageAutoDeleteTimerChanged(42)}, + {"migrate_to_chat_id": -12345}, + {"migrate_from_chat_id": -54321}, + {"pinned_message": Message(7, None, None, None)}, + {"invoice": Invoice("my invoice", "invoice", "start", "EUR", 243)}, { - 'successful_payment': SuccessfulPayment( - 'EUR', 243, 'payload', 'charge_id', 'provider_id', order_info={} + "successful_payment": SuccessfulPayment( + "EUR", 243, "payload", "charge_id", "provider_id", order_info={} ) }, - {'connected_website': 'http://example.com/'}, - {'forward_signature': 'some_forward_sign'}, - {'author_signature': 'some_author_sign'}, + {"connected_website": "http://example.com/"}, + {"forward_signature": "some_forward_sign"}, + {"author_signature": "some_author_sign"}, { - 'photo': [PhotoSize('photo_id', 'unique_id', 50, 50)], - 'caption': 'photo_file', - 'media_group_id': 1234443322222, + "photo": [PhotoSize("photo_id", "unique_id", 50, 50)], + "caption": "photo_file", + "media_group_id": 1234443322222, }, - {'passport_data': PassportData.de_json(RAW_PASSPORT_DATA, None)}, + {"passport_data": PassportData.de_json(RAW_PASSPORT_DATA, None)}, { - 'poll': Poll( - id='abc', - question='What is this?', - options=[PollOption(text='a', voter_count=1), PollOption(text='b', voter_count=2)], + "poll": Poll( + id="abc", + question="What is this?", + options=[PollOption(text="a", voter_count=1), PollOption(text="b", voter_count=2)], is_closed=False, total_voter_count=0, is_anonymous=False, @@ -153,82 +153,82 @@ def message(bot): ) }, { - 'text': 'a text message', - 'reply_markup': { - 'inline_keyboard': [ + "text": "a text message", + "reply_markup": { + "inline_keyboard": [ [ - {'text': 'start', 'url': 'http://google.com'}, - {'text': 'next', 'callback_data': 'abcd'}, + {"text": "start", "url": "http://google.com"}, + {"text": "next", "callback_data": "abcd"}, ], - [{'text': 'Cancel', 'callback_data': 'Cancel'}], + [{"text": "Cancel", "callback_data": "Cancel"}], ] }, }, - {'dice': Dice(4, '🎲')}, - {'via_bot': User(9, 'A_Bot', True)}, + {"dice": Dice(4, "🎲")}, + {"via_bot": User(9, "A_Bot", True)}, { - 'proximity_alert_triggered': ProximityAlertTriggered( - User(1, 'John', False), User(2, 'Doe', False), 42 + "proximity_alert_triggered": ProximityAlertTriggered( + User(1, "John", False), User(2, "Doe", False), 42 ) }, - {'voice_chat_scheduled': VoiceChatScheduled(datetime.utcnow())}, - {'voice_chat_started': VoiceChatStarted()}, - {'voice_chat_ended': VoiceChatEnded(100)}, + {"voice_chat_scheduled": VoiceChatScheduled(datetime.utcnow())}, + {"voice_chat_started": VoiceChatStarted()}, + {"voice_chat_ended": VoiceChatEnded(100)}, { - 'voice_chat_participants_invited': VoiceChatParticipantsInvited( - [User(1, 'Rem', False), User(2, 'Emilia', False)] + "voice_chat_participants_invited": VoiceChatParticipantsInvited( + [User(1, "Rem", False), User(2, "Emilia", False)] ) }, - {'sender_chat': Chat(-123, 'discussion_channel')}, + {"sender_chat": Chat(-123, "discussion_channel")}, ], ids=[ - 'forwarded_user', - 'forwarded_channel', - 'reply', - 'edited', - 'text', - 'caption_entities', - 'audio', - 'document', - 'animation', - 'game', - 'photo', - 'sticker', - 'video', - 'voice', - 'video_note', - 'new_members', - 'contact', - 'location', - 'venue', - 'left_member', - 'new_title', - 'new_photo', - 'delete_photo', - 'group_created', - 'supergroup_created', - 'channel_created', - 'message_auto_delete_timer_changed', - 'migrated_to', - 'migrated_from', - 'pinned', - 'invoice', - 'successful_payment', - 'connected_website', - 'forward_signature', - 'author_signature', - 'photo_from_media_group', - 'passport_data', - 'poll', - 'reply_markup', - 'dice', - 'via_bot', - 'proximity_alert_triggered', - 'voice_chat_scheduled', - 'voice_chat_started', - 'voice_chat_ended', - 'voice_chat_participants_invited', - 'sender_chat', + "forwarded_user", + "forwarded_channel", + "reply", + "edited", + "text", + "caption_entities", + "audio", + "document", + "animation", + "game", + "photo", + "sticker", + "video", + "voice", + "video_note", + "new_members", + "contact", + "location", + "venue", + "left_member", + "new_title", + "new_photo", + "delete_photo", + "group_created", + "supergroup_created", + "channel_created", + "message_auto_delete_timer_changed", + "migrated_to", + "migrated_from", + "pinned", + "invoice", + "successful_payment", + "connected_website", + "forward_signature", + "author_signature", + "photo_from_media_group", + "passport_data", + "poll", + "reply_markup", + "dice", + "via_bot", + "proximity_alert_triggered", + "voice_chat_scheduled", + "voice_chat_started", + "voice_chat_ended", + "voice_chat_participants_invited", + "sender_chat", ], ) def message_params(bot, request): @@ -244,47 +244,47 @@ def message_params(bot, request): class TestMessage: id_ = 1 - from_user = User(2, 'testuser', False) + from_user = User(2, "testuser", False) date = datetime.utcnow() - chat = Chat(3, 'private') + chat = Chat(3, "private") test_entities = [ - {'length': 4, 'offset': 10, 'type': 'bold'}, - {'length': 3, 'offset': 16, 'type': 'italic'}, - {'length': 3, 'offset': 20, 'type': 'italic'}, - {'length': 4, 'offset': 25, 'type': 'code'}, - {'length': 5, 'offset': 31, 'type': 'text_link', 'url': 'http://github.com/ab_'}, + {"length": 4, "offset": 10, "type": "bold"}, + {"length": 3, "offset": 16, "type": "italic"}, + {"length": 3, "offset": 20, "type": "italic"}, + {"length": 4, "offset": 25, "type": "code"}, + {"length": 5, "offset": 31, "type": "text_link", "url": "http://github.com/ab_"}, { - 'length': 12, - 'offset': 38, - 'type': 'text_mention', - 'user': User(123456789, 'mentioned user', False), + "length": 12, + "offset": 38, + "type": "text_mention", + "user": User(123456789, "mentioned user", False), }, - {'length': 3, 'offset': 55, 'type': 'pre', 'language': 'python'}, - {'length': 21, 'offset': 60, 'type': 'url'}, + {"length": 3, "offset": 55, "type": "pre", "language": "python"}, + {"length": 21, "offset": 60, "type": "url"}, ] - test_text = 'Test for trgh nested in italic. Python pre.' + r"Test for trgh nested in italic. Python pre." ) test_message = Message( message_id=1, @@ -307,13 +307,10 @@ class TestMessage: caption_entities=[MessageEntity(**e) for e in test_entities_v2], ) - def test_slot_behaviour(self, message, recwarn, mro_slots): + def test_slot_behaviour(self, message, mro_slots): for attr in message.__slots__: - assert getattr(message, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not message.__dict__, f"got missing slot(s): {message.__dict__}" + assert getattr(message, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(message)) == len(set(mro_slots(message))), "duplicate slot" - message.custom, message.message_id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_all_possibilities_de_json_and_to_dict(self, bot, message_params): new = Message.de_json(message_params.to_dict(), bot) @@ -321,48 +318,48 @@ def test_all_possibilities_de_json_and_to_dict(self, bot, message_params): assert new.to_dict() == message_params.to_dict() def test_dict_approach(self, message): - assert message['text'] == message.text - assert message['chat_id'] == message.chat_id - assert message['no_key'] is None + assert message["text"] == message.text + assert message["chat_id"] == message.chat_id + assert message["no_key"] is None def test_parse_entity(self): text = ( - b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467' - b'\\u200d\\U0001f467\\U0001f431http://google.com' - ).decode('unicode-escape') + b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" + b"\\u200d\\U0001f467\\U0001f431http://google.com" + ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) message = Message(1, self.from_user, self.date, self.chat, text=text, entities=[entity]) - assert message.parse_entity(entity) == 'http://google.com' + assert message.parse_entity(entity) == "http://google.com" def test_parse_caption_entity(self): caption = ( - b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467' - b'\\u200d\\U0001f467\\U0001f431http://google.com' - ).decode('unicode-escape') + b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" + b"\\u200d\\U0001f467\\U0001f431http://google.com" + ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) message = Message( 1, self.from_user, self.date, self.chat, caption=caption, caption_entities=[entity] ) - assert message.parse_caption_entity(entity) == 'http://google.com' + assert message.parse_caption_entity(entity) == "http://google.com" def test_parse_entities(self): text = ( - b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467' - b'\\u200d\\U0001f467\\U0001f431http://google.com' - ).decode('unicode-escape') + b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" + b"\\u200d\\U0001f467\\U0001f431http://google.com" + ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1) message = Message( 1, self.from_user, self.date, self.chat, text=text, entities=[entity_2, entity] ) - assert message.parse_entities(MessageEntity.URL) == {entity: 'http://google.com'} - assert message.parse_entities() == {entity: 'http://google.com', entity_2: 'h'} + assert message.parse_entities(MessageEntity.URL) == {entity: "http://google.com"} + assert message.parse_entities() == {entity: "http://google.com", entity_2: "h"} def test_parse_caption_entities(self): text = ( - b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467' - b'\\u200d\\U0001f467\\U0001f431http://google.com' - ).decode('unicode-escape') + b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" + b"\\u200d\\U0001f467\\U0001f431http://google.com" + ).decode("unicode-escape") entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1) message = Message( @@ -373,17 +370,17 @@ def test_parse_caption_entities(self): caption=text, caption_entities=[entity_2, entity], ) - assert message.parse_caption_entities(MessageEntity.URL) == {entity: 'http://google.com'} - assert message.parse_caption_entities() == {entity: 'http://google.com', entity_2: 'h'} + assert message.parse_caption_entities(MessageEntity.URL) == {entity: "http://google.com"} + assert message.parse_caption_entities() == {entity: "http://google.com", entity_2: "h"} def test_text_html_simple(self): test_html_string = ( - 'Test for <bold, ita_lic, ' - r'\`code, ' + "Test for <bold, ita_lic, " + r"\`code, " r'links, ' 'text-mention and ' - r'
`\pre
. http://google.com ' - 'and bold nested in strk>trgh nested in italic. ' + r"
`\pre
. http://google.com " + "and bold nested in strk>trgh nested in italic. " '
Python pre
.' ) text_html = self.test_message_v2.text_html @@ -396,12 +393,12 @@ def test_text_html_empty(self, message): def test_text_html_urled(self): test_html_string = ( - 'Test for <bold, ita_lic, ' - r'\`code, ' + "Test for <bold, ita_lic, " + r"\`code, " r'links, ' 'text-mention and ' r'
`\pre
. http://google.com ' - 'and bold nested in strk>trgh nested in italic. ' + "and bold nested in strk>trgh nested in italic. " '
Python pre
.' ) text_html = self.test_message_v2.text_html_urled @@ -409,27 +406,27 @@ def test_text_html_urled(self): def test_text_markdown_simple(self): test_md_string = ( - r'Test for <*bold*, _ita_\__lic_, `code`, ' - '[links](http://github.com/ab_), ' - '[text-mention](tg://user?id=123456789) and ```python\npre```. ' - r'http://google.com/ab\_' + r"Test for <*bold*, _ita_\__lic_, `code`, " + "[links](http://github.com/ab_), " + "[text-mention](tg://user?id=123456789) and ```python\npre```. " + r"http://google.com/ab\_" ) text_markdown = self.test_message.text_markdown assert text_markdown == test_md_string def test_text_markdown_v2_simple(self): test_md_string = ( - r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, ' - '[links](http://github.com/abc\\\\\\)def), ' - '[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. ' - r'http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. ' - '```python\nPython pre```\\.' + r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " + "[links](http://github.com/abc\\\\\\)def), " + "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " + r"http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. " + "```python\nPython pre```\\." ) text_markdown = self.test_message_v2.text_markdown_v2 assert text_markdown == test_md_string def test_text_markdown_new_in_v2(self, message): - message.text = 'test' + message.text = "test" message.entities = [ MessageEntity(MessageEntity.BOLD, offset=0, length=4), MessageEntity(MessageEntity.ITALIC, offset=0, length=4), @@ -455,28 +452,28 @@ def test_text_markdown_empty(self, message): def test_text_markdown_urled(self): test_md_string = ( - r'Test for <*bold*, _ita_\__lic_, `code`, ' - '[links](http://github.com/ab_), ' - '[text-mention](tg://user?id=123456789) and ```python\npre```. ' - '[http://google.com/ab_](http://google.com/ab_)' + r"Test for <*bold*, _ita_\__lic_, `code`, " + "[links](http://github.com/ab_), " + "[text-mention](tg://user?id=123456789) and ```python\npre```. " + "[http://google.com/ab_](http://google.com/ab_)" ) text_markdown = self.test_message.text_markdown_urled assert text_markdown == test_md_string def test_text_markdown_v2_urled(self): test_md_string = ( - r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, ' - '[links](http://github.com/abc\\\\\\)def), ' - '[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. ' - r'[http://google\.com](http://google.com) and _bold *nested in ~strk\>trgh~ ' - 'nested in* italic_\\. ```python\nPython pre```\\.' + r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " + "[links](http://github.com/abc\\\\\\)def), " + "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " + r"[http://google\.com](http://google.com) and _bold *nested in ~strk\>trgh~ " + "nested in* italic_\\. ```python\nPython pre```\\." ) text_markdown = self.test_message_v2.text_markdown_v2_urled assert text_markdown == test_md_string def test_text_html_emoji(self): - text = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape') - expected = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape') + text = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") + expected = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, self.from_user, self.date, self.chat, text=text, entities=[bold_entity] @@ -484,8 +481,8 @@ def test_text_html_emoji(self): assert expected == message.text_html def test_text_markdown_emoji(self): - text = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape') - expected = b'\\U0001f469\\u200d\\U0001f469\\u200d *ABC*'.decode('unicode-escape') + text = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") + expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, self.from_user, self.date, self.chat, text=text, entities=[bold_entity] @@ -494,12 +491,12 @@ def test_text_markdown_emoji(self): def test_caption_html_simple(self): test_html_string = ( - 'Test for <bold, ita_lic, ' - r'\`code, ' + "Test for <bold, ita_lic, " + r"\`code, " r'links, ' 'text-mention and ' - r'
`\pre
. http://google.com ' - 'and bold nested in strk>trgh nested in italic. ' + r"
`\pre
. http://google.com " + "and bold nested in strk>trgh nested in italic. " '
Python pre
.' ) caption_html = self.test_message_v2.caption_html @@ -512,12 +509,12 @@ def test_caption_html_empty(self, message): def test_caption_html_urled(self): test_html_string = ( - 'Test for <bold, ita_lic, ' - r'\`code, ' + "Test for <bold, ita_lic, " + r"\`code, " r'links, ' 'text-mention and ' r'
`\pre
. http://google.com ' - 'and bold nested in strk>trgh nested in italic. ' + "and bold nested in strk>trgh nested in italic. " '
Python pre
.' ) caption_html = self.test_message_v2.caption_html_urled @@ -525,21 +522,21 @@ def test_caption_html_urled(self): def test_caption_markdown_simple(self): test_md_string = ( - r'Test for <*bold*, _ita_\__lic_, `code`, ' - '[links](http://github.com/ab_), ' - '[text-mention](tg://user?id=123456789) and ```python\npre```. ' - r'http://google.com/ab\_' + r"Test for <*bold*, _ita_\__lic_, `code`, " + "[links](http://github.com/ab_), " + "[text-mention](tg://user?id=123456789) and ```python\npre```. " + r"http://google.com/ab\_" ) caption_markdown = self.test_message.caption_markdown assert caption_markdown == test_md_string def test_caption_markdown_v2_simple(self): test_md_string = ( - r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, ' - '[links](http://github.com/abc\\\\\\)def), ' - '[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. ' - r'http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. ' - '```python\nPython pre```\\.' + r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " + "[links](http://github.com/abc\\\\\\)def), " + "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " + r"http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. " + "```python\nPython pre```\\." ) caption_markdown = self.test_message_v2.caption_markdown_v2 assert caption_markdown == test_md_string @@ -552,28 +549,28 @@ def test_caption_markdown_empty(self, message): def test_caption_markdown_urled(self): test_md_string = ( - r'Test for <*bold*, _ita_\__lic_, `code`, ' - '[links](http://github.com/ab_), ' - '[text-mention](tg://user?id=123456789) and ```python\npre```. ' - '[http://google.com/ab_](http://google.com/ab_)' + r"Test for <*bold*, _ita_\__lic_, `code`, " + "[links](http://github.com/ab_), " + "[text-mention](tg://user?id=123456789) and ```python\npre```. " + "[http://google.com/ab_](http://google.com/ab_)" ) caption_markdown = self.test_message.caption_markdown_urled assert caption_markdown == test_md_string def test_caption_markdown_v2_urled(self): test_md_string = ( - r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, ' - '[links](http://github.com/abc\\\\\\)def), ' - '[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. ' - r'[http://google\.com](http://google.com) and _bold *nested in ~strk\>trgh~ ' - 'nested in* italic_\\. ```python\nPython pre```\\.' + r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " + "[links](http://github.com/abc\\\\\\)def), " + "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " + r"[http://google\.com](http://google.com) and _bold *nested in ~strk\>trgh~ " + "nested in* italic_\\. ```python\nPython pre```\\." ) caption_markdown = self.test_message_v2.caption_markdown_v2_urled assert caption_markdown == test_md_string def test_caption_html_emoji(self): - caption = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape') - expected = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape') + caption = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") + expected = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, @@ -586,8 +583,8 @@ def test_caption_html_emoji(self): assert expected == message.caption_html def test_caption_markdown_emoji(self): - caption = b'\\U0001f469\\u200d\\U0001f469\\u200d ABC'.decode('unicode-escape') - expected = b'\\U0001f469\\u200d\\U0001f469\\u200d *ABC*'.decode('unicode-escape') + caption = b"\\U0001f469\\u200d\\U0001f469\\u200d ABC".decode("unicode-escape") + expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape") bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3) message = Message( 1, @@ -600,8 +597,8 @@ def test_caption_markdown_emoji(self): assert expected == message.caption_markdown def test_parse_entities_url_emoji(self): - url = b'http://github.com/?unicode=\\u2713\\U0001f469'.decode('unicode-escape') - text = 'some url' + url = b"http://github.com/?unicode=\\u2713\\U0001f469".decode("unicode-escape") + text = "some url" link_entity = MessageEntity(type=MessageEntity.URL, offset=0, length=8, url=url) message = Message( 1, self.from_user, self.date, self.chat, text=text, entities=[link_entity] @@ -612,23 +609,23 @@ def test_parse_entities_url_emoji(self): def test_chat_id(self, message): assert message.chat_id == message.chat.id - @pytest.mark.parametrize('_type', argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) + @pytest.mark.parametrize("_type", argvalues=[Chat.SUPERGROUP, Chat.CHANNEL]) def test_link_with_username(self, message, _type): - message.chat.username = 'username' + message.chat.username = "username" message.chat.type = _type - assert message.link == f'https://t.me/{message.chat.username}/{message.message_id}' + 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): message.chat.username = None 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}' + assert message.link == f"https://t.me/c/{3}/{message.message_id}" - @pytest.mark.parametrize('_id, username', argvalues=[(None, 'username'), (-3, None)]) + @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 @@ -639,20 +636,20 @@ def test_link_private_chats(self, message, _id, username): def test_effective_attachment(self, message_params): for i in ( - 'audio', - 'game', - 'document', - 'animation', - 'photo', - 'sticker', - 'video', - 'voice', - 'video_note', - 'contact', - 'location', - 'venue', - 'invoice', - 'successful_payment', + "audio", + "game", + "document", + "animation", + "photo", + "sticker", + "video", + "voice", + "video_note", + "contact", + "location", + "venue", + "invoice", + "successful_payment", ): item = getattr(message_params, i, None) if item: @@ -663,53 +660,53 @@ def test_effective_attachment(self, message_params): def test_reply_text(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - text = kwargs['text'] == 'test' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + text = kwargs["text"] == "test" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and text and reply assert check_shortcut_signature( - Message.reply_text, Bot.send_message, ['chat_id'], ['quote'] + Message.reply_text, Bot.send_message, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_text, message.bot, 'send_message') + assert check_shortcut_call(message.reply_text, message.bot, "send_message") assert check_defaults_handling(message.reply_text, message.bot) - monkeypatch.setattr(message.bot, 'send_message', make_assertion) - assert message.reply_text('test') - assert message.reply_text('test', quote=True) - assert message.reply_text('test', reply_to_message_id=message.message_id, quote=True) + monkeypatch.setattr(message.bot, "send_message", make_assertion) + assert message.reply_text("test") + assert message.reply_text("test", quote=True) + assert message.reply_text("test", reply_to_message_id=message.message_id, quote=True) def test_reply_markdown(self, monkeypatch, message): test_md_string = ( - r'Test for <*bold*, _ita_\__lic_, `code`, ' - '[links](http://github.com/ab_), ' - '[text-mention](tg://user?id=123456789) and ```python\npre```. ' - r'http://google.com/ab\_' + r"Test for <*bold*, _ita_\__lic_, `code`, " + "[links](http://github.com/ab_), " + "[text-mention](tg://user?id=123456789) and ```python\npre```. " + r"http://google.com/ab\_" ) def make_assertion(*_, **kwargs): - cid = kwargs['chat_id'] == message.chat_id - markdown_text = kwargs['text'] == test_md_string - markdown_enabled = kwargs['parse_mode'] == ParseMode.MARKDOWN - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + cid = kwargs["chat_id"] == message.chat_id + markdown_text = kwargs["text"] == test_md_string + markdown_enabled = kwargs["parse_mode"] == ParseMode.MARKDOWN + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return all([cid, markdown_text, reply, markdown_enabled]) assert check_shortcut_signature( - Message.reply_markdown, Bot.send_message, ['chat_id', 'parse_mode'], ['quote'] + Message.reply_markdown, Bot.send_message, ["chat_id", "parse_mode"], ["quote"] ) - assert check_shortcut_call(message.reply_text, message.bot, 'send_message') + assert check_shortcut_call(message.reply_text, message.bot, "send_message") assert check_defaults_handling(message.reply_text, message.bot) text_markdown = self.test_message.text_markdown assert text_markdown == test_md_string - monkeypatch.setattr(message.bot, 'send_message', make_assertion) + monkeypatch.setattr(message.bot, "send_message", make_assertion) assert message.reply_markdown(self.test_message.text_markdown) assert message.reply_markdown(self.test_message.text_markdown, quote=True) assert message.reply_markdown( @@ -718,33 +715,33 @@ def make_assertion(*_, **kwargs): def test_reply_markdown_v2(self, monkeypatch, message): test_md_string = ( - r'__Test__ for <*bold*, _ita\_lic_, `\\\`code`, ' - '[links](http://github.com/abc\\\\\\)def), ' - '[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. ' - r'http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. ' - '```python\nPython pre```\\.' + r"__Test__ for <*bold*, _ita\_lic_, `\\\`code`, " + "[links](http://github.com/abc\\\\\\)def), " + "[text\\-mention](tg://user?id=123456789) and ```\\`\\\\pre```\\. " + r"http://google\.com and _bold *nested in ~strk\>trgh~ nested in* italic_\. " + "```python\nPython pre```\\." ) def make_assertion(*_, **kwargs): - cid = kwargs['chat_id'] == message.chat_id - markdown_text = kwargs['text'] == test_md_string - markdown_enabled = kwargs['parse_mode'] == ParseMode.MARKDOWN_V2 - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + cid = kwargs["chat_id"] == message.chat_id + markdown_text = kwargs["text"] == test_md_string + markdown_enabled = kwargs["parse_mode"] == ParseMode.MARKDOWN_V2 + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return all([cid, markdown_text, reply, markdown_enabled]) assert check_shortcut_signature( - Message.reply_markdown_v2, Bot.send_message, ['chat_id', 'parse_mode'], ['quote'] + Message.reply_markdown_v2, Bot.send_message, ["chat_id", "parse_mode"], ["quote"] ) - assert check_shortcut_call(message.reply_text, message.bot, 'send_message') + assert check_shortcut_call(message.reply_text, message.bot, "send_message") assert check_defaults_handling(message.reply_text, message.bot) text_markdown = self.test_message_v2.text_markdown_v2 assert text_markdown == test_md_string - monkeypatch.setattr(message.bot, 'send_message', make_assertion) + monkeypatch.setattr(message.bot, "send_message", make_assertion) assert message.reply_markdown_v2(self.test_message_v2.text_markdown_v2) assert message.reply_markdown_v2(self.test_message_v2.text_markdown_v2, quote=True) assert message.reply_markdown_v2( @@ -755,35 +752,35 @@ def make_assertion(*_, **kwargs): def test_reply_html(self, monkeypatch, message): test_html_string = ( - 'Test for <bold, ita_lic, ' - r'\`code, ' + "Test for <bold, ita_lic, " + r"\`code, " r'links, ' 'text-mention and ' - r'
`\pre
. http://google.com ' - 'and bold nested in strk>trgh nested in italic. ' + r"
`\pre
. http://google.com " + "and bold nested in strk>trgh nested in italic. " '
Python pre
.' ) def make_assertion(*_, **kwargs): - cid = kwargs['chat_id'] == message.chat_id - html_text = kwargs['text'] == test_html_string - html_enabled = kwargs['parse_mode'] == ParseMode.HTML - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + cid = kwargs["chat_id"] == message.chat_id + html_text = kwargs["text"] == test_html_string + html_enabled = kwargs["parse_mode"] == ParseMode.HTML + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return all([cid, html_text, reply, html_enabled]) assert check_shortcut_signature( - Message.reply_html, Bot.send_message, ['chat_id', 'parse_mode'], ['quote'] + Message.reply_html, Bot.send_message, ["chat_id", "parse_mode"], ["quote"] ) - assert check_shortcut_call(message.reply_text, message.bot, 'send_message') + assert check_shortcut_call(message.reply_text, message.bot, "send_message") assert check_defaults_handling(message.reply_text, message.bot) text_html = self.test_message_v2.text_html assert text_html == test_html_string - monkeypatch.setattr(message.bot, 'send_message', make_assertion) + monkeypatch.setattr(message.bot, "send_message", make_assertion) assert message.reply_html(self.test_message_v2.text_html) assert message.reply_html(self.test_message_v2.text_html, quote=True) assert message.reply_html( @@ -792,419 +789,419 @@ def make_assertion(*_, **kwargs): def test_reply_media_group(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - media = kwargs['media'] == 'reply_media_group' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + media = kwargs["media"] == "reply_media_group" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and media and reply assert check_shortcut_signature( - Message.reply_media_group, Bot.send_media_group, ['chat_id'], ['quote'] + Message.reply_media_group, Bot.send_media_group, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_media_group, message.bot, 'send_media_group') + assert check_shortcut_call(message.reply_media_group, message.bot, "send_media_group") assert check_defaults_handling(message.reply_media_group, message.bot) - monkeypatch.setattr(message.bot, 'send_media_group', make_assertion) - assert message.reply_media_group(media='reply_media_group') - assert message.reply_media_group(media='reply_media_group', quote=True) + monkeypatch.setattr(message.bot, "send_media_group", make_assertion) + assert message.reply_media_group(media="reply_media_group") + assert message.reply_media_group(media="reply_media_group", quote=True) def test_reply_photo(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - photo = kwargs['photo'] == 'test_photo' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + photo = kwargs["photo"] == "test_photo" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and photo and reply assert check_shortcut_signature( - Message.reply_photo, Bot.send_photo, ['chat_id'], ['quote'] + Message.reply_photo, Bot.send_photo, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_photo, message.bot, 'send_photo') + assert check_shortcut_call(message.reply_photo, message.bot, "send_photo") assert check_defaults_handling(message.reply_photo, message.bot) - monkeypatch.setattr(message.bot, 'send_photo', make_assertion) - assert message.reply_photo(photo='test_photo') - assert message.reply_photo(photo='test_photo', quote=True) + monkeypatch.setattr(message.bot, "send_photo", make_assertion) + assert message.reply_photo(photo="test_photo") + assert message.reply_photo(photo="test_photo", quote=True) def test_reply_audio(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - audio = kwargs['audio'] == 'test_audio' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + audio = kwargs["audio"] == "test_audio" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and audio and reply assert check_shortcut_signature( - Message.reply_audio, Bot.send_audio, ['chat_id'], ['quote'] + Message.reply_audio, Bot.send_audio, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_audio, message.bot, 'send_audio') + assert check_shortcut_call(message.reply_audio, message.bot, "send_audio") assert check_defaults_handling(message.reply_audio, message.bot) - monkeypatch.setattr(message.bot, 'send_audio', make_assertion) - assert message.reply_audio(audio='test_audio') - assert message.reply_audio(audio='test_audio', quote=True) + monkeypatch.setattr(message.bot, "send_audio", make_assertion) + assert message.reply_audio(audio="test_audio") + assert message.reply_audio(audio="test_audio", quote=True) def test_reply_document(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - document = kwargs['document'] == 'test_document' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + document = kwargs["document"] == "test_document" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and document and reply assert check_shortcut_signature( - Message.reply_document, Bot.send_document, ['chat_id'], ['quote'] + Message.reply_document, Bot.send_document, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_document, message.bot, 'send_document') + assert check_shortcut_call(message.reply_document, message.bot, "send_document") assert check_defaults_handling(message.reply_document, message.bot) - monkeypatch.setattr(message.bot, 'send_document', make_assertion) - assert message.reply_document(document='test_document') - assert message.reply_document(document='test_document', quote=True) + monkeypatch.setattr(message.bot, "send_document", make_assertion) + assert message.reply_document(document="test_document") + assert message.reply_document(document="test_document", quote=True) def test_reply_animation(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - animation = kwargs['animation'] == 'test_animation' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + animation = kwargs["animation"] == "test_animation" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and animation and reply assert check_shortcut_signature( - Message.reply_animation, Bot.send_animation, ['chat_id'], ['quote'] + Message.reply_animation, Bot.send_animation, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_animation, message.bot, 'send_animation') + assert check_shortcut_call(message.reply_animation, message.bot, "send_animation") assert check_defaults_handling(message.reply_animation, message.bot) - monkeypatch.setattr(message.bot, 'send_animation', make_assertion) - assert message.reply_animation(animation='test_animation') - assert message.reply_animation(animation='test_animation', quote=True) + monkeypatch.setattr(message.bot, "send_animation", make_assertion) + assert message.reply_animation(animation="test_animation") + assert message.reply_animation(animation="test_animation", quote=True) def test_reply_sticker(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - sticker = kwargs['sticker'] == 'test_sticker' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + sticker = kwargs["sticker"] == "test_sticker" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and sticker and reply assert check_shortcut_signature( - Message.reply_sticker, Bot.send_sticker, ['chat_id'], ['quote'] + Message.reply_sticker, Bot.send_sticker, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_sticker, message.bot, 'send_sticker') + assert check_shortcut_call(message.reply_sticker, message.bot, "send_sticker") assert check_defaults_handling(message.reply_sticker, message.bot) - monkeypatch.setattr(message.bot, 'send_sticker', make_assertion) - assert message.reply_sticker(sticker='test_sticker') - assert message.reply_sticker(sticker='test_sticker', quote=True) + monkeypatch.setattr(message.bot, "send_sticker", make_assertion) + assert message.reply_sticker(sticker="test_sticker") + assert message.reply_sticker(sticker="test_sticker", quote=True) def test_reply_video(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - video = kwargs['video'] == 'test_video' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + video = kwargs["video"] == "test_video" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and video and reply assert check_shortcut_signature( - Message.reply_video, Bot.send_video, ['chat_id'], ['quote'] + Message.reply_video, Bot.send_video, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_video, message.bot, 'send_video') + assert check_shortcut_call(message.reply_video, message.bot, "send_video") assert check_defaults_handling(message.reply_video, message.bot) - monkeypatch.setattr(message.bot, 'send_video', make_assertion) - assert message.reply_video(video='test_video') - assert message.reply_video(video='test_video', quote=True) + monkeypatch.setattr(message.bot, "send_video", make_assertion) + assert message.reply_video(video="test_video") + assert message.reply_video(video="test_video", quote=True) def test_reply_video_note(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - video_note = kwargs['video_note'] == 'test_video_note' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + video_note = kwargs["video_note"] == "test_video_note" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and video_note and reply assert check_shortcut_signature( - Message.reply_video_note, Bot.send_video_note, ['chat_id'], ['quote'] + Message.reply_video_note, Bot.send_video_note, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_video_note, message.bot, 'send_video_note') + assert check_shortcut_call(message.reply_video_note, message.bot, "send_video_note") assert check_defaults_handling(message.reply_video_note, message.bot) - monkeypatch.setattr(message.bot, 'send_video_note', make_assertion) - assert message.reply_video_note(video_note='test_video_note') - assert message.reply_video_note(video_note='test_video_note', quote=True) + monkeypatch.setattr(message.bot, "send_video_note", make_assertion) + assert message.reply_video_note(video_note="test_video_note") + assert message.reply_video_note(video_note="test_video_note", quote=True) def test_reply_voice(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - voice = kwargs['voice'] == 'test_voice' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + voice = kwargs["voice"] == "test_voice" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and voice and reply assert check_shortcut_signature( - Message.reply_voice, Bot.send_voice, ['chat_id'], ['quote'] + Message.reply_voice, Bot.send_voice, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_voice, message.bot, 'send_voice') + assert check_shortcut_call(message.reply_voice, message.bot, "send_voice") assert check_defaults_handling(message.reply_voice, message.bot) - monkeypatch.setattr(message.bot, 'send_voice', make_assertion) - assert message.reply_voice(voice='test_voice') - assert message.reply_voice(voice='test_voice', quote=True) + monkeypatch.setattr(message.bot, "send_voice", make_assertion) + assert message.reply_voice(voice="test_voice") + assert message.reply_voice(voice="test_voice", quote=True) def test_reply_location(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - location = kwargs['location'] == 'test_location' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + location = kwargs["location"] == "test_location" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and location and reply assert check_shortcut_signature( - Message.reply_location, Bot.send_location, ['chat_id'], ['quote'] + Message.reply_location, Bot.send_location, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_location, message.bot, 'send_location') + assert check_shortcut_call(message.reply_location, message.bot, "send_location") assert check_defaults_handling(message.reply_location, message.bot) - monkeypatch.setattr(message.bot, 'send_location', make_assertion) - assert message.reply_location(location='test_location') - assert message.reply_location(location='test_location', quote=True) + monkeypatch.setattr(message.bot, "send_location", make_assertion) + assert message.reply_location(location="test_location") + assert message.reply_location(location="test_location", quote=True) def test_reply_venue(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - venue = kwargs['venue'] == 'test_venue' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + venue = kwargs["venue"] == "test_venue" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and venue and reply assert check_shortcut_signature( - Message.reply_venue, Bot.send_venue, ['chat_id'], ['quote'] + Message.reply_venue, Bot.send_venue, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_venue, message.bot, 'send_venue') + assert check_shortcut_call(message.reply_venue, message.bot, "send_venue") assert check_defaults_handling(message.reply_venue, message.bot) - monkeypatch.setattr(message.bot, 'send_venue', make_assertion) - assert message.reply_venue(venue='test_venue') - assert message.reply_venue(venue='test_venue', quote=True) + monkeypatch.setattr(message.bot, "send_venue", make_assertion) + assert message.reply_venue(venue="test_venue") + assert message.reply_venue(venue="test_venue", quote=True) def test_reply_contact(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - contact = kwargs['contact'] == 'test_contact' - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + contact = kwargs["contact"] == "test_contact" + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and contact and reply assert check_shortcut_signature( - Message.reply_contact, Bot.send_contact, ['chat_id'], ['quote'] + Message.reply_contact, Bot.send_contact, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_contact, message.bot, 'send_contact') + assert check_shortcut_call(message.reply_contact, message.bot, "send_contact") assert check_defaults_handling(message.reply_contact, message.bot) - monkeypatch.setattr(message.bot, 'send_contact', make_assertion) - assert message.reply_contact(contact='test_contact') - assert message.reply_contact(contact='test_contact', quote=True) + monkeypatch.setattr(message.bot, "send_contact", make_assertion) + assert message.reply_contact(contact="test_contact") + assert message.reply_contact(contact="test_contact", quote=True) def test_reply_poll(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - question = kwargs['question'] == 'test_poll' - options = kwargs['options'] == ['1', '2', '3'] - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + question = kwargs["question"] == "test_poll" + options = kwargs["options"] == ["1", "2", "3"] + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and question and options and reply - assert check_shortcut_signature(Message.reply_poll, Bot.send_poll, ['chat_id'], ['quote']) - assert check_shortcut_call(message.reply_poll, message.bot, 'send_poll') + assert check_shortcut_signature(Message.reply_poll, Bot.send_poll, ["chat_id"], ["quote"]) + assert check_shortcut_call(message.reply_poll, message.bot, "send_poll") assert check_defaults_handling(message.reply_poll, message.bot) - monkeypatch.setattr(message.bot, 'send_poll', make_assertion) - assert message.reply_poll(question='test_poll', options=['1', '2', '3']) - assert message.reply_poll(question='test_poll', quote=True, options=['1', '2', '3']) + monkeypatch.setattr(message.bot, "send_poll", make_assertion) + assert message.reply_poll(question="test_poll", options=["1", "2", "3"]) + assert message.reply_poll(question="test_poll", quote=True, options=["1", "2", "3"]) def test_reply_dice(self, monkeypatch, message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - contact = kwargs['disable_notification'] is True - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + id_ = kwargs["chat_id"] == message.chat_id + contact = kwargs["disable_notification"] is True + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return id_ and contact and reply - assert check_shortcut_signature(Message.reply_dice, Bot.send_dice, ['chat_id'], ['quote']) - assert check_shortcut_call(message.reply_dice, message.bot, 'send_dice') + assert check_shortcut_signature(Message.reply_dice, Bot.send_dice, ["chat_id"], ["quote"]) + assert check_shortcut_call(message.reply_dice, message.bot, "send_dice") assert check_defaults_handling(message.reply_dice, message.bot) - monkeypatch.setattr(message.bot, 'send_dice', make_assertion) + monkeypatch.setattr(message.bot, "send_dice", make_assertion) assert message.reply_dice(disable_notification=True) assert message.reply_dice(disable_notification=True, quote=True) def test_reply_action(self, monkeypatch, message: Message): def make_assertion(*_, **kwargs): - id_ = kwargs['chat_id'] == message.chat_id - action = kwargs['action'] == ChatAction.TYPING + id_ = kwargs["chat_id"] == message.chat_id + action = kwargs["action"] == ChatAction.TYPING return id_ and action assert check_shortcut_signature( - Message.reply_chat_action, Bot.send_chat_action, ['chat_id'], [] + Message.reply_chat_action, Bot.send_chat_action, ["chat_id"], [] ) - assert check_shortcut_call(message.reply_chat_action, message.bot, 'send_chat_action') + assert check_shortcut_call(message.reply_chat_action, message.bot, "send_chat_action") assert check_defaults_handling(message.reply_chat_action, message.bot) - monkeypatch.setattr(message.bot, 'send_chat_action', make_assertion) + monkeypatch.setattr(message.bot, "send_chat_action", make_assertion) assert message.reply_chat_action(action=ChatAction.TYPING) def test_reply_game(self, monkeypatch, message: Message): def make_assertion(*_, **kwargs): return ( - kwargs['chat_id'] == message.chat_id and kwargs['game_short_name'] == 'test_game' + kwargs["chat_id"] == message.chat_id and kwargs["game_short_name"] == "test_game" ) - assert check_shortcut_signature(Message.reply_game, Bot.send_game, ['chat_id'], ['quote']) - assert check_shortcut_call(message.reply_game, message.bot, 'send_game') + assert check_shortcut_signature(Message.reply_game, Bot.send_game, ["chat_id"], ["quote"]) + assert check_shortcut_call(message.reply_game, message.bot, "send_game") assert check_defaults_handling(message.reply_game, message.bot) - monkeypatch.setattr(message.bot, 'send_game', make_assertion) - assert message.reply_game(game_short_name='test_game') - assert message.reply_game(game_short_name='test_game', quote=True) + monkeypatch.setattr(message.bot, "send_game", make_assertion) + assert message.reply_game(game_short_name="test_game") + assert message.reply_game(game_short_name="test_game", quote=True) def test_reply_invoice(self, monkeypatch, message: Message): def make_assertion(*_, **kwargs): - title = kwargs['title'] == 'title' - description = kwargs['description'] == 'description' - payload = kwargs['payload'] == 'payload' - provider_token = kwargs['provider_token'] == 'provider_token' - currency = kwargs['currency'] == 'currency' - prices = kwargs['prices'] == 'prices' + title = kwargs["title"] == "title" + description = kwargs["description"] == "description" + payload = kwargs["payload"] == "payload" + provider_token = kwargs["provider_token"] == "provider_token" + currency = kwargs["currency"] == "currency" + prices = kwargs["prices"] == "prices" args = title and description and payload and provider_token and currency and prices - return kwargs['chat_id'] == message.chat_id and args + return kwargs["chat_id"] == message.chat_id and args assert check_shortcut_signature( - Message.reply_invoice, Bot.send_invoice, ['chat_id'], ['quote'] + Message.reply_invoice, Bot.send_invoice, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.reply_invoice, message.bot, 'send_invoice') + assert check_shortcut_call(message.reply_invoice, message.bot, "send_invoice") assert check_defaults_handling(message.reply_invoice, message.bot) - monkeypatch.setattr(message.bot, 'send_invoice', make_assertion) + monkeypatch.setattr(message.bot, "send_invoice", make_assertion) assert message.reply_invoice( - 'title', - 'description', - 'payload', - 'provider_token', - 'currency', - 'prices', + "title", + "description", + "payload", + "provider_token", + "currency", + "prices", ) assert message.reply_invoice( - 'title', - 'description', - 'payload', - 'provider_token', - 'currency', - 'prices', + "title", + "description", + "payload", + "provider_token", + "currency", + "prices", quote=True, ) - @pytest.mark.parametrize('disable_notification', [False, True]) + @pytest.mark.parametrize("disable_notification", [False, True]) def test_forward(self, monkeypatch, message, disable_notification): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == 123456 - from_chat = kwargs['from_chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - notification = kwargs['disable_notification'] == disable_notification + chat_id = kwargs["chat_id"] == 123456 + from_chat = kwargs["from_chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + notification = kwargs["disable_notification"] == disable_notification return chat_id and from_chat and message_id and notification assert check_shortcut_signature( - Message.forward, Bot.forward_message, ['from_chat_id', 'message_id'], [] + Message.forward, Bot.forward_message, ["from_chat_id", "message_id"], [] ) - assert check_shortcut_call(message.forward, message.bot, 'forward_message') + assert check_shortcut_call(message.forward, message.bot, "forward_message") assert check_defaults_handling(message.forward, message.bot) - monkeypatch.setattr(message.bot, 'forward_message', make_assertion) + monkeypatch.setattr(message.bot, "forward_message", make_assertion) assert message.forward(123456, disable_notification=disable_notification) assert not message.forward(635241) - @pytest.mark.parametrize('disable_notification', [True, False]) + @pytest.mark.parametrize("disable_notification", [True, False]) def test_copy(self, monkeypatch, message, disable_notification): keyboard = [[1, 2]] def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == 123456 - from_chat = kwargs['from_chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - notification = kwargs['disable_notification'] == disable_notification - if kwargs.get('reply_markup') is not None: - reply_markup = kwargs['reply_markup'] is keyboard + chat_id = kwargs["chat_id"] == 123456 + from_chat = kwargs["from_chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + notification = kwargs["disable_notification"] == disable_notification + if kwargs.get("reply_markup") is not None: + reply_markup = kwargs["reply_markup"] is keyboard else: reply_markup = True return chat_id and from_chat and message_id and notification and reply_markup assert check_shortcut_signature( - Message.copy, Bot.copy_message, ['from_chat_id', 'message_id'], [] + Message.copy, Bot.copy_message, ["from_chat_id", "message_id"], [] ) - assert check_shortcut_call(message.copy, message.bot, 'copy_message') + assert check_shortcut_call(message.copy, message.bot, "copy_message") assert check_defaults_handling(message.copy, message.bot) - monkeypatch.setattr(message.bot, 'copy_message', make_assertion) + monkeypatch.setattr(message.bot, "copy_message", make_assertion) assert message.copy(123456, disable_notification=disable_notification) assert message.copy( 123456, reply_markup=keyboard, disable_notification=disable_notification ) assert not message.copy(635241) - @pytest.mark.parametrize('disable_notification', [True, False]) + @pytest.mark.parametrize("disable_notification", [True, False]) def test_reply_copy(self, monkeypatch, message, disable_notification): keyboard = [[1, 2]] def make_assertion(*_, **kwargs): - chat_id = kwargs['from_chat_id'] == 123456 - from_chat = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == 456789 - notification = kwargs['disable_notification'] == disable_notification - if kwargs.get('reply_markup') is not None: - reply_markup = kwargs['reply_markup'] is keyboard + chat_id = kwargs["from_chat_id"] == 123456 + from_chat = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == 456789 + notification = kwargs["disable_notification"] == disable_notification + if kwargs.get("reply_markup") is not None: + reply_markup = kwargs["reply_markup"] is keyboard else: reply_markup = True - if kwargs.get('reply_to_message_id') is not None: - reply = kwargs['reply_to_message_id'] == message.message_id + if kwargs.get("reply_to_message_id") is not None: + reply = kwargs["reply_to_message_id"] == message.message_id else: reply = True return chat_id and from_chat and message_id and notification and reply_markup and reply assert check_shortcut_signature( - Message.reply_copy, Bot.copy_message, ['chat_id'], ['quote'] + Message.reply_copy, Bot.copy_message, ["chat_id"], ["quote"] ) - assert check_shortcut_call(message.copy, message.bot, 'copy_message') + assert check_shortcut_call(message.copy, message.bot, "copy_message") assert check_defaults_handling(message.copy, message.bot) - monkeypatch.setattr(message.bot, 'copy_message', make_assertion) + monkeypatch.setattr(message.bot, "copy_message", make_assertion) assert message.reply_copy(123456, 456789, disable_notification=disable_notification) assert message.reply_copy( 123456, 456789, reply_markup=keyboard, disable_notification=disable_notification @@ -1222,268 +1219,268 @@ def make_assertion(*_, **kwargs): def test_edit_text(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - text = kwargs['text'] == 'test' + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + text = kwargs["text"] == "test" return chat_id and message_id and text assert check_shortcut_signature( Message.edit_text, Bot.edit_message_text, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.edit_text, message.bot, - 'edit_message_text', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "edit_message_text", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.edit_text, message.bot) - monkeypatch.setattr(message.bot, 'edit_message_text', make_assertion) - assert message.edit_text(text='test') + monkeypatch.setattr(message.bot, "edit_message_text", make_assertion) + assert message.edit_text(text="test") def test_edit_caption(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - caption = kwargs['caption'] == 'new caption' + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + caption = kwargs["caption"] == "new caption" return chat_id and message_id and caption assert check_shortcut_signature( Message.edit_caption, Bot.edit_message_caption, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.edit_caption, message.bot, - 'edit_message_caption', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "edit_message_caption", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.edit_caption, message.bot) - monkeypatch.setattr(message.bot, 'edit_message_caption', make_assertion) - assert message.edit_caption(caption='new caption') + monkeypatch.setattr(message.bot, "edit_message_caption", make_assertion) + assert message.edit_caption(caption="new caption") def test_edit_media(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - media = kwargs['media'] == 'my_media' + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + media = kwargs["media"] == "my_media" return chat_id and message_id and media assert check_shortcut_signature( Message.edit_media, Bot.edit_message_media, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.edit_media, message.bot, - 'edit_message_media', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "edit_message_media", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.edit_media, message.bot) - monkeypatch.setattr(message.bot, 'edit_message_media', make_assertion) - assert message.edit_media('my_media') + monkeypatch.setattr(message.bot, "edit_message_media", make_assertion) + assert message.edit_media("my_media") def test_edit_reply_markup(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - reply_markup = kwargs['reply_markup'] == [['1', '2']] + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + reply_markup = kwargs["reply_markup"] == [["1", "2"]] return chat_id and message_id and reply_markup assert check_shortcut_signature( Message.edit_reply_markup, Bot.edit_message_reply_markup, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.edit_reply_markup, message.bot, - 'edit_message_reply_markup', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "edit_message_reply_markup", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.edit_reply_markup, message.bot) - monkeypatch.setattr(message.bot, 'edit_message_reply_markup', make_assertion) - assert message.edit_reply_markup(reply_markup=[['1', '2']]) + monkeypatch.setattr(message.bot, "edit_message_reply_markup", make_assertion) + assert message.edit_reply_markup(reply_markup=[["1", "2"]]) def test_edit_live_location(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - latitude = kwargs['latitude'] == 1 - longitude = kwargs['longitude'] == 2 + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + latitude = kwargs["latitude"] == 1 + longitude = kwargs["longitude"] == 2 return chat_id and message_id and longitude and latitude assert check_shortcut_signature( Message.edit_live_location, Bot.edit_message_live_location, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.edit_live_location, message.bot, - 'edit_message_live_location', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "edit_message_live_location", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.edit_live_location, message.bot) - monkeypatch.setattr(message.bot, 'edit_message_live_location', make_assertion) + monkeypatch.setattr(message.bot, "edit_message_live_location", make_assertion) assert message.edit_live_location(latitude=1, longitude=2) def test_stop_live_location(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( Message.stop_live_location, Bot.stop_message_live_location, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.stop_live_location, message.bot, - 'stop_message_live_location', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "stop_message_live_location", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.stop_live_location, message.bot) - monkeypatch.setattr(message.bot, 'stop_message_live_location', make_assertion) + monkeypatch.setattr(message.bot, "stop_message_live_location", make_assertion) assert message.stop_live_location() def test_set_game_score(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - user_id = kwargs['user_id'] == 1 - score = kwargs['score'] == 2 + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + user_id = kwargs["user_id"] == 1 + score = kwargs["score"] == 2 return chat_id and message_id and user_id and score assert check_shortcut_signature( Message.set_game_score, Bot.set_game_score, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.set_game_score, message.bot, - 'set_game_score', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "set_game_score", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.set_game_score, message.bot) - monkeypatch.setattr(message.bot, 'set_game_score', make_assertion) + monkeypatch.setattr(message.bot, "set_game_score", make_assertion) assert message.set_game_score(user_id=1, score=2) def test_get_game_high_scores(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id - user_id = kwargs['user_id'] == 1 + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id + user_id = kwargs["user_id"] == 1 return chat_id and message_id and user_id assert check_shortcut_signature( Message.get_game_high_scores, Bot.get_game_high_scores, - ['chat_id', 'message_id', 'inline_message_id'], + ["chat_id", "message_id", "inline_message_id"], [], ) assert check_shortcut_call( message.get_game_high_scores, message.bot, - 'get_game_high_scores', - skip_params=['inline_message_id'], - shortcut_kwargs=['message_id', 'chat_id'], + "get_game_high_scores", + skip_params=["inline_message_id"], + shortcut_kwargs=["message_id", "chat_id"], ) assert check_defaults_handling(message.get_game_high_scores, message.bot) - monkeypatch.setattr(message.bot, 'get_game_high_scores', make_assertion) + monkeypatch.setattr(message.bot, "get_game_high_scores", make_assertion) assert message.get_game_high_scores(user_id=1) def test_delete(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( - Message.delete, Bot.delete_message, ['chat_id', 'message_id'], [] + Message.delete, Bot.delete_message, ["chat_id", "message_id"], [] ) - assert check_shortcut_call(message.delete, message.bot, 'delete_message') + assert check_shortcut_call(message.delete, message.bot, "delete_message") assert check_defaults_handling(message.delete, message.bot) - monkeypatch.setattr(message.bot, 'delete_message', make_assertion) + monkeypatch.setattr(message.bot, "delete_message", make_assertion) assert message.delete() def test_stop_poll(self, monkeypatch, message): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( - Message.stop_poll, Bot.stop_poll, ['chat_id', 'message_id'], [] + Message.stop_poll, Bot.stop_poll, ["chat_id", "message_id"], [] ) - assert check_shortcut_call(message.stop_poll, message.bot, 'stop_poll') + assert check_shortcut_call(message.stop_poll, message.bot, "stop_poll") assert check_defaults_handling(message.stop_poll, message.bot) - monkeypatch.setattr(message.bot, 'stop_poll', make_assertion) + monkeypatch.setattr(message.bot, "stop_poll", make_assertion) assert message.stop_poll() def test_pin(self, monkeypatch, message): def make_assertion(*args, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( - Message.pin, Bot.pin_chat_message, ['chat_id', 'message_id'], [] + Message.pin, Bot.pin_chat_message, ["chat_id", "message_id"], [] ) - assert check_shortcut_call(message.pin, message.bot, 'pin_chat_message') + assert check_shortcut_call(message.pin, message.bot, "pin_chat_message") assert check_defaults_handling(message.pin, message.bot) - monkeypatch.setattr(message.bot, 'pin_chat_message', make_assertion) + monkeypatch.setattr(message.bot, "pin_chat_message", make_assertion) assert message.pin() def test_unpin(self, monkeypatch, message): def make_assertion(*args, **kwargs): - chat_id = kwargs['chat_id'] == message.chat_id - message_id = kwargs['message_id'] == message.message_id + chat_id = kwargs["chat_id"] == message.chat_id + message_id = kwargs["message_id"] == message.message_id return chat_id and message_id assert check_shortcut_signature( - Message.unpin, Bot.unpin_chat_message, ['chat_id', 'message_id'], [] + Message.unpin, Bot.unpin_chat_message, ["chat_id", "message_id"], [] ) assert check_shortcut_call( message.unpin, message.bot, - 'unpin_chat_message', - shortcut_kwargs=['chat_id', 'message_id'], + "unpin_chat_message", + shortcut_kwargs=["chat_id", "message_id"], ) assert check_defaults_handling(message.unpin, message.bot) - monkeypatch.setattr(message.bot, 'unpin_chat_message', make_assertion) + monkeypatch.setattr(message.bot, "unpin_chat_message", make_assertion) assert message.unpin() def test_default_quote(self, message): @@ -1519,7 +1516,7 @@ def test_equality(self): self.chat, from_user=self.from_user, ) - c = Message(id_, self.date, Chat(123, Chat.GROUP), from_user=User(0, '', False)) + c = Message(id_, self.date, Chat(123, Chat.GROUP), from_user=User(0, "", False)) d = Message(0, self.date, self.chat, from_user=self.from_user) e = Update(id_) diff --git a/tests/test_messageautodeletetimerchanged.py b/tests/test_messageautodeletetimerchanged.py index 15a62f73e06..525db7d281b 100644 --- a/tests/test_messageautodeletetimerchanged.py +++ b/tests/test_messageautodeletetimerchanged.py @@ -22,17 +22,14 @@ class TestMessageAutoDeleteTimerChanged: message_auto_delete_time = 100 - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): action = MessageAutoDeleteTimerChanged(self.message_auto_delete_time) for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self): - json_dict = {'message_auto_delete_time': self.message_auto_delete_time} + json_dict = {"message_auto_delete_time": self.message_auto_delete_time} madtc = MessageAutoDeleteTimerChanged.de_json(json_dict, None) assert madtc.message_auto_delete_time == self.message_auto_delete_time diff --git a/tests/test_messageentity.py b/tests/test_messageentity.py index 2f632c073c1..c7d9068d1f0 100644 --- a/tests/test_messageentity.py +++ b/tests/test_messageentity.py @@ -26,10 +26,10 @@ def message_entity(request): type_ = request.param url = None if type_ == MessageEntity.TEXT_LINK: - url = 't.me' + url = "t.me" user = None if type_ == MessageEntity.TEXT_MENTION: - user = User(1, 'test_user', False) + user = User(1, "test_user", False) language = None if type == MessageEntity.PRE: language = "python" @@ -37,22 +37,19 @@ def message_entity(request): class TestMessageEntity: - type_ = 'url' + type_ = "url" offset = 1 length = 2 - url = 'url' + url = "url" - def test_slot_behaviour(self, message_entity, recwarn, mro_slots): + def test_slot_behaviour(self, message_entity, mro_slots): inst = message_entity for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): - json_dict = {'type': self.type_, 'offset': self.offset, 'length': self.length} + json_dict = {"type": self.type_, "offset": self.offset, "length": self.length} entity = MessageEntity.de_json(json_dict, bot) assert entity.type == self.type_ @@ -63,15 +60,15 @@ def test_to_dict(self, message_entity): entity_dict = message_entity.to_dict() assert isinstance(entity_dict, dict) - assert entity_dict['type'] == message_entity.type - assert entity_dict['offset'] == message_entity.offset - assert entity_dict['length'] == message_entity.length + assert entity_dict["type"] == message_entity.type + assert entity_dict["offset"] == message_entity.offset + assert entity_dict["length"] == message_entity.length if message_entity.url: - assert entity_dict['url'] == message_entity.url + assert entity_dict["url"] == message_entity.url if message_entity.user: - assert entity_dict['user'] == message_entity.user.to_dict() + assert entity_dict["user"] == message_entity.user.to_dict() if message_entity.language: - assert entity_dict['language'] == message_entity.language + assert entity_dict["language"] == message_entity.language def test_equality(self): a = MessageEntity(MessageEntity.BOLD, 2, 3) diff --git a/tests/test_messagehandler.py b/tests/test_messagehandler.py index 29d0c3d1ca3..d3dd433172f 100644 --- a/tests/test_messagehandler.py +++ b/tests/test_messagehandler.py @@ -20,7 +20,6 @@ from queue import Queue import pytest -from telegram.utils.deprecate import TelegramDeprecationWarning from telegram import ( Message, @@ -36,71 +35,51 @@ ) from telegram.ext import Filters, MessageHandler, CallbackContext, JobQueue, UpdateFilter -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'callback_query', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "callback_query", + "inline_query", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=1, **request.param) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def message(bot): - return Message(1, None, Chat(1, ''), from_user=User(1, '', False), bot=bot) + return Message(1, None, Chat(1, ""), from_user=User(1, "", False), bot=bot) class TestMessageHandler: test_flag = False SRE_TYPE = type(re.match("", "")) - def test_slot_behaviour(self, recwarn, mro_slots): - handler = MessageHandler(Filters.all, self.callback_basic) + def test_slot_behaviour(self, mro_slots): + handler = MessageHandler(Filters.all, self.callback_context) for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -140,80 +119,13 @@ def callback_context_regex2(self, update, context): num = len(context.matches) == 2 self.test_flag = types and num - def test_basic(self, dp, message): - handler = MessageHandler(None, self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(Update(0, message)) - dp.process_update(Update(0, message)) - assert self.test_flag - - def test_deprecation_warning(self): - with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'): - MessageHandler(None, self.callback_basic, edited_updates=True) - with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'): - MessageHandler(None, self.callback_basic, message_updates=False) - with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'): - MessageHandler(None, self.callback_basic, channel_post_updates=True) - - def test_edited_deprecated(self, message): - handler = MessageHandler( - None, - self.callback_basic, - edited_updates=True, - message_updates=False, - channel_post_updates=False, - ) - - assert handler.check_update(Update(0, edited_message=message)) - assert not handler.check_update(Update(0, message=message)) - assert not handler.check_update(Update(0, channel_post=message)) - assert handler.check_update(Update(0, edited_channel_post=message)) - - def test_channel_post_deprecated(self, message): - handler = MessageHandler( - None, - self.callback_basic, - edited_updates=False, - message_updates=False, - channel_post_updates=True, - ) - assert not handler.check_update(Update(0, edited_message=message)) - assert not handler.check_update(Update(0, message=message)) - assert handler.check_update(Update(0, channel_post=message)) - assert not handler.check_update(Update(0, edited_channel_post=message)) - - def test_multiple_flags_deprecated(self, message): - handler = MessageHandler( - None, - self.callback_basic, - edited_updates=True, - message_updates=True, - channel_post_updates=True, - ) - - assert handler.check_update(Update(0, edited_message=message)) - assert handler.check_update(Update(0, message=message)) - assert handler.check_update(Update(0, channel_post=message)) - assert handler.check_update(Update(0, edited_channel_post=message)) - - def test_none_allowed_deprecated(self): - with pytest.raises(ValueError, match='are all False'): - MessageHandler( - None, - self.callback_basic, - message_updates=False, - channel_post_updates=False, - edited_updates=False, - ) - def test_with_filter(self, message): - handler = MessageHandler(Filters.group, self.callback_basic) + handler = MessageHandler(Filters.chat_type.group, self.callback_context) - message.chat.type = 'group' + message.chat.type = "group" assert handler.check_update(Update(0, message)) - message.chat.type = 'private' + message.chat.type = "private" assert not handler.check_update(Update(0, message)) def test_callback_query_with_filter(self, message): @@ -224,7 +136,7 @@ def filter(self, u): self.flag = True test_filter = TestFilter() - handler = MessageHandler(test_filter, self.callback_basic) + handler = MessageHandler(test_filter, self.callback_context) update = Update(1, callback_query=CallbackQuery(1, None, None, message=message)) @@ -238,110 +150,61 @@ def test_specific_filters(self, message): & ~Filters.update.channel_post & Filters.update.edited_channel_post ) - handler = MessageHandler(f, self.callback_basic) + handler = MessageHandler(f, self.callback_context) assert not handler.check_update(Update(0, edited_message=message)) assert not handler.check_update(Update(0, message=message)) assert not handler.check_update(Update(0, channel_post=message)) assert handler.check_update(Update(0, edited_channel_post=message)) - def test_pass_user_or_chat_data(self, dp, message): - handler = MessageHandler(None, self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = MessageHandler(None, self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = MessageHandler( - None, self.callback_data_2, pass_chat_data=True, pass_user_data=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, message): - handler = MessageHandler(None, self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = MessageHandler(None, self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = MessageHandler( - None, self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - def test_other_update_types(self, false_update): - handler = MessageHandler(None, self.callback_basic, edited_updates=True) + handler = MessageHandler(None, self.callback_context) assert not handler.check_update(false_update) - def test_context(self, cdp, message): + def test_context(self, dp, message): handler = MessageHandler( - None, self.callback_context, edited_updates=True, channel_post_updates=True + None, + self.callback_context, ) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(Update(0, message=message)) + dp.process_update(Update(0, message=message)) assert self.test_flag self.test_flag = False - cdp.process_update(Update(0, edited_message=message)) + dp.process_update(Update(0, edited_message=message)) assert self.test_flag self.test_flag = False - cdp.process_update(Update(0, channel_post=message)) + dp.process_update(Update(0, channel_post=message)) assert self.test_flag self.test_flag = False - cdp.process_update(Update(0, edited_channel_post=message)) + dp.process_update(Update(0, edited_channel_post=message)) assert self.test_flag - def test_context_regex(self, cdp, message): - handler = MessageHandler(Filters.regex('one two'), self.callback_context_regex1) - cdp.add_handler(handler) + def test_context_regex(self, dp, message): + handler = MessageHandler(Filters.regex("one two"), self.callback_context_regex1) + dp.add_handler(handler) - message.text = 'not it' - cdp.process_update(Update(0, message)) + message.text = "not it" + dp.process_update(Update(0, message)) assert not self.test_flag - message.text += ' one two now it is' - cdp.process_update(Update(0, message)) + message.text += " one two now it is" + dp.process_update(Update(0, message)) assert self.test_flag - def test_context_multiple_regex(self, cdp, message): + def test_context_multiple_regex(self, dp, message): handler = MessageHandler( - Filters.regex('one') & Filters.regex('two'), self.callback_context_regex2 + Filters.regex("one") & Filters.regex("two"), self.callback_context_regex2 ) - cdp.add_handler(handler) + dp.add_handler(handler) - message.text = 'not it' - cdp.process_update(Update(0, message)) + message.text = "not it" + dp.process_update(Update(0, message)) assert not self.test_flag - message.text += ' one two now it is' - cdp.process_update(Update(0, message)) + message.text += " one two now it is" + dp.process_update(Update(0, message)) assert self.test_flag diff --git a/tests/test_messageid.py b/tests/test_messageid.py index 2573c13d8e5..1d90ca5799c 100644 --- a/tests/test_messageid.py +++ b/tests/test_messageid.py @@ -27,16 +27,13 @@ def message_id(): class TestMessageId: m_id = 1234 - def test_slot_behaviour(self, message_id, recwarn, mro_slots): + def test_slot_behaviour(self, message_id, mro_slots): for attr in message_id.__slots__: - assert getattr(message_id, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not message_id.__dict__, f"got missing slot(s): {message_id.__dict__}" + assert getattr(message_id, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(message_id)) == len(set(mro_slots(message_id))), "duplicate slot" - message_id.custom, message_id.message_id = 'should give warning', self.m_id - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self): - json_dict = {'message_id': self.m_id} + json_dict = {"message_id": self.m_id} message_id = MessageId.de_json(json_dict, None) assert message_id.message_id == self.m_id @@ -45,13 +42,13 @@ def test_to_dict(self, message_id): message_id_dict = message_id.to_dict() assert isinstance(message_id_dict, dict) - assert message_id_dict['message_id'] == message_id.message_id + assert message_id_dict["message_id"] == message_id.message_id def test_equality(self): a = MessageId(message_id=1) b = MessageId(message_id=1) c = MessageId(message_id=2) - d = User(id=1, first_name='name', is_bot=False) + d = User(id=1, first_name="name", is_bot=False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_messagequeue.py b/tests/test_messagequeue.py deleted file mode 100644 index 122207b9f04..00000000000 --- a/tests/test_messagequeue.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -import os -from time import sleep, perf_counter - -import pytest - -import telegram.ext.messagequeue as mq - - -@pytest.mark.skipif( - os.getenv('GITHUB_ACTIONS', False) and os.name == 'nt', - reason="On windows precise timings are not accurate.", -) -class TestDelayQueue: - N = 128 - burst_limit = 30 - time_limit_ms = 1000 - margin_ms = 0 - testtimes = [] - - def call(self): - self.testtimes.append(perf_counter()) - - def test_delayqueue_limits(self): - dsp = mq.DelayQueue( - burst_limit=self.burst_limit, time_limit_ms=self.time_limit_ms, autostart=True - ) - assert dsp.is_alive() is True - - for _ in range(self.N): - dsp(self.call) - - starttime = perf_counter() - # wait up to 20 sec more than needed - app_endtime = (self.N * self.burst_limit / (1000 * self.time_limit_ms)) + starttime + 20 - while not dsp._queue.empty() and perf_counter() < app_endtime: - sleep(1) - assert dsp._queue.empty() is True # check loop exit condition - - dsp.stop() - assert dsp.is_alive() is False - - assert self.testtimes or self.N == 0 - passes, fails = [], [] - delta = (self.time_limit_ms - self.margin_ms) / 1000 - for start, stop in enumerate(range(self.burst_limit + 1, len(self.testtimes))): - part = self.testtimes[start:stop] - if (part[-1] - part[0]) >= delta: - passes.append(part) - else: - fails.append(part) - assert not fails diff --git a/tests/test_meta.py b/tests/test_meta.py index 35b1f80a2a1..ceb762bb44a 100644 --- a/tests/test_meta.py +++ b/tests/test_meta.py @@ -24,14 +24,14 @@ @pytest.mark.skipif( - not env_var_2_bool(os.getenv('TEST_BUILD', False)), reason='TEST_BUILD not enabled' + not env_var_2_bool(os.getenv("TEST_BUILD", False)), reason="TEST_BUILD not enabled" ) def test_build(): - assert os.system('python setup.py bdist_dumb') == 0 # pragma: no cover + assert os.system("python setup.py bdist_dumb") == 0 # pragma: no cover @pytest.mark.skipif( - not env_var_2_bool(os.getenv('TEST_BUILD', False)), reason='TEST_BUILD not enabled' + not env_var_2_bool(os.getenv("TEST_BUILD", False)), reason="TEST_BUILD not enabled" ) def test_build_raw(): - assert os.system('python setup-raw.py bdist_dumb') == 0 # pragma: no cover + assert os.system("python setup-raw.py bdist_dumb") == 0 # pragma: no cover diff --git a/tests/test_no_passport.py b/tests/test_no_passport.py index 8345f6ced61..d5761001fe2 100644 --- a/tests/test_no_passport.py +++ b/tests/test_no_passport.py @@ -41,17 +41,17 @@ from telegram.passport import credentials from tests.conftest import env_var_2_bool -TEST_NO_PASSPORT = env_var_2_bool(os.getenv('TEST_NO_PASSPORT', False)) +TEST_NO_PASSPORT = env_var_2_bool(os.getenv("TEST_NO_PASSPORT", False)) if TEST_NO_PASSPORT: orig_import = __import__ def import_mock(module_name, *args, **kwargs): - if module_name.startswith('cryptography'): - raise ModuleNotFoundError('We are testing without cryptography here') + if module_name.startswith("cryptography"): + raise ModuleNotFoundError("We are testing without cryptography here") return orig_import(module_name, *args, **kwargs) - with mock.patch('builtins.__import__', side_effect=import_mock): + with mock.patch("builtins.__import__", side_effect=import_mock): reload(bot) reload(credentials) @@ -64,19 +64,19 @@ class TestNoPassport: def test_bot_init(self, bot_info, monkeypatch): if not TEST_NO_PASSPORT: - monkeypatch.setattr(bot, 'CRYPTO_INSTALLED', False) - with pytest.raises(RuntimeError, match='passport'): - bot.Bot(bot_info['token'], private_key=1, private_key_password=2) + monkeypatch.setattr(bot, "CRYPTO_INSTALLED", False) + with pytest.raises(RuntimeError, match="passport"): + bot.Bot(bot_info["token"], private_key=1, private_key_password=2) def test_credentials_decrypt(self, monkeypatch): if not TEST_NO_PASSPORT: - monkeypatch.setattr(credentials, 'CRYPTO_INSTALLED', False) - with pytest.raises(RuntimeError, match='passport'): + monkeypatch.setattr(credentials, "CRYPTO_INSTALLED", False) + with pytest.raises(RuntimeError, match="passport"): credentials.decrypt(1, 1, 1) def test_encrypted_credentials_decrypted_secret(self, monkeypatch): if not TEST_NO_PASSPORT: - monkeypatch.setattr(credentials, 'CRYPTO_INSTALLED', False) - ec = credentials.EncryptedCredentials('data', 'hash', 'secret') - with pytest.raises(RuntimeError, match='passport'): + monkeypatch.setattr(credentials, "CRYPTO_INSTALLED", False) + ec = credentials.EncryptedCredentials("data", "hash", "secret") + with pytest.raises(RuntimeError, match="passport"): ec.decrypted_secret diff --git a/tests/test_official.py b/tests/test_official.py index f522ee266e6..4b17c0cb12c 100644 --- a/tests/test_official.py +++ b/tests/test_official.py @@ -18,6 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. import os import inspect +from typing import List import certifi import pytest @@ -27,16 +28,24 @@ import telegram from tests.conftest import env_var_2_bool -IGNORED_OBJECTS = ('ResponseParameters', 'CallbackGame') +IGNORED_OBJECTS = ("ResponseParameters", "CallbackGame") IGNORED_PARAMETERS = { - 'self', - 'args', - '_kwargs', - 'read_latency', - 'network_delay', - 'timeout', - 'bot', - 'api_kwargs', + "self", + "args", + "_kwargs", + "read_latency", + "network_delay", + "timeout", + "bot", + "api_kwargs", + "kwargs", +} + +ignored_param_requirements = { # Ignore these since there's convenience params in them (eg. Venue) + "send_location": {"latitude", "longitude"}, + "edit_message_live_location": {"latitude", "longitude"}, + "send_venue": {"latitude", "longitude", "title", "address"}, + "send_contact": {"phone_number", "first_name"}, } @@ -48,19 +57,20 @@ def find_next_sibling_until(tag, name, until): return sibling -def parse_table(h4): - table = find_next_sibling_until(h4, 'table', h4.find_next_sibling('h4')) +def parse_table(h4) -> List[List[str]]: + """Parses the Telegram doc table and has an output of a 2D list.""" + table = find_next_sibling_until(h4, "table", h4.find_next_sibling("h4")) if not table: return [] t = [] - for tr in table.find_all('tr')[1:]: - t.append([td.text for td in tr.find_all('td')]) + for tr in table.find_all("tr")[1:]: + t.append([td.text for td in tr.find_all("td")]) return t def check_method(h4): - name = h4.text - method = getattr(telegram.Bot, name) + name = h4.text # name of the method in telegram's docs. + method = getattr(telegram.Bot, name) # Retrieve our lib method table = parse_table(h4) # Check arguments based on source @@ -70,36 +80,37 @@ def check_method(h4): for parameter in table: param = sig.parameters.get(parameter[0]) assert param is not None, f"Parameter {parameter[0]} not found in {method.__name__}" + # TODO: Check type via docstring - # TODO: Check if optional or required + assert check_required_param( + parameter, param.name, sig, method.__name__ + ), f"Param {param.name!r} of method {method.__name__!r} requirement mismatch!" checked.append(parameter[0]) ignored = IGNORED_PARAMETERS.copy() - if name == 'getUpdates': - ignored -= {'timeout'} # Has it's own timeout parameter that we do wanna check for + if name == "getUpdates": + ignored -= {"timeout"} # Has it's own timeout parameter that we do wanna check for elif name in ( - f'send{media_type}' + f"send{media_type}" for media_type in [ - 'Animation', - 'Audio', - 'Document', - 'Photo', - 'Video', - 'VideoNote', - 'Voice', + "Animation", + "Audio", + "Document", + "Photo", + "Video", + "VideoNote", + "Voice", ] ): - ignored |= {'filename'} # Convenience parameter - elif name == 'setGameScore': - ignored |= {'edit_message'} # TODO: Now deprecated, so no longer in telegrams docs - elif name == 'sendContact': - ignored |= {'contact'} # Added for ease of use - elif name in ['sendLocation', 'editMessageLiveLocation']: - ignored |= {'location'} # Added for ease of use - elif name == 'sendVenue': - ignored |= {'venue'} # Added for ease of use - elif name == 'answerInlineQuery': - ignored |= {'current_offset'} # Added for ease of use + ignored |= {"filename"} # Convenience parameter + elif name == "sendContact": + ignored |= {"contact"} # Added for ease of use + elif name in ["sendLocation", "editMessageLiveLocation"]: + ignored |= {"location"} # Added for ease of use + elif name == "sendVenue": + ignored |= {"venue"} # Added for ease of use + elif name == "answerInlineQuery": + ignored |= {"current_offset"} # Added for ease of use assert (sig.parameters.keys() ^ checked) - ignored == set() @@ -109,87 +120,86 @@ def check_object(h4): obj = getattr(telegram, name) table = parse_table(h4) - # Check arguments based on source - sig = inspect.signature(obj, follow_wrapped=True) + # Check arguments based on source. Makes sure to only check __init__'s signature & nothing else + sig = inspect.signature(obj.__init__, follow_wrapped=True) - checked = [] + checked = set() for parameter in table: field = parameter[0] - if field == 'from': - field = 'from_user' + if field == "from": + field = "from_user" elif ( - name.startswith('InlineQueryResult') - or name.startswith('InputMedia') - or name.startswith('BotCommandScope') - ) and field == 'type': + name.startswith("InlineQueryResult") + or name.startswith("InputMedia") + or name.startswith("BotCommandScope") + ) and field == "type": continue - elif (name.startswith('ChatMember')) and field == 'status': + elif (name.startswith("ChatMember")) and field == "status": # We autofill the status continue elif ( - name.startswith('PassportElementError') and field == 'source' - ) or field == 'remove_keyboard': + name.startswith("PassportElementError") and field == "source" + ) or field == "remove_keyboard": + continue + elif name.startswith("ForceReply") and field == "force_reply": # this param is always True continue param = sig.parameters.get(field) assert param is not None, f"Attribute {field} not found in {obj.__name__}" # TODO: Check type via docstring - # TODO: Check if optional or required - checked.append(field) + assert check_required_param( + parameter, field, sig, obj.__name__ + ), f"{obj.__name__!r} parameter {param.name!r} requirement mismatch" + checked.add(field) ignored = IGNORED_PARAMETERS.copy() - if name == 'InputFile': + if name == "InputFile": return - if name == 'InlineQueryResult': - ignored |= {'id', 'type'} # attributes common to all subclasses - if name == 'ChatMember': - ignored |= {'user', 'status'} # attributes common to all subclasses - if name == 'ChatMember': - ignored |= { - 'can_add_web_page_previews', # for backwards compatibility - 'can_be_edited', - 'can_change_info', - 'can_delete_messages', - 'can_edit_messages', - 'can_invite_users', - 'can_manage_chat', - 'can_manage_voice_chats', - 'can_pin_messages', - 'can_post_messages', - 'can_promote_members', - 'can_restrict_members', - 'can_send_media_messages', - 'can_send_messages', - 'can_send_other_messages', - 'can_send_polls', - 'custom_title', - 'is_anonymous', - 'is_member', - 'until_date', - } - if name == 'BotCommandScope': - ignored |= {'type'} # attributes common to all subclasses - elif name == 'User': - ignored |= {'type'} # TODO: Deprecation - elif name in ('PassportFile', 'EncryptedPassportElement'): - ignored |= {'credentials'} - elif name == 'PassportElementError': - ignored |= {'message', 'type', 'source'} - elif name.startswith('InputMedia'): - ignored |= {'filename'} # Convenience parameter + if name == "InlineQueryResult": + ignored |= {"id", "type"} # attributes common to all subclasses + if name == "ChatMember": + ignored |= {"user", "status"} # attributes common to all subclasses + if name == "BotCommandScope": + ignored |= {"type"} # attributes common to all subclasses + elif name in ("PassportFile", "EncryptedPassportElement"): + ignored |= {"credentials"} + elif name == "PassportElementError": + ignored |= {"message", "type", "source"} + elif name.startswith("InputMedia"): + ignored |= {"filename"} # Convenience parameter assert (sig.parameters.keys() ^ checked) - ignored == set() +def check_required_param( + param_desc: List[str], param_name: str, sig: inspect.Signature, method_or_obj_name: str +) -> bool: + """Checks if the method/class parameter is a required/optional param as per Telegram docs.""" + if len(param_desc) == 4: # this means that there is a dedicated 'Required' column present. + # Handle cases where we provide convenience intentionally- + if param_name in ignored_param_requirements.get(method_or_obj_name, {}): + return True + is_required = True if param_desc[2] in {"Required", "Yes"} else False + is_ours_required = sig.parameters[param_name].default is inspect.Signature.empty + return is_required is is_ours_required + + if len(param_desc) == 3: # The docs mention the requirement in the description for classes... + if param_name in ignored_param_requirements.get(method_or_obj_name, {}): + return True + is_required = False if param_desc[2].split(".", 1)[0] == "Optional" else True + is_ours_required = sig.parameters[param_name].default is inspect.Signature.empty + return is_required is is_ours_required + + argvalues = [] names = [] -http = urllib3.PoolManager(cert_reqs='CERT_REQUIRED', ca_certs=certifi.where()) -request = http.request('GET', 'https://core.telegram.org/bots/api') -soup = BeautifulSoup(request.data.decode('utf-8'), 'html.parser') +http = urllib3.PoolManager(cert_reqs="CERT_REQUIRED", ca_certs=certifi.where()) +request = http.request("GET", "https://core.telegram.org/bots/api") +soup = BeautifulSoup(request.data.decode("utf-8"), "html.parser") -for thing in soup.select('h4 > a.anchor'): +for thing in soup.select("h4 > a.anchor"): # Methods and types don't have spaces in them, luckily all other sections of the docs do # TODO: don't depend on that - if '-' not in thing['name']: + if "-" not in thing["name"]: h4 = thing.parent # Is it a method @@ -201,9 +211,9 @@ def check_object(h4): names.append(h4.text) -@pytest.mark.parametrize(('method', 'data'), argvalues=argvalues, ids=names) +@pytest.mark.parametrize(("method", "data"), argvalues=argvalues, ids=names) @pytest.mark.skipif( - not env_var_2_bool(os.getenv('TEST_OFFICIAL')), reason='test_official is not enabled' + not env_var_2_bool(os.getenv("TEST_OFFICIAL")), reason="test_official is not enabled" ) def test_official(method, data): method(data) diff --git a/tests/test_orderinfo.py b/tests/test_orderinfo.py index 90faeafaecb..7c5bdf84ba0 100644 --- a/tests/test_orderinfo.py +++ b/tests/test_orderinfo.py @@ -21,7 +21,7 @@ from telegram import ShippingAddress, OrderInfo -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def order_info(): return OrderInfo( TestOrderInfo.name, @@ -32,25 +32,22 @@ def order_info(): class TestOrderInfo: - name = 'name' - phone_number = 'phone_number' - email = 'email' - shipping_address = ShippingAddress('GB', '', 'London', '12 Grimmauld Place', '', 'WC1') + name = "name" + phone_number = "phone_number" + email = "email" + shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1") - def test_slot_behaviour(self, order_info, mro_slots, recwarn): + def test_slot_behaviour(self, order_info, mro_slots): for attr in order_info.__slots__: - assert getattr(order_info, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not order_info.__dict__, f"got missing slot(s): {order_info.__dict__}" + assert getattr(order_info, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(order_info)) == len(set(mro_slots(order_info))), "duplicate slot" - order_info.custom, order_info.name = 'should give warning', self.name - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'name': TestOrderInfo.name, - 'phone_number': TestOrderInfo.phone_number, - 'email': TestOrderInfo.email, - 'shipping_address': TestOrderInfo.shipping_address.to_dict(), + "name": TestOrderInfo.name, + "phone_number": TestOrderInfo.phone_number, + "email": TestOrderInfo.email, + "shipping_address": TestOrderInfo.shipping_address.to_dict(), } order_info = OrderInfo.de_json(json_dict, bot) @@ -63,37 +60,37 @@ def test_to_dict(self, order_info): order_info_dict = order_info.to_dict() assert isinstance(order_info_dict, dict) - assert order_info_dict['name'] == order_info.name - assert order_info_dict['phone_number'] == order_info.phone_number - assert order_info_dict['email'] == order_info.email - assert order_info_dict['shipping_address'] == order_info.shipping_address.to_dict() + assert order_info_dict["name"] == order_info.name + assert order_info_dict["phone_number"] == order_info.phone_number + assert order_info_dict["email"] == order_info.email + assert order_info_dict["shipping_address"] == order_info.shipping_address.to_dict() def test_equality(self): a = OrderInfo( - 'name', - 'number', - 'mail', - ShippingAddress('GB', '', 'London', '12 Grimmauld Place', '', 'WC1'), + "name", + "number", + "mail", + ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1"), ) b = OrderInfo( - 'name', - 'number', - 'mail', - ShippingAddress('GB', '', 'London', '12 Grimmauld Place', '', 'WC1'), + "name", + "number", + "mail", + ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1"), ) c = OrderInfo( - 'name', - 'number', - 'mail', - ShippingAddress('GB', '', 'London', '13 Grimmauld Place', '', 'WC1'), + "name", + "number", + "mail", + ShippingAddress("GB", "", "London", "13 Grimmauld Place", "", "WC1"), ) d = OrderInfo( - 'name', - 'number', - 'e-mail', - ShippingAddress('GB', '', 'London', '12 Grimmauld Place', '', 'WC1'), + "name", + "number", + "e-mail", + ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1"), ) - e = ShippingAddress('GB', '', 'London', '12 Grimmauld Place', '', 'WC1') + e = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_parsemode.py b/tests/test_parsemode.py index 3c7644877bb..b57095f019d 100644 --- a/tests/test_parsemode.py +++ b/tests/test_parsemode.py @@ -22,21 +22,18 @@ class TestParseMode: - markdown_text = '*bold* _italic_ [link](http://google.com) [name](tg://user?id=123456789).' + markdown_text = "*bold* _italic_ [link](http://google.com) [name](tg://user?id=123456789)." html_text = ( 'bold italic link ' 'name.' ) - formatted_text_formatted = 'bold italic link name.' + formatted_text_formatted = "bold italic link name." - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): inst = ParseMode() for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @flaky(3, 1) def test_send_message_with_parse_mode_markdown(self, bot, chat_id): diff --git a/tests/test_passport.py b/tests/test_passport.py index 38687f9651b..55b6f5efffd 100644 --- a/tests/test_passport.py +++ b/tests/test_passport.py @@ -28,7 +28,7 @@ PassportElementErrorSelfie, PassportElementErrorDataField, Credentials, - TelegramDecryptionError, + PassportDecryptionError, ) @@ -38,191 +38,214 @@ RAW_PASSPORT_DATA = { - 'credentials': { - 'hash': 'qB4hz2LMcXYhglwz6EvXMMyI3PURisWLXl/iCmCXcSk=', - 'secret': 'O6x3X2JrLO1lUIhw48os1gaenDuZLhesoZMKXehZwtM3vsxOdtxHKWQyLNwtbyy4snYpARXDwf8f1QHNmQ/M1PwBQvk1ozrZBXb4a6k/iYj+P4v8Xw2M++CRHqZv0LaxFtyHOXnNYZ6dXpNeF0ZvYYTmm0FsYvK+/3/F6VDB3Oe6xWlXFLwaCCP/jA9i2+dKp6iq8NLOo4VnxenPKWWYz20RZ50MdAbS3UR+NCx4AHM2P5DEGrHNW0tMXJ+FG3jpVrit5BuCbB/eRgKxNGRWNxEGV5hun5MChdxKCEHCimnUA97h7MZdoTgYxkrfvDSZ/V89BnFXLdr87t/NLvVE0Q==', - 'data': 'MjHCHQT277BgJMxq5PfkPUl9p9h/5GbWtR0lcEi9MvtmQ9ONW8DZ3OmddaaVDdEHwh6Lfcr/0mxyMKhttm9QyACA1+oGBdw/KHRzLKS4a0P+rMyCcgctO6Q/+P9o6xs66hPFJAsN+sOUU4d431zaQN/RuHYuGM2s14A1K4YNRvNlp5/0JiS7RrV6SH6LC/97CvgGUnBOhLISmJXiMqwyVgg+wfS5SnOy2hQ5Zt/XdzxFyuehE3W4mHyY5W09I+MB/IafM4HcEvaqfFkWPmXNTkgBk9C2EJU9Lqc0PLmrXZn4LKeHVjuY7iloes/JecYKPMWmNjXwZQg/duIXvWL5icUaNrfjEcT5oljwZsrAc6NyyZwIp4w/+cb98jFwFAJ5uF81lRkZbeC3iw84mdpSVVYEzJSWSkSRs6JydfRCOYki0BNX9RnjgGqPYT+hNtUpEix2vHvJTIyvceflLF5vu+ol/axusirRiBVgNjKMfhs+x5bwBj5nDEE1XtEVrKtRq8/Ss96p0Tlds8eKulCDtPv/YujHVIErEhgUxDCGhr7OShokAFs/RwLmj6IBYQwnVbo0zIsq5qmCn/+1ogxJK+e934cDcwJAs8pnpgp7JPeFN9wBdmXSTpkO3KZt5Lgl3V86Rv5qv8oExQoJIUH5pKoXM+H2GB3QdfHLc/KpCeedG8RjateuIXKL2EtVe3JDMGBeI56eP9bTlW8+G1zVcpUuw/YEV14q4yiPlIRuWzrxXMvC1EtSzfGeY899trZBMCI00aeSpJyanf1f7B7nlQu6UbtMyN/9/GXbnjQjdP15CCQnmUK3PEWGtGV4XmK4iXIjBJELDD3T86RJyX/JAhJbT6funMt05w0bTyKFUDXdOcMyw2upj+wCsWTVMRNkw9yM63xL5TEfOc24aNi4pc4/LARSvwaNI/iBStqZEpG3KkYBQ2KutA022jRWzQ+xHIIz3mgA8z4PmXhcAU2RrTDGjGZUfbcX9LysZ/HvCHo/EB5njRISn3Yr1Ewu1pLX+Z4mERs+PCBXbpqBrZjY6dSWJ1QhggVJTPpWHya4CTGhkpyeFIc+D35t4kgt3U5ib61IaO9ABq0fUnB6dsvTGiN/i7KM8Ie1RUvPFBoVbz9x5YU9IT/ai8ln+1kfFfhiy8Ku4MnczthOUIjdr8nGUo4r3y0iEd5JEmqEcEsNx+/ZVMb7NEhpqXG8GPUxmwFTaHekldENxqTylv6qIxodhch6SLs/+iMat86DeCk1/+0u2fGmqZpxEEd9B89iD0+Av3UZC/C1rHn5FhC+o89RQAFWnH245rOHSbrTXyAtVBu2s1R0eIGadtAZYOI8xjULkbp52XyznZKCKaMKmr3UYah4P4VnUmhddBy+Mp/Bvxh8N3Ma8VSltO1n+3lyQWeUwdlCjt/3q3UpjAmilIKwEfeXMVhyjRlae1YGi/k+vgn+9LbFogh3Pl+N/kuyNqsTqPlzei2RXgpkX2qqHdF8WfkwQJpjXRurQN5LYaBfalygrUT+fCCpyaNkByxdDljKIPq6EibqtFA5jprAVDWGTTtFCKsPDJKFf9vc2lFy+7zyJxe8kMP1Wru8GrzF5z+pbfNp1tB80NqOrqJUbRnPB2I9Fb47ab76L8RBu2MROUNGcKJ62imQtfPH2I0f8zpbqqTZQwr3AmZ+PS9df2hHp2dYR9gFpMyR9u+bJ7HbpiKbYhh7mEFYeB/pQHsQRqM2OU5Bxk8XzxrwsdnzYO6tVcn8xr3Q4P9kZNXA6X5H0vJPpzClWoCPEr3ZGGWGl5DOhfsAmmst47vdAA1Cbl5k3pUW7/T3LWnMNwRnP8OdDOnsm06/v1nxIDjH08YlzLj4GTeXphSnsXSRNKFmz+M7vsOZPhWB8Y/WQmpJpOIj6IRstLxJk0h47TfYC7/RHBr4y7HQ8MLHODoPz/FM+nZtm2MMpB+u0qFNBvZG+Tjvlia7ZhX0n0OtivLWhnqygx3jZX7Ffwt5Es03wDP39ru4IccVZ9Jly/YUriHZURS6oDGycH3+DKUn5gRAxgOyjAwxGRqJh/YKfPt14d4iur0H3VUuLwFCbwj5hSvHVIv5cNapitgINU+0qzIlhyeE0HfRKstO7nOQ9A+nclqbOikYgurYIe0z70WZyJ3qSiHbOMMqQqcoKOJ6M9v2hDdJo9MDQ13dF6bl4+BfX4mcF0m7nVUBkzCRiSOQWWFUMgLX7CxSdmotT+eawKLjrCpSPmq9sicWyrFtVlq/NYLDGhT0jUUau6Mb5ksT+/OBVeMzqoezUcly29L1/gaeWAc8zOApVEjAMT48U63NXK5o8GrANeqqAt3TB36S5yeIjMf194nXAAzsJZ+s/tXprLn2M5mA1Iag4RbVPTarEsMp10JYag==', + "credentials": { + "hash": "qB4hz2LMcXYhglwz6EvXMMyI3PURisWLXl/iCmCXcSk=", + "secret": "O6x3X2JrLO1lUIhw48os1gaenDuZLhesoZMKXehZwtM3vsxOdtxHKWQyLNwtbyy4snYpARXDwf8f1QHNmQ/M1PwBQvk1ozrZBXb4a6k/iYj+P4v8Xw2M++CRHqZv0LaxFtyHOXnNYZ6dXpNeF0ZvYYTmm0FsYvK+/3/F6VDB3Oe6xWlXFLwaCCP/jA9i2+dKp6iq8NLOo4VnxenPKWWYz20RZ50MdAbS3UR+NCx4AHM2P5DEGrHNW0tMXJ+FG3jpVrit5BuCbB/eRgKxNGRWNxEGV5hun5MChdxKCEHCimnUA97h7MZdoTgYxkrfvDSZ/V89BnFXLdr87t/NLvVE0Q==", + "data": "MjHCHQT277BgJMxq5PfkPUl9p9h/5GbWtR0lcEi9MvtmQ9ONW8DZ3OmddaaVDdEHwh6Lfcr/0mxyMKhttm9QyACA1+oGBdw/KHRzLKS4a0P+rMyCcgctO6Q/+P9o6xs66hPFJAsN+sOUU4d431zaQN/RuHYuGM2s14A1K4YNRvNlp5/0JiS7RrV6SH6LC/97CvgGUnBOhLISmJXiMqwyVgg+wfS5SnOy2hQ5Zt/XdzxFyuehE3W4mHyY5W09I+MB/IafM4HcEvaqfFkWPmXNTkgBk9C2EJU9Lqc0PLmrXZn4LKeHVjuY7iloes/JecYKPMWmNjXwZQg/duIXvWL5icUaNrfjEcT5oljwZsrAc6NyyZwIp4w/+cb98jFwFAJ5uF81lRkZbeC3iw84mdpSVVYEzJSWSkSRs6JydfRCOYki0BNX9RnjgGqPYT+hNtUpEix2vHvJTIyvceflLF5vu+ol/axusirRiBVgNjKMfhs+x5bwBj5nDEE1XtEVrKtRq8/Ss96p0Tlds8eKulCDtPv/YujHVIErEhgUxDCGhr7OShokAFs/RwLmj6IBYQwnVbo0zIsq5qmCn/+1ogxJK+e934cDcwJAs8pnpgp7JPeFN9wBdmXSTpkO3KZt5Lgl3V86Rv5qv8oExQoJIUH5pKoXM+H2GB3QdfHLc/KpCeedG8RjateuIXKL2EtVe3JDMGBeI56eP9bTlW8+G1zVcpUuw/YEV14q4yiPlIRuWzrxXMvC1EtSzfGeY899trZBMCI00aeSpJyanf1f7B7nlQu6UbtMyN/9/GXbnjQjdP15CCQnmUK3PEWGtGV4XmK4iXIjBJELDD3T86RJyX/JAhJbT6funMt05w0bTyKFUDXdOcMyw2upj+wCsWTVMRNkw9yM63xL5TEfOc24aNi4pc4/LARSvwaNI/iBStqZEpG3KkYBQ2KutA022jRWzQ+xHIIz3mgA8z4PmXhcAU2RrTDGjGZUfbcX9LysZ/HvCHo/EB5njRISn3Yr1Ewu1pLX+Z4mERs+PCBXbpqBrZjY6dSWJ1QhggVJTPpWHya4CTGhkpyeFIc+D35t4kgt3U5ib61IaO9ABq0fUnB6dsvTGiN/i7KM8Ie1RUvPFBoVbz9x5YU9IT/ai8ln+1kfFfhiy8Ku4MnczthOUIjdr8nGUo4r3y0iEd5JEmqEcEsNx+/ZVMb7NEhpqXG8GPUxmwFTaHekldENxqTylv6qIxodhch6SLs/+iMat86DeCk1/+0u2fGmqZpxEEd9B89iD0+Av3UZC/C1rHn5FhC+o89RQAFWnH245rOHSbrTXyAtVBu2s1R0eIGadtAZYOI8xjULkbp52XyznZKCKaMKmr3UYah4P4VnUmhddBy+Mp/Bvxh8N3Ma8VSltO1n+3lyQWeUwdlCjt/3q3UpjAmilIKwEfeXMVhyjRlae1YGi/k+vgn+9LbFogh3Pl+N/kuyNqsTqPlzei2RXgpkX2qqHdF8WfkwQJpjXRurQN5LYaBfalygrUT+fCCpyaNkByxdDljKIPq6EibqtFA5jprAVDWGTTtFCKsPDJKFf9vc2lFy+7zyJxe8kMP1Wru8GrzF5z+pbfNp1tB80NqOrqJUbRnPB2I9Fb47ab76L8RBu2MROUNGcKJ62imQtfPH2I0f8zpbqqTZQwr3AmZ+PS9df2hHp2dYR9gFpMyR9u+bJ7HbpiKbYhh7mEFYeB/pQHsQRqM2OU5Bxk8XzxrwsdnzYO6tVcn8xr3Q4P9kZNXA6X5H0vJPpzClWoCPEr3ZGGWGl5DOhfsAmmst47vdAA1Cbl5k3pUW7/T3LWnMNwRnP8OdDOnsm06/v1nxIDjH08YlzLj4GTeXphSnsXSRNKFmz+M7vsOZPhWB8Y/WQmpJpOIj6IRstLxJk0h47TfYC7/RHBr4y7HQ8MLHODoPz/FM+nZtm2MMpB+u0qFNBvZG+Tjvlia7ZhX0n0OtivLWhnqygx3jZX7Ffwt5Es03wDP39ru4IccVZ9Jly/YUriHZURS6oDGycH3+DKUn5gRAxgOyjAwxGRqJh/YKfPt14d4iur0H3VUuLwFCbwj5hSvHVIv5cNapitgINU+0qzIlhyeE0HfRKstO7nOQ9A+nclqbOikYgurYIe0z70WZyJ3qSiHbOMMqQqcoKOJ6M9v2hDdJo9MDQ13dF6bl4+BfX4mcF0m7nVUBkzCRiSOQWWFUMgLX7CxSdmotT+eawKLjrCpSPmq9sicWyrFtVlq/NYLDGhT0jUUau6Mb5ksT+/OBVeMzqoezUcly29L1/gaeWAc8zOApVEjAMT48U63NXK5o8GrANeqqAt3TB36S5yeIjMf194nXAAzsJZ+s/tXprLn2M5mA1Iag4RbVPTarEsMp10JYag==", }, - 'data': [ + "data": [ { - 'data': 'QRfzWcCN4WncvRO3lASG+d+c5gzqXtoCinQ1PgtYiZMKXCksx9eB9Ic1bOt8C/un9/XaX220PjJSO7Kuba+nXXC51qTsjqP9rnLKygnEIWjKrfiDdklzgcukpRzFSjiOAvhy86xFJZ1PfPSrFATy/Gp1RydLzbrBd2ZWxZqXrxcMoA0Q2UTTFXDoCYerEAiZoD69i79tB/6nkLBcUUvN5d52gKd/GowvxWqAAmdO6l1N7jlo6aWjdYQNBAK1KHbJdbRZMJLxC1MqMuZXAYrPoYBRKr5xAnxDTmPn/LEZKLc3gwwZyEgR5x7e9jp5heM6IEMmsv3O/6SUeEQs7P0iVuRSPLMJLfDdwns8Tl3fF2M4IxKVovjCaOVW+yHKsADDAYQPzzH2RcrWVD0TP5I64mzpK64BbTOq3qm3Hn51SV9uA/+LvdGbCp7VnzHx4EdUizHsVyilJULOBwvklsrDRvXMiWmh34ZSR6zilh051tMEcRf0I+Oe7pIxVJd/KKfYA2Z/eWVQTCn5gMuAInQNXFSqDIeIqBX+wca6kvOCUOXB7J2uRjTpLaC4DM9s/sNjSBvFixcGAngt+9oap6Y45rQc8ZJaNN/ALqEJAmkphW8=', - 'type': 'personal_details', + "data": "QRfzWcCN4WncvRO3lASG+d+c5gzqXtoCinQ1PgtYiZMKXCksx9eB9Ic1bOt8C/un9/XaX220PjJSO7Kuba+nXXC51qTsjqP9rnLKygnEIWjKrfiDdklzgcukpRzFSjiOAvhy86xFJZ1PfPSrFATy/Gp1RydLzbrBd2ZWxZqXrxcMoA0Q2UTTFXDoCYerEAiZoD69i79tB/6nkLBcUUvN5d52gKd/GowvxWqAAmdO6l1N7jlo6aWjdYQNBAK1KHbJdbRZMJLxC1MqMuZXAYrPoYBRKr5xAnxDTmPn/LEZKLc3gwwZyEgR5x7e9jp5heM6IEMmsv3O/6SUeEQs7P0iVuRSPLMJLfDdwns8Tl3fF2M4IxKVovjCaOVW+yHKsADDAYQPzzH2RcrWVD0TP5I64mzpK64BbTOq3qm3Hn51SV9uA/+LvdGbCp7VnzHx4EdUizHsVyilJULOBwvklsrDRvXMiWmh34ZSR6zilh051tMEcRf0I+Oe7pIxVJd/KKfYA2Z/eWVQTCn5gMuAInQNXFSqDIeIqBX+wca6kvOCUOXB7J2uRjTpLaC4DM9s/sNjSBvFixcGAngt+9oap6Y45rQc8ZJaNN/ALqEJAmkphW8=", + "type": "personal_details", + "hash": "What to put here?", }, { - 'reverse_side': { - 'file_date': 1534074942, - 'file_id': 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI', - 'file_unique_id': 'adc3145fd2e84d95b64d68eaa22aa33e', + "reverse_side": { + "file_size": 32424112, + "file_date": 1534074942, + "file_id": "DgADBAADNQQAAtoagFPf4wwmFZdmyQI", + "file_unique_id": "adc3145fd2e84d95b64d68eaa22aa33e", }, - 'translation': [ + "translation": [ { - 'file_size': 28640, - 'file_date': 1535630933, - 'file_id': 'DgADBAADswMAAisqQVAmooP-kVgLgAI', - 'file_unique_id': '52a90d53d6064bb58feb582acdc3a324', + "file_size": 28640, + "file_date": 1535630933, + "file_id": "DgADBAADswMAAisqQVAmooP-kVgLgAI", + "file_unique_id": "52a90d53d6064bb58feb582acdc3a324", }, { - 'file_size': 28672, - 'file_date': 1535630933, - 'file_id': 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI', - 'file_unique_id': '7285f864d168441ba1f7d02146250432', + "file_size": 28672, + "file_date": 1535630933, + "file_id": "DgADBAAD1QMAAnrpQFBMZsT3HysjwwI", + "file_unique_id": "7285f864d168441ba1f7d02146250432", }, ], - 'front_side': { - 'file_size': 28624, - 'file_date': 1534074942, - 'file_id': 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI', - 'file_unique_id': 'd9d52a700cbb4a189a80104aa5978133', + "front_side": { + "file_size": 28624, + "file_date": 1534074942, + "file_id": "DgADBAADxwMAApnQgVPK2-ckL2eXVAI", + "file_unique_id": "d9d52a700cbb4a189a80104aa5978133", }, - 'type': 'driver_license', - 'selfie': { - 'file_size': 28592, - 'file_date': 1534074942, - 'file_id': 'DgADBAADEQQAAkopgFNr6oi-wISRtAI', - 'file_unique_id': 'd4e390cca57b4da5a65322b304762a12', + "type": "driver_license", + "selfie": { + "file_size": 28592, + "file_date": 1534074942, + "file_id": "DgADBAADEQQAAkopgFNr6oi-wISRtAI", + "file_unique_id": "d4e390cca57b4da5a65322b304762a12", }, - 'data': 'eJUOFuY53QKmGqmBgVWlLBAQCUQJ79n405SX6M5aGFIIodOPQqnLYvMNqTwTrXGDlW+mVLZcbu+y8luLVO8WsJB/0SB7q5WaXn/IMt1G9lz5G/KMLIZG/x9zlnimsaQLg7u8srG6L4KZzv+xkbbHjZdETrxU8j0N/DoS4HvLMRSJAgeFUrY6v2YW9vSRg+fSxIqQy1jR2VKpzAT8OhOz7A==', + "data": "eJUOFuY53QKmGqmBgVWlLBAQCUQJ79n405SX6M5aGFIIodOPQqnLYvMNqTwTrXGDlW+mVLZcbu+y8luLVO8WsJB/0SB7q5WaXn/IMt1G9lz5G/KMLIZG/x9zlnimsaQLg7u8srG6L4KZzv+xkbbHjZdETrxU8j0N/DoS4HvLMRSJAgeFUrY6v2YW9vSRg+fSxIqQy1jR2VKpzAT8OhOz7A==", + "hash": "We seriously need to improve this mess! took so long to debug!", }, { - 'translation': [ + "translation": [ { - 'file_size': 28480, - 'file_date': 1535630939, - 'file_id': 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI', - 'file_unique_id': '38b2877b443542cbaf520c6e36a33ac4', + "file_size": 28480, + "file_date": 1535630939, + "file_id": "DgADBAADyQUAAqyqQVC_eoX_KwNjJwI", + "file_unique_id": "38b2877b443542cbaf520c6e36a33ac4", }, { - 'file_size': 28528, - 'file_date': 1535630939, - 'file_id': 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI', - 'file_unique_id': 'f008ca48c44b4a47895ddbcd2f76741e', + "file_size": 28528, + "file_date": 1535630939, + "file_id": "DgADBAADsQQAAubTQVDRO_FN3lOwWwI", + "file_unique_id": "f008ca48c44b4a47895ddbcd2f76741e", }, ], - 'files': [ + "files": [ { - 'file_size': 28640, - 'file_date': 1534074988, - 'file_id': 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI', - 'file_unique_id': 'b170748794834644baaa3ec57ee4ce7a', + "file_size": 28640, + "file_date": 1534074988, + "file_id": "DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI", + "file_unique_id": "b170748794834644baaa3ec57ee4ce7a", }, { - 'file_size': 28480, - 'file_date': 1534074988, - 'file_id': 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI', - 'file_unique_id': '19a12ae34dca424b85e0308f706cee75', + "file_size": 28480, + "file_date": 1534074988, + "file_id": "DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI", + "file_unique_id": "19a12ae34dca424b85e0308f706cee75", }, ], - 'type': 'utility_bill', + "type": "utility_bill", + "hash": "Wow over 30 minutes spent debugging passport stuff.", }, { - 'data': 'j9SksVkSj128DBtZA+3aNjSFNirzv+R97guZaMgae4Gi0oDVNAF7twPR7j9VSmPedfJrEwL3O889Ei+a5F1xyLLyEI/qEBljvL70GFIhYGitS0JmNabHPHSZrjOl8b4s/0Z0Px2GpLO5siusTLQonimdUvu4UPjKquYISmlKEKhtmGATy+h+JDjNCYuOkhakeNw0Rk0BHgj0C3fCb7WZNQSyVb+2GTu6caR6eXf/AFwFp0TV3sRz3h0WIVPW8bna', - 'type': 'address', + "data": "j9SksVkSj128DBtZA+3aNjSFNirzv+R97guZaMgae4Gi0oDVNAF7twPR7j9VSmPedfJrEwL3O889Ei+a5F1xyLLyEI/qEBljvL70GFIhYGitS0JmNabHPHSZrjOl8b4s/0Z0Px2GpLO5siusTLQonimdUvu4UPjKquYISmlKEKhtmGATy+h+JDjNCYuOkhakeNw0Rk0BHgj0C3fCb7WZNQSyVb+2GTu6caR6eXf/AFwFp0TV3sRz3h0WIVPW8bna", + "type": "address", + "hash": "at least I get the pattern now", }, - {'email': 'fb3e3i47zt@dispostable.com', 'type': 'email'}, + {"email": "fb3e3i47zt@dispostable.com", "type": "email", "hash": "this should be it."}, ], } -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def all_passport_data(): return [ - {'type': 'personal_details', 'data': RAW_PASSPORT_DATA['data'][0]['data']}, { - 'type': 'passport', - 'data': RAW_PASSPORT_DATA['data'][1]['data'], - 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], - 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + "type": "personal_details", + "data": RAW_PASSPORT_DATA["data"][0]["data"], + "hash": "what to put here?", }, { - 'type': 'internal_passport', - 'data': RAW_PASSPORT_DATA['data'][1]['data'], - 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], - 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + "type": "passport", + "data": RAW_PASSPORT_DATA["data"][1]["data"], + "front_side": RAW_PASSPORT_DATA["data"][1]["front_side"], + "selfie": RAW_PASSPORT_DATA["data"][1]["selfie"], + "translation": RAW_PASSPORT_DATA["data"][1]["translation"], + "hash": "more data arghh", }, { - 'type': 'driver_license', - 'data': RAW_PASSPORT_DATA['data'][1]['data'], - 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], - 'reverse_side': RAW_PASSPORT_DATA['data'][1]['reverse_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], - 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + "type": "internal_passport", + "data": RAW_PASSPORT_DATA["data"][1]["data"], + "front_side": RAW_PASSPORT_DATA["data"][1]["front_side"], + "selfie": RAW_PASSPORT_DATA["data"][1]["selfie"], + "translation": RAW_PASSPORT_DATA["data"][1]["translation"], + "hash": "more data arghh", }, { - 'type': 'identity_card', - 'data': RAW_PASSPORT_DATA['data'][1]['data'], - 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], - 'reverse_side': RAW_PASSPORT_DATA['data'][1]['reverse_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], - 'translation': RAW_PASSPORT_DATA['data'][1]['translation'], + "type": "driver_license", + "data": RAW_PASSPORT_DATA["data"][1]["data"], + "front_side": RAW_PASSPORT_DATA["data"][1]["front_side"], + "reverse_side": RAW_PASSPORT_DATA["data"][1]["reverse_side"], + "selfie": RAW_PASSPORT_DATA["data"][1]["selfie"], + "translation": RAW_PASSPORT_DATA["data"][1]["translation"], + "hash": "more data arghh", }, { - 'type': 'utility_bill', - 'files': RAW_PASSPORT_DATA['data'][2]['files'], - 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + "type": "identity_card", + "data": RAW_PASSPORT_DATA["data"][1]["data"], + "front_side": RAW_PASSPORT_DATA["data"][1]["front_side"], + "reverse_side": RAW_PASSPORT_DATA["data"][1]["reverse_side"], + "selfie": RAW_PASSPORT_DATA["data"][1]["selfie"], + "translation": RAW_PASSPORT_DATA["data"][1]["translation"], + "hash": "more data arghh", }, { - 'type': 'bank_statement', - 'files': RAW_PASSPORT_DATA['data'][2]['files'], - 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + "type": "utility_bill", + "files": RAW_PASSPORT_DATA["data"][2]["files"], + "translation": RAW_PASSPORT_DATA["data"][2]["translation"], + "hash": "more data arghh", }, { - 'type': 'rental_agreement', - 'files': RAW_PASSPORT_DATA['data'][2]['files'], - 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + "type": "bank_statement", + "files": RAW_PASSPORT_DATA["data"][2]["files"], + "translation": RAW_PASSPORT_DATA["data"][2]["translation"], + "hash": "more data arghh", }, { - 'type': 'passport_registration', - 'files': RAW_PASSPORT_DATA['data'][2]['files'], - 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + "type": "rental_agreement", + "files": RAW_PASSPORT_DATA["data"][2]["files"], + "translation": RAW_PASSPORT_DATA["data"][2]["translation"], + "hash": "more data arghh", }, { - 'type': 'temporary_registration', - 'files': RAW_PASSPORT_DATA['data'][2]['files'], - 'translation': RAW_PASSPORT_DATA['data'][2]['translation'], + "type": "passport_registration", + "files": RAW_PASSPORT_DATA["data"][2]["files"], + "translation": RAW_PASSPORT_DATA["data"][2]["translation"], + "hash": "more data arghh", + }, + { + "type": "temporary_registration", + "files": RAW_PASSPORT_DATA["data"][2]["files"], + "translation": RAW_PASSPORT_DATA["data"][2]["translation"], + "hash": "more data arghh", + }, + { + "type": "address", + "data": RAW_PASSPORT_DATA["data"][3]["data"], + "hash": "more data arghh", + }, + {"type": "email", "email": "fb3e3i47zt@dispostable.com", "hash": "more data arghh"}, + { + "type": "phone_number", + "phone_number": "fb3e3i47zt@dispostable.com", + "hash": "more data arghh", }, - {'type': 'address', 'data': RAW_PASSPORT_DATA['data'][3]['data']}, - {'type': 'email', 'email': 'fb3e3i47zt@dispostable.com'}, - {'type': 'phone_number', 'phone_number': 'fb3e3i47zt@dispostable.com'}, ] -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def passport_data(bot): return PassportData.de_json(RAW_PASSPORT_DATA, bot=bot) class TestPassport: - driver_license_selfie_file_id = 'DgADBAADEQQAAkopgFNr6oi-wISRtAI' - driver_license_selfie_file_unique_id = 'd4e390cca57b4da5a65322b304762a12' - driver_license_front_side_file_id = 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI' - driver_license_front_side_file_unique_id = 'd9d52a700cbb4a189a80104aa5978133' - driver_license_reverse_side_file_id = 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI' - driver_license_reverse_side_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' - driver_license_translation_1_file_id = 'DgADBAADswMAAisqQVAmooP-kVgLgAI' - driver_license_translation_1_file_unique_id = '52a90d53d6064bb58feb582acdc3a324' - driver_license_translation_2_file_id = 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI' - driver_license_translation_2_file_unique_id = '7285f864d168441ba1f7d02146250432' - utility_bill_1_file_id = 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI' - utility_bill_1_file_unique_id = 'b170748794834644baaa3ec57ee4ce7a' - utility_bill_2_file_id = 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI' - utility_bill_2_file_unique_id = '19a12ae34dca424b85e0308f706cee75' - utility_bill_translation_1_file_id = 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI' - utility_bill_translation_1_file_unique_id = '38b2877b443542cbaf520c6e36a33ac4' - utility_bill_translation_2_file_id = 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI' - utility_bill_translation_2_file_unique_id = 'f008ca48c44b4a47895ddbcd2f76741e' - driver_license_selfie_credentials_file_hash = 'Cila/qLXSBH7DpZFbb5bRZIRxeFW2uv/ulL0u0JNsYI=' - driver_license_selfie_credentials_secret = 'tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E=' - - def test_slot_behaviour(self, passport_data, mro_slots, recwarn): + driver_license_selfie_file_id = "DgADBAADEQQAAkopgFNr6oi-wISRtAI" + driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12" + driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI" + driver_license_front_side_file_unique_id = "d9d52a700cbb4a189a80104aa5978133" + driver_license_reverse_side_file_id = "DgADBAADNQQAAtoagFPf4wwmFZdmyQI" + driver_license_reverse_side_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" + driver_license_translation_1_file_id = "DgADBAADswMAAisqQVAmooP-kVgLgAI" + driver_license_translation_1_file_unique_id = "52a90d53d6064bb58feb582acdc3a324" + driver_license_translation_2_file_id = "DgADBAAD1QMAAnrpQFBMZsT3HysjwwI" + driver_license_translation_2_file_unique_id = "7285f864d168441ba1f7d02146250432" + utility_bill_1_file_id = "DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI" + utility_bill_1_file_unique_id = "b170748794834644baaa3ec57ee4ce7a" + utility_bill_2_file_id = "DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI" + utility_bill_2_file_unique_id = "19a12ae34dca424b85e0308f706cee75" + utility_bill_translation_1_file_id = "DgADBAADyQUAAqyqQVC_eoX_KwNjJwI" + utility_bill_translation_1_file_unique_id = "38b2877b443542cbaf520c6e36a33ac4" + utility_bill_translation_2_file_id = "DgADBAADsQQAAubTQVDRO_FN3lOwWwI" + utility_bill_translation_2_file_unique_id = "f008ca48c44b4a47895ddbcd2f76741e" + driver_license_selfie_credentials_file_hash = "Cila/qLXSBH7DpZFbb5bRZIRxeFW2uv/ulL0u0JNsYI=" + driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E=" + + def test_slot_behaviour(self, passport_data, mro_slots): inst = passport_data for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.data = 'should give warning', passport_data.data - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, passport_data): assert isinstance(passport_data, PassportData) @@ -230,11 +253,11 @@ def test_creation(self, passport_data): def test_expected_encrypted_values(self, passport_data): personal_details, driver_license, utility_bill, address, email = passport_data.data - assert personal_details.type == 'personal_details' - assert personal_details.data == RAW_PASSPORT_DATA['data'][0]['data'] + assert personal_details.type == "personal_details" + assert personal_details.data == RAW_PASSPORT_DATA["data"][0]["data"] - assert driver_license.type == 'driver_license' - assert driver_license.data == RAW_PASSPORT_DATA['data'][1]['data'] + assert driver_license.type == "driver_license" + assert driver_license.data == RAW_PASSPORT_DATA["data"][1]["data"] assert isinstance(driver_license.selfie, PassportFile) assert driver_license.selfie.file_id == self.driver_license_selfie_file_id assert driver_license.selfie.file_unique_id == self.driver_license_selfie_file_unique_id @@ -267,7 +290,7 @@ def test_expected_encrypted_values(self, passport_data): == self.driver_license_translation_2_file_unique_id ) - assert utility_bill.type == 'utility_bill' + assert utility_bill.type == "utility_bill" assert isinstance(utility_bill.files[0], PassportFile) assert utility_bill.files[0].file_id == self.utility_bill_1_file_id assert utility_bill.files[0].file_unique_id == self.utility_bill_1_file_unique_id @@ -290,11 +313,11 @@ def test_expected_encrypted_values(self, passport_data): == self.utility_bill_translation_2_file_unique_id ) - assert address.type == 'address' - assert address.data == RAW_PASSPORT_DATA['data'][3]['data'] + assert address.type == "address" + assert address.data == RAW_PASSPORT_DATA["data"][3]["data"] - assert email.type == 'email' - assert email.email == 'fb3e3i47zt@dispostable.com' + assert email.type == "email" + assert email.email == "fb3e3i47zt@dispostable.com" def test_expected_decrypted_values(self, passport_data): ( @@ -305,24 +328,24 @@ def test_expected_decrypted_values(self, passport_data): email, ) = passport_data.decrypted_data - assert personal_details.type == 'personal_details' + assert personal_details.type == "personal_details" assert personal_details.data.to_dict() == { - 'first_name': 'FIRSTNAME', - 'middle_name': 'MIDDLENAME', - 'first_name_native': 'FIRSTNAMENATIVE', - 'residence_country_code': 'DK', - 'birth_date': '01.01.2001', - 'last_name_native': 'LASTNAMENATIVE', - 'gender': 'female', - 'middle_name_native': 'MIDDLENAMENATIVE', - 'country_code': 'DK', - 'last_name': 'LASTNAME', + "first_name": "FIRSTNAME", + "middle_name": "MIDDLENAME", + "first_name_native": "FIRSTNAMENATIVE", + "residence_country_code": "DK", + "birth_date": "01.01.2001", + "last_name_native": "LASTNAMENATIVE", + "gender": "female", + "middle_name_native": "MIDDLENAMENATIVE", + "country_code": "DK", + "last_name": "LASTNAME", } - assert driver_license.type == 'driver_license' + assert driver_license.type == "driver_license" assert driver_license.data.to_dict() == { - 'expiry_date': '01.01.2001', - 'document_no': 'DOCUMENT_NO', + "expiry_date": "01.01.2001", + "document_no": "DOCUMENT_NO", } assert isinstance(driver_license.selfie, PassportFile) assert driver_license.selfie.file_id == self.driver_license_selfie_file_id @@ -342,17 +365,17 @@ def test_expected_decrypted_values(self, passport_data): == self.driver_license_reverse_side_file_unique_id ) - assert address.type == 'address' + assert address.type == "address" assert address.data.to_dict() == { - 'city': 'CITY', - 'street_line2': 'STREET_LINE2', - 'state': 'STATE', - 'post_code': 'POSTCODE', - 'country_code': 'DK', - 'street_line1': 'STREET_LINE1', + "city": "CITY", + "street_line2": "STREET_LINE2", + "state": "STATE", + "post_code": "POSTCODE", + "country_code": "DK", + "street_line1": "STREET_LINE1", } - assert utility_bill.type == 'utility_bill' + assert utility_bill.type == "utility_bill" assert isinstance(utility_bill.files[0], PassportFile) assert utility_bill.files[0].file_id == self.utility_bill_1_file_id assert utility_bill.files[0].file_unique_id == self.utility_bill_1_file_unique_id @@ -361,33 +384,33 @@ def test_expected_decrypted_values(self, passport_data): assert utility_bill.files[1].file_id == self.utility_bill_2_file_id assert utility_bill.files[1].file_unique_id == self.utility_bill_2_file_unique_id - assert email.type == 'email' - assert email.email == 'fb3e3i47zt@dispostable.com' + assert email.type == "email" + assert email.email == "fb3e3i47zt@dispostable.com" def test_all_types(self, passport_data, bot, all_passport_data): credentials = passport_data.decrypted_credentials.to_dict() # Copy credentials from other types to all types so we can decrypt everything - sd = credentials['secure_data'] - credentials['secure_data'] = { - 'personal_details': sd['personal_details'].copy(), - 'passport': sd['driver_license'].copy(), - 'internal_passport': sd['driver_license'].copy(), - 'driver_license': sd['driver_license'].copy(), - 'identity_card': sd['driver_license'].copy(), - 'address': sd['address'].copy(), - 'utility_bill': sd['utility_bill'].copy(), - 'bank_statement': sd['utility_bill'].copy(), - 'rental_agreement': sd['utility_bill'].copy(), - 'passport_registration': sd['utility_bill'].copy(), - 'temporary_registration': sd['utility_bill'].copy(), + sd = credentials["secure_data"] + credentials["secure_data"] = { + "personal_details": sd["personal_details"].copy(), + "passport": sd["driver_license"].copy(), + "internal_passport": sd["driver_license"].copy(), + "driver_license": sd["driver_license"].copy(), + "identity_card": sd["driver_license"].copy(), + "address": sd["address"].copy(), + "utility_bill": sd["utility_bill"].copy(), + "bank_statement": sd["utility_bill"].copy(), + "rental_agreement": sd["utility_bill"].copy(), + "passport_registration": sd["utility_bill"].copy(), + "temporary_registration": sd["utility_bill"].copy(), } new = PassportData.de_json( { - 'data': all_passport_data, + "data": all_passport_data, # Replaced below - 'credentials': {'data': 'data', 'hash': 'hash', 'secret': 'secret'}, + "credentials": {"data": "data", "hash": "hash", "secret": "secret"}, }, bot=bot, ) @@ -399,10 +422,10 @@ def test_all_types(self, passport_data, bot, all_passport_data): def test_bot_init_invalid_key(self, bot): with pytest.raises(TypeError): - Bot(bot.token, private_key='Invalid key!') + Bot(bot.token, private_key="Invalid key!") with pytest.raises(ValueError): - Bot(bot.token, private_key=b'Invalid key!') + Bot(bot.token, private_key=b"Invalid key!") def test_passport_data_okay_with_non_crypto_bot(self, bot): b = Bot(bot.token) @@ -410,22 +433,22 @@ def test_passport_data_okay_with_non_crypto_bot(self, bot): def test_wrong_hash(self, bot): data = deepcopy(RAW_PASSPORT_DATA) - data['credentials']['hash'] = 'bm90Y29ycmVjdGhhc2g=' # Not correct hash + data["credentials"]["hash"] = "bm90Y29ycmVjdGhhc2g=" # Not correct hash passport_data = PassportData.de_json(data, bot=bot) - with pytest.raises(TelegramDecryptionError): + with pytest.raises(PassportDecryptionError): assert passport_data.decrypted_data def test_wrong_key(self, bot): short_key = b"-----BEGIN RSA PRIVATE KEY-----\r\nMIIBOQIBAAJBAKU+OZ2jJm7sCA/ec4gngNZhXYPu+DZ/TAwSMl0W7vAPXAsLplBk\r\nO8l6IBHx8N0ZC4Bc65mO3b2G8YAzqndyqH8CAwEAAQJAWOx3jQFzeVXDsOaBPdAk\r\nYTncXVeIc6tlfUl9mOLyinSbRNCy1XicOiOZFgH1rRKOGIC1235QmqxFvdecySoY\r\nwQIhAOFeGgeX9CrEPuSsd9+kqUcA2avCwqdQgSdy2qggRFyJAiEAu7QHT8JQSkHU\r\nDELfzrzc24AhjyG0z1DpGZArM8COascCIDK42SboXj3Z2UXiQ0CEcMzYNiVgOisq\r\nBUd5pBi+2mPxAiAM5Z7G/Sv1HjbKrOGh29o0/sXPhtpckEuj5QMC6E0gywIgFY6S\r\nNjwrAA+cMmsgY0O2fAzEKkDc5YiFsiXaGaSS4eA=\r\n-----END RSA PRIVATE KEY-----" b = Bot(bot.token, private_key=short_key) passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot=b) - with pytest.raises(TelegramDecryptionError): + with pytest.raises(PassportDecryptionError): assert passport_data.decrypted_data wrong_key = b"-----BEGIN RSA PRIVATE KEY-----\r\nMIIEogIBAAKCAQB4qCFltuvHakZze86TUweU7E/SB3VLGEHAe7GJlBmrou9SSWsL\r\nH7E++157X6UqWFl54LOE9MeHZnoW7rZ+DxLKhk6NwAHTxXPnvw4CZlvUPC3OFxg3\r\nhEmNen6ojSM4sl4kYUIa7F+Q5uMEYaboxoBen9mbj4zzMGsG4aY/xBOb2ewrXQyL\r\nRh//tk1Px4ago+lUPisAvQVecz7/6KU4Xj4Lpv2z20f3cHlZX6bb7HlE1vixCMOf\r\nxvfC5SkWEGZMR/ZoWQUsoDkrDSITF/S3GtLfg083TgtCKaOF3mCT27sJ1og77npP\r\n0cH/qdlbdoFtdrRj3PvBpaj/TtXRhmdGcJBxAgMBAAECggEAYSq1Sp6XHo8dkV8B\r\nK2/QSURNu8y5zvIH8aUrgqo8Shb7OH9bryekrB3vJtgNwR5JYHdu2wHttcL3S4SO\r\nftJQxbyHgmxAjHUVNGqOM6yPA0o7cR70J7FnMoKVgdO3q68pVY7ll50IET9/T0X9\r\nDrTdKFb+/eILFsXFS1NpeSzExdsKq3zM0sP/vlJHHYVTmZDGaGEvny/eLAS+KAfG\r\nrKP96DeO4C/peXEJzALZ/mG1ReBB05Qp9Dx1xEC20yreRk5MnnBA5oiHVG5ZLOl9\r\nEEHINidqN+TMNSkxv67xMfQ6utNu5IpbklKv/4wqQOJOO50HZ+qBtSurTN573dky\r\nzslbCQKBgQDHDUBYyKN/v69VLmvNVcxTgrOcrdbqAfefJXb9C3dVXhS8/oRkCRU/\r\ndzxYWNT7hmQyWUKor/izh68rZ/M+bsTnlaa7IdAgyChzTfcZL/2pxG9pq05GF1Q4\r\nBSJ896ZEe3jEhbpJXRlWYvz7455svlxR0H8FooCTddTmkU3nsQSx0wKBgQCbLSa4\r\nyZs2QVstQQerNjxAtLi0IvV8cJkuvFoNC2Q21oqQc7BYU7NJL7uwriprZr5nwkCQ\r\nOFQXi4N3uqimNxuSng31ETfjFZPp+pjb8jf7Sce7cqU66xxR+anUzVZqBG1CJShx\r\nVxN7cWN33UZvIH34gA2Ax6AXNnJG42B5Gn1GKwKBgQCZ/oh/p4nGNXfiAK3qB6yy\r\nFvX6CwuvsqHt/8AUeKBz7PtCU+38roI/vXF0MBVmGky+HwxREQLpcdl1TVCERpIT\r\nUFXThI9OLUwOGI1IcTZf9tby+1LtKvM++8n4wGdjp9qAv6ylQV9u09pAzZItMwCd\r\nUx5SL6wlaQ2y60tIKk0lfQKBgBJS+56YmA6JGzY11qz+I5FUhfcnpauDNGOTdGLT\r\n9IqRPR2fu7RCdgpva4+KkZHLOTLReoRNUojRPb4WubGfEk93AJju5pWXR7c6k3Bt\r\novS2mrJk8GQLvXVksQxjDxBH44sLDkKMEM3j7uYJqDaZNKbyoCWT7TCwikAau5qx\r\naRevAoGAAKZV705dvrpJuyoHFZ66luANlrAwG/vNf6Q4mBEXB7guqMkokCsSkjqR\r\nhsD79E6q06zA0QzkLCavbCn5kMmDS/AbA80+B7El92iIN6d3jRdiNZiewkhlWhEG\r\nm4N0gQRfIu+rUjsS/4xk8UuQUT/Ossjn/hExi7ejpKdCc7N++bc=\r\n-----END RSA PRIVATE KEY-----" b = Bot(bot.token, private_key=wrong_key) passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot=b) - with pytest.raises(TelegramDecryptionError): + with pytest.raises(PassportDecryptionError): assert passport_data.decrypted_data def test_mocked_download_passport_file(self, passport_data, monkeypatch): @@ -437,9 +460,9 @@ def test_mocked_download_passport_file(self, passport_data, monkeypatch): # NOTE: file_unique_id is not used in the get_file method, so it is passed directly def get_file(*_, **kwargs): - return File(kwargs['file_id'], selfie.file_unique_id) + return File(kwargs["file_id"], selfie.file_unique_id) - monkeypatch.setattr(passport_data.bot, 'get_file', get_file) + monkeypatch.setattr(passport_data.bot, "get_file", get_file) file = selfie.get_file() assert file.file_id == selfie.file_id assert file.file_unique_id == selfie.file_unique_id @@ -449,33 +472,33 @@ def get_file(*_, **kwargs): def test_mocked_set_passport_data_errors(self, monkeypatch, bot, chat_id, passport_data): def test(url, data, **kwargs): return ( - data['user_id'] == chat_id - and data['errors'][0]['file_hash'] + data["user_id"] == chat_id + and data["errors"][0]["file_hash"] == ( passport_data.decrypted_credentials.secure_data.driver_license.selfie.file_hash ) - and data['errors'][1]['data_hash'] + and data["errors"][1]["data_hash"] == (passport_data.decrypted_credentials.secure_data.driver_license.data.data_hash) ) - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.set_passport_data_errors( chat_id, [ PassportElementErrorSelfie( - 'driver_license', + "driver_license", ( passport_data.decrypted_credentials.secure_data.driver_license.selfie.file_hash ), - 'You\'re not handsome enough to use this app!', + "You're not handsome enough to use this app!", ), PassportElementErrorDataField( - 'driver_license', - 'expiry_date', + "driver_license", + "expiry_date", ( passport_data.decrypted_credentials.secure_data.driver_license.data.data_hash ), - 'Your driver license is expired!', + "Your driver license is expired!", ), ], ) @@ -496,7 +519,7 @@ def test_equality(self, passport_data): assert hash(a) == hash(b) assert a is not b - passport_data.credentials.hash = 'NOTAPROPERHASH' + passport_data.credentials.hash = "NOTAPROPERHASH" c = PassportData(passport_data.data, passport_data.credentials) assert a != c diff --git a/tests/test_passportelementerrordatafield.py b/tests/test_passportelementerrordatafield.py index 2073df2ab45..f8bb7ef2533 100644 --- a/tests/test_passportelementerrordatafield.py +++ b/tests/test_passportelementerrordatafield.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorDataField, PassportElementErrorSelfie -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_data_field(): return PassportElementErrorDataField( TestPassportElementErrorDataField.type_, @@ -32,20 +32,17 @@ def passport_element_error_data_field(): class TestPassportElementErrorDataField: - source = 'data' - type_ = 'test_type' - field_name = 'test_field' - data_hash = 'data_hash' - message = 'Error message' + source = "data" + type_ = "test_type" + field_name = "test_field" + data_hash = "data_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_data_field, recwarn, mro_slots): + def test_slot_behaviour(self, passport_element_error_data_field, mro_slots): inst = passport_element_error_data_field for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_data_field): assert passport_element_error_data_field.source == self.source @@ -59,23 +56,23 @@ def test_to_dict(self, passport_element_error_data_field): assert isinstance(passport_element_error_data_field_dict, dict) assert ( - passport_element_error_data_field_dict['source'] + passport_element_error_data_field_dict["source"] == passport_element_error_data_field.source ) assert ( - passport_element_error_data_field_dict['type'] + passport_element_error_data_field_dict["type"] == passport_element_error_data_field.type ) assert ( - passport_element_error_data_field_dict['field_name'] + passport_element_error_data_field_dict["field_name"] == passport_element_error_data_field.field_name ) assert ( - passport_element_error_data_field_dict['data_hash'] + passport_element_error_data_field_dict["data_hash"] == passport_element_error_data_field.data_hash ) assert ( - passport_element_error_data_field_dict['message'] + passport_element_error_data_field_dict["message"] == passport_element_error_data_field.message ) @@ -86,11 +83,11 @@ def test_equality(self): b = PassportElementErrorDataField( self.type_, self.field_name, self.data_hash, self.message ) - c = PassportElementErrorDataField(self.type_, '', '', '') - d = PassportElementErrorDataField('', self.field_name, '', '') - e = PassportElementErrorDataField('', '', self.data_hash, '') - f = PassportElementErrorDataField('', '', '', self.message) - g = PassportElementErrorSelfie(self.type_, '', self.message) + c = PassportElementErrorDataField(self.type_, "", "", "") + d = PassportElementErrorDataField("", self.field_name, "", "") + e = PassportElementErrorDataField("", "", self.data_hash, "") + f = PassportElementErrorDataField("", "", "", self.message) + g = PassportElementErrorSelfie(self.type_, "", self.message) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_passportelementerrorfile.py b/tests/test_passportelementerrorfile.py index f7dd0c5d85b..fd754510606 100644 --- a/tests/test_passportelementerrorfile.py +++ b/tests/test_passportelementerrorfile.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorFile, PassportElementErrorSelfie -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_file(): return PassportElementErrorFile( TestPassportElementErrorFile.type_, @@ -31,19 +31,16 @@ def passport_element_error_file(): class TestPassportElementErrorFile: - source = 'file' - type_ = 'test_type' - file_hash = 'file_hash' - message = 'Error message' + source = "file" + type_ = "test_type" + file_hash = "file_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_file, recwarn, mro_slots): + def test_slot_behaviour(self, passport_element_error_file, mro_slots): inst = passport_element_error_file for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_file): assert passport_element_error_file.source == self.source @@ -55,19 +52,19 @@ def test_to_dict(self, passport_element_error_file): passport_element_error_file_dict = passport_element_error_file.to_dict() assert isinstance(passport_element_error_file_dict, dict) - assert passport_element_error_file_dict['source'] == passport_element_error_file.source - assert passport_element_error_file_dict['type'] == passport_element_error_file.type + assert passport_element_error_file_dict["source"] == passport_element_error_file.source + assert passport_element_error_file_dict["type"] == passport_element_error_file.type assert ( - passport_element_error_file_dict['file_hash'] == passport_element_error_file.file_hash + passport_element_error_file_dict["file_hash"] == passport_element_error_file.file_hash ) - assert passport_element_error_file_dict['message'] == passport_element_error_file.message + assert passport_element_error_file_dict["message"] == passport_element_error_file.message def test_equality(self): a = PassportElementErrorFile(self.type_, self.file_hash, self.message) b = PassportElementErrorFile(self.type_, self.file_hash, self.message) - c = PassportElementErrorFile(self.type_, '', '') - d = PassportElementErrorFile('', self.file_hash, '') - e = PassportElementErrorFile('', '', self.message) + c = PassportElementErrorFile(self.type_, "", "") + d = PassportElementErrorFile("", self.file_hash, "") + e = PassportElementErrorFile("", "", self.message) f = PassportElementErrorSelfie(self.type_, self.file_hash, self.message) assert a == b diff --git a/tests/test_passportelementerrorfiles.py b/tests/test_passportelementerrorfiles.py index 5dcab832d63..a8ca370bde2 100644 --- a/tests/test_passportelementerrorfiles.py +++ b/tests/test_passportelementerrorfiles.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorFiles, PassportElementErrorSelfie -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_files(): return PassportElementErrorFiles( TestPassportElementErrorFiles.type_, @@ -31,19 +31,16 @@ def passport_element_error_files(): class TestPassportElementErrorFiles: - source = 'files' - type_ = 'test_type' - file_hashes = ['hash1', 'hash2'] - message = 'Error message' + source = "files" + type_ = "test_type" + file_hashes = ["hash1", "hash2"] + message = "Error message" - def test_slot_behaviour(self, passport_element_error_files, mro_slots, recwarn): + def test_slot_behaviour(self, passport_element_error_files, mro_slots): inst = passport_element_error_files for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_files): assert passport_element_error_files.source == self.source @@ -56,21 +53,21 @@ def test_to_dict(self, passport_element_error_files): passport_element_error_files_dict = passport_element_error_files.to_dict() assert isinstance(passport_element_error_files_dict, dict) - assert passport_element_error_files_dict['source'] == passport_element_error_files.source - assert passport_element_error_files_dict['type'] == passport_element_error_files.type + assert passport_element_error_files_dict["source"] == passport_element_error_files.source + assert passport_element_error_files_dict["type"] == passport_element_error_files.type assert ( - passport_element_error_files_dict['file_hashes'] + passport_element_error_files_dict["file_hashes"] == passport_element_error_files.file_hashes ) - assert passport_element_error_files_dict['message'] == passport_element_error_files.message + assert passport_element_error_files_dict["message"] == passport_element_error_files.message def test_equality(self): a = PassportElementErrorFiles(self.type_, self.file_hashes, self.message) b = PassportElementErrorFiles(self.type_, self.file_hashes, self.message) - c = PassportElementErrorFiles(self.type_, '', '') - d = PassportElementErrorFiles('', self.file_hashes, '') - e = PassportElementErrorFiles('', '', self.message) - f = PassportElementErrorSelfie(self.type_, '', self.message) + c = PassportElementErrorFiles(self.type_, "", "") + d = PassportElementErrorFiles("", self.file_hashes, "") + e = PassportElementErrorFiles("", "", self.message) + f = PassportElementErrorSelfie(self.type_, "", self.message) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_passportelementerrorfrontside.py b/tests/test_passportelementerrorfrontside.py index fed480e0b17..be10b64840f 100644 --- a/tests/test_passportelementerrorfrontside.py +++ b/tests/test_passportelementerrorfrontside.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorFrontSide, PassportElementErrorSelfie -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_front_side(): return PassportElementErrorFrontSide( TestPassportElementErrorFrontSide.type_, @@ -31,19 +31,16 @@ def passport_element_error_front_side(): class TestPassportElementErrorFrontSide: - source = 'front_side' - type_ = 'test_type' - file_hash = 'file_hash' - message = 'Error message' + source = "front_side" + type_ = "test_type" + file_hash = "file_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_front_side, recwarn, mro_slots): + def test_slot_behaviour(self, passport_element_error_front_side, mro_slots): inst = passport_element_error_front_side for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_front_side): assert passport_element_error_front_side.source == self.source @@ -56,28 +53,28 @@ def test_to_dict(self, passport_element_error_front_side): assert isinstance(passport_element_error_front_side_dict, dict) assert ( - passport_element_error_front_side_dict['source'] + passport_element_error_front_side_dict["source"] == passport_element_error_front_side.source ) assert ( - passport_element_error_front_side_dict['type'] + passport_element_error_front_side_dict["type"] == passport_element_error_front_side.type ) assert ( - passport_element_error_front_side_dict['file_hash'] + passport_element_error_front_side_dict["file_hash"] == passport_element_error_front_side.file_hash ) assert ( - passport_element_error_front_side_dict['message'] + passport_element_error_front_side_dict["message"] == passport_element_error_front_side.message ) def test_equality(self): a = PassportElementErrorFrontSide(self.type_, self.file_hash, self.message) b = PassportElementErrorFrontSide(self.type_, self.file_hash, self.message) - c = PassportElementErrorFrontSide(self.type_, '', '') - d = PassportElementErrorFrontSide('', self.file_hash, '') - e = PassportElementErrorFrontSide('', '', self.message) + c = PassportElementErrorFrontSide(self.type_, "", "") + d = PassportElementErrorFrontSide("", self.file_hash, "") + e = PassportElementErrorFrontSide("", "", self.message) f = PassportElementErrorSelfie(self.type_, self.file_hash, self.message) assert a == b diff --git a/tests/test_passportelementerrorreverseside.py b/tests/test_passportelementerrorreverseside.py index a4172e76d69..058aca90f64 100644 --- a/tests/test_passportelementerrorreverseside.py +++ b/tests/test_passportelementerrorreverseside.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorReverseSide, PassportElementErrorSelfie -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_reverse_side(): return PassportElementErrorReverseSide( TestPassportElementErrorReverseSide.type_, @@ -31,19 +31,16 @@ def passport_element_error_reverse_side(): class TestPassportElementErrorReverseSide: - source = 'reverse_side' - type_ = 'test_type' - file_hash = 'file_hash' - message = 'Error message' + source = "reverse_side" + type_ = "test_type" + file_hash = "file_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_reverse_side, mro_slots, recwarn): + def test_slot_behaviour(self, passport_element_error_reverse_side, mro_slots): inst = passport_element_error_reverse_side for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_reverse_side): assert passport_element_error_reverse_side.source == self.source @@ -56,28 +53,28 @@ def test_to_dict(self, passport_element_error_reverse_side): assert isinstance(passport_element_error_reverse_side_dict, dict) assert ( - passport_element_error_reverse_side_dict['source'] + passport_element_error_reverse_side_dict["source"] == passport_element_error_reverse_side.source ) assert ( - passport_element_error_reverse_side_dict['type'] + passport_element_error_reverse_side_dict["type"] == passport_element_error_reverse_side.type ) assert ( - passport_element_error_reverse_side_dict['file_hash'] + passport_element_error_reverse_side_dict["file_hash"] == passport_element_error_reverse_side.file_hash ) assert ( - passport_element_error_reverse_side_dict['message'] + passport_element_error_reverse_side_dict["message"] == passport_element_error_reverse_side.message ) def test_equality(self): a = PassportElementErrorReverseSide(self.type_, self.file_hash, self.message) b = PassportElementErrorReverseSide(self.type_, self.file_hash, self.message) - c = PassportElementErrorReverseSide(self.type_, '', '') - d = PassportElementErrorReverseSide('', self.file_hash, '') - e = PassportElementErrorReverseSide('', '', self.message) + c = PassportElementErrorReverseSide(self.type_, "", "") + d = PassportElementErrorReverseSide("", self.file_hash, "") + e = PassportElementErrorReverseSide("", "", self.message) f = PassportElementErrorSelfie(self.type_, self.file_hash, self.message) assert a == b diff --git a/tests/test_passportelementerrorselfie.py b/tests/test_passportelementerrorselfie.py index ea804012fcd..890e8859229 100644 --- a/tests/test_passportelementerrorselfie.py +++ b/tests/test_passportelementerrorselfie.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorSelfie, PassportElementErrorDataField -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_selfie(): return PassportElementErrorSelfie( TestPassportElementErrorSelfie.type_, @@ -31,19 +31,16 @@ def passport_element_error_selfie(): class TestPassportElementErrorSelfie: - source = 'selfie' - type_ = 'test_type' - file_hash = 'file_hash' - message = 'Error message' + source = "selfie" + type_ = "test_type" + file_hash = "file_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_selfie, recwarn, mro_slots): + def test_slot_behaviour(self, passport_element_error_selfie, mro_slots): inst = passport_element_error_selfie for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_selfie): assert passport_element_error_selfie.source == self.source @@ -55,23 +52,23 @@ def test_to_dict(self, passport_element_error_selfie): passport_element_error_selfie_dict = passport_element_error_selfie.to_dict() assert isinstance(passport_element_error_selfie_dict, dict) - assert passport_element_error_selfie_dict['source'] == passport_element_error_selfie.source - assert passport_element_error_selfie_dict['type'] == passport_element_error_selfie.type + assert passport_element_error_selfie_dict["source"] == passport_element_error_selfie.source + assert passport_element_error_selfie_dict["type"] == passport_element_error_selfie.type assert ( - passport_element_error_selfie_dict['file_hash'] + passport_element_error_selfie_dict["file_hash"] == passport_element_error_selfie.file_hash ) assert ( - passport_element_error_selfie_dict['message'] == passport_element_error_selfie.message + passport_element_error_selfie_dict["message"] == passport_element_error_selfie.message ) def test_equality(self): a = PassportElementErrorSelfie(self.type_, self.file_hash, self.message) b = PassportElementErrorSelfie(self.type_, self.file_hash, self.message) - c = PassportElementErrorSelfie(self.type_, '', '') - d = PassportElementErrorSelfie('', self.file_hash, '') - e = PassportElementErrorSelfie('', '', self.message) - f = PassportElementErrorDataField(self.type_, '', '', self.message) + c = PassportElementErrorSelfie(self.type_, "", "") + d = PassportElementErrorSelfie("", self.file_hash, "") + e = PassportElementErrorSelfie("", "", self.message) + f = PassportElementErrorDataField(self.type_, "", "", self.message) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_passportelementerrortranslationfile.py b/tests/test_passportelementerrortranslationfile.py index e30d0af768a..b5013cd585b 100644 --- a/tests/test_passportelementerrortranslationfile.py +++ b/tests/test_passportelementerrortranslationfile.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorTranslationFile, PassportElementErrorDataField -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_translation_file(): return PassportElementErrorTranslationFile( TestPassportElementErrorTranslationFile.type_, @@ -31,19 +31,16 @@ def passport_element_error_translation_file(): class TestPassportElementErrorTranslationFile: - source = 'translation_file' - type_ = 'test_type' - file_hash = 'file_hash' - message = 'Error message' + source = "translation_file" + type_ = "test_type" + file_hash = "file_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_translation_file, recwarn, mro_slots): + def test_slot_behaviour(self, passport_element_error_translation_file, mro_slots): inst = passport_element_error_translation_file for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_translation_file): assert passport_element_error_translation_file.source == self.source @@ -58,29 +55,29 @@ def test_to_dict(self, passport_element_error_translation_file): assert isinstance(passport_element_error_translation_file_dict, dict) assert ( - passport_element_error_translation_file_dict['source'] + passport_element_error_translation_file_dict["source"] == passport_element_error_translation_file.source ) assert ( - passport_element_error_translation_file_dict['type'] + passport_element_error_translation_file_dict["type"] == passport_element_error_translation_file.type ) assert ( - passport_element_error_translation_file_dict['file_hash'] + passport_element_error_translation_file_dict["file_hash"] == passport_element_error_translation_file.file_hash ) assert ( - passport_element_error_translation_file_dict['message'] + passport_element_error_translation_file_dict["message"] == passport_element_error_translation_file.message ) def test_equality(self): a = PassportElementErrorTranslationFile(self.type_, self.file_hash, self.message) b = PassportElementErrorTranslationFile(self.type_, self.file_hash, self.message) - c = PassportElementErrorTranslationFile(self.type_, '', '') - d = PassportElementErrorTranslationFile('', self.file_hash, '') - e = PassportElementErrorTranslationFile('', '', self.message) - f = PassportElementErrorDataField(self.type_, '', '', self.message) + c = PassportElementErrorTranslationFile(self.type_, "", "") + d = PassportElementErrorTranslationFile("", self.file_hash, "") + e = PassportElementErrorTranslationFile("", "", self.message) + f = PassportElementErrorDataField(self.type_, "", "", self.message) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_passportelementerrortranslationfiles.py b/tests/test_passportelementerrortranslationfiles.py index 5911d59e488..71194934e1f 100644 --- a/tests/test_passportelementerrortranslationfiles.py +++ b/tests/test_passportelementerrortranslationfiles.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorTranslationFiles, PassportElementErrorSelfie -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_translation_files(): return PassportElementErrorTranslationFiles( TestPassportElementErrorTranslationFiles.type_, @@ -31,19 +31,16 @@ def passport_element_error_translation_files(): class TestPassportElementErrorTranslationFiles: - source = 'translation_files' - type_ = 'test_type' - file_hashes = ['hash1', 'hash2'] - message = 'Error message' + source = "translation_files" + type_ = "test_type" + file_hashes = ["hash1", "hash2"] + message = "Error message" - def test_slot_behaviour(self, passport_element_error_translation_files, mro_slots, recwarn): + def test_slot_behaviour(self, passport_element_error_translation_files, mro_slots): inst = passport_element_error_translation_files for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_translation_files): assert passport_element_error_translation_files.source == self.source @@ -59,29 +56,29 @@ def test_to_dict(self, passport_element_error_translation_files): assert isinstance(passport_element_error_translation_files_dict, dict) assert ( - passport_element_error_translation_files_dict['source'] + passport_element_error_translation_files_dict["source"] == passport_element_error_translation_files.source ) assert ( - passport_element_error_translation_files_dict['type'] + passport_element_error_translation_files_dict["type"] == passport_element_error_translation_files.type ) assert ( - passport_element_error_translation_files_dict['file_hashes'] + passport_element_error_translation_files_dict["file_hashes"] == passport_element_error_translation_files.file_hashes ) assert ( - passport_element_error_translation_files_dict['message'] + passport_element_error_translation_files_dict["message"] == passport_element_error_translation_files.message ) def test_equality(self): a = PassportElementErrorTranslationFiles(self.type_, self.file_hashes, self.message) b = PassportElementErrorTranslationFiles(self.type_, self.file_hashes, self.message) - c = PassportElementErrorTranslationFiles(self.type_, '', '') - d = PassportElementErrorTranslationFiles('', self.file_hashes, '') - e = PassportElementErrorTranslationFiles('', '', self.message) - f = PassportElementErrorSelfie(self.type_, '', self.message) + c = PassportElementErrorTranslationFiles(self.type_, "", "") + d = PassportElementErrorTranslationFiles("", self.file_hashes, "") + e = PassportElementErrorTranslationFiles("", "", self.message) + f = PassportElementErrorSelfie(self.type_, "", self.message) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_passportelementerrorunspecified.py b/tests/test_passportelementerrorunspecified.py index 7a9d67d59fd..ecc8bbee012 100644 --- a/tests/test_passportelementerrorunspecified.py +++ b/tests/test_passportelementerrorunspecified.py @@ -21,7 +21,7 @@ from telegram import PassportElementErrorUnspecified, PassportElementErrorDataField -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_element_error_unspecified(): return PassportElementErrorUnspecified( TestPassportElementErrorUnspecified.type_, @@ -31,19 +31,16 @@ def passport_element_error_unspecified(): class TestPassportElementErrorUnspecified: - source = 'unspecified' - type_ = 'test_type' - element_hash = 'element_hash' - message = 'Error message' + source = "unspecified" + type_ = "test_type" + element_hash = "element_hash" + message = "Error message" - def test_slot_behaviour(self, passport_element_error_unspecified, recwarn, mro_slots): + def test_slot_behaviour(self, passport_element_error_unspecified, mro_slots): inst = passport_element_error_unspecified for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.type = 'should give warning', self.type_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_element_error_unspecified): assert passport_element_error_unspecified.source == self.source @@ -56,29 +53,29 @@ def test_to_dict(self, passport_element_error_unspecified): assert isinstance(passport_element_error_unspecified_dict, dict) assert ( - passport_element_error_unspecified_dict['source'] + passport_element_error_unspecified_dict["source"] == passport_element_error_unspecified.source ) assert ( - passport_element_error_unspecified_dict['type'] + passport_element_error_unspecified_dict["type"] == passport_element_error_unspecified.type ) assert ( - passport_element_error_unspecified_dict['element_hash'] + passport_element_error_unspecified_dict["element_hash"] == passport_element_error_unspecified.element_hash ) assert ( - passport_element_error_unspecified_dict['message'] + passport_element_error_unspecified_dict["message"] == passport_element_error_unspecified.message ) def test_equality(self): a = PassportElementErrorUnspecified(self.type_, self.element_hash, self.message) b = PassportElementErrorUnspecified(self.type_, self.element_hash, self.message) - c = PassportElementErrorUnspecified(self.type_, '', '') - d = PassportElementErrorUnspecified('', self.element_hash, '') - e = PassportElementErrorUnspecified('', '', self.message) - f = PassportElementErrorDataField(self.type_, '', '', self.message) + c = PassportElementErrorUnspecified(self.type_, "", "") + d = PassportElementErrorUnspecified("", self.element_hash, "") + e = PassportElementErrorUnspecified("", "", self.message) + f = PassportElementErrorDataField(self.type_, "", "", self.message) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_passportfile.py b/tests/test_passportfile.py index ef3b54f6b8a..f7b99853249 100644 --- a/tests/test_passportfile.py +++ b/tests/test_passportfile.py @@ -22,7 +22,7 @@ from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def passport_file(bot): return PassportFile( file_id=TestPassportFile.file_id, @@ -34,19 +34,16 @@ def passport_file(bot): class TestPassportFile: - file_id = 'data' - file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + file_id = "data" + file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" file_size = 50 file_date = 1532879128 - def test_slot_behaviour(self, passport_file, mro_slots, recwarn): + def test_slot_behaviour(self, passport_file, mro_slots): inst = passport_file for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.file_id = 'should give warning', self.file_id - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, passport_file): assert passport_file.file_id == self.file_id @@ -58,30 +55,30 @@ def test_to_dict(self, passport_file): passport_file_dict = passport_file.to_dict() assert isinstance(passport_file_dict, dict) - assert passport_file_dict['file_id'] == passport_file.file_id - assert passport_file_dict['file_unique_id'] == passport_file.file_unique_id - assert passport_file_dict['file_size'] == passport_file.file_size - assert passport_file_dict['file_date'] == passport_file.file_date + assert passport_file_dict["file_id"] == passport_file.file_id + assert passport_file_dict["file_unique_id"] == passport_file.file_unique_id + assert passport_file_dict["file_size"] == passport_file.file_size + assert passport_file_dict["file_date"] == passport_file.file_date def test_get_file_instance_method(self, monkeypatch, passport_file): def make_assertion(*_, **kwargs): - result = kwargs['file_id'] == passport_file.file_id + result = kwargs["file_id"] == passport_file.file_id # we need to be a bit hacky here, b/c PF.get_file needs Bot.get_file to return a File return File(file_id=result, file_unique_id=result) - assert check_shortcut_signature(PassportFile.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(passport_file.get_file, passport_file.bot, 'get_file') + assert check_shortcut_signature(PassportFile.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(passport_file.get_file, passport_file.bot, "get_file") assert check_defaults_handling(passport_file.get_file, passport_file.bot) - monkeypatch.setattr(passport_file.bot, 'get_file', make_assertion) - assert passport_file.get_file().file_id == 'True' + monkeypatch.setattr(passport_file.bot, "get_file", make_assertion) + assert passport_file.get_file().file_id == "True" def test_equality(self): a = PassportFile(self.file_id, self.file_unique_id, self.file_size, self.file_date) - b = PassportFile('', self.file_unique_id, self.file_size, self.file_date) - c = PassportFile(self.file_id, self.file_unique_id, '', '') - d = PassportFile('', '', self.file_size, self.file_date) - e = PassportElementError('source', 'type', 'message') + b = PassportFile("", self.file_unique_id, self.file_size, self.file_date) + c = PassportFile(self.file_id, self.file_unique_id, "", "") + d = PassportFile("", "", self.file_size, self.file_date) + e = PassportElementError("source", "type", "message") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 56e797219df..03805eb2a1a 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -21,6 +21,7 @@ import uuid from threading import Lock +from telegram.ext import PersistenceInput from telegram.ext.callbackdatacache import CallbackDataCache from telegram.utils.helpers import encode_conversations_to_json @@ -34,7 +35,6 @@ from collections import defaultdict from collections.abc import Container from time import sleep -from sys import version_info as py_ver import pytest @@ -98,12 +98,28 @@ def update_conversation(self, name, key, new_state): def update_user_data(self, user_id, data): raise NotImplementedError + def get_callback_data(self): + raise NotImplementedError + + def refresh_user_data(self, user_id, user_data): + raise NotImplementedError + + def refresh_chat_data(self, chat_id, chat_data): + raise NotImplementedError + + def refresh_bot_data(self, bot_data): + raise NotImplementedError + + def update_callback_data(self, data): + raise NotImplementedError + + def flush(self): + raise NotImplementedError + @pytest.fixture(scope="function") def base_persistence(): - return OwnPersistence( - store_chat_data=True, store_user_data=True, store_bot_data=True, store_callback_data=True - ) + return OwnPersistence() @pytest.fixture(scope="function") @@ -148,57 +164,63 @@ def update_callback_data(self, data): def update_conversation(self, name, key, new_state): raise NotImplementedError + def refresh_user_data(self, user_id, user_data): + pass + + def refresh_chat_data(self, chat_id, chat_data): + pass + + def refresh_bot_data(self, bot_data): + pass + + def flush(self): + pass + return BotPersistence() @pytest.fixture(scope="function") def bot_data(): - return {'test1': 'test2', 'test3': {'test4': 'test5'}} + return {"test1": "test2", "test3": {"test4": "test5"}} @pytest.fixture(scope="function") def chat_data(): return defaultdict( - dict, {-12345: {'test1': 'test2', 'test3': {'test4': 'test5'}}, -67890: {3: 'test4'}} + dict, {-12345: {"test1": "test2", "test3": {"test4": "test5"}}, -67890: {3: "test4"}} ) @pytest.fixture(scope="function") def user_data(): return defaultdict( - dict, {12345: {'test1': 'test2', 'test3': {'test4': 'test5'}}, 67890: {3: 'test4'}} + dict, {12345: {"test1": "test2", "test3": {"test4": "test5"}}, 67890: {3: "test4"}} ) @pytest.fixture(scope="function") def callback_data(): - return [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})], {'test1': 'test2'} + return [("test1", 1000, {"button1": "test0", "button2": "test1"})], {"test1": "test2"} -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def conversations(): return { - 'name1': {(123, 123): 3, (456, 654): 4}, - 'name2': {(123, 321): 1, (890, 890): 2}, - 'name3': {(123, 321): 1, (890, 890): 2}, + "name1": {(123, 123): 3, (456, 654): 4}, + "name2": {(123, 321): 1, (890, 890): 2}, + "name3": {(123, 321): 1, (890, 890): 2}, } @pytest.fixture(scope="function") def updater(bot, base_persistence): - base_persistence.store_chat_data = False - base_persistence.store_bot_data = False - base_persistence.store_user_data = False - base_persistence.store_callback_data = False + base_persistence.store_data = PersistenceInput(False, False, False, False) u = Updater(bot=bot, persistence=base_persistence) - base_persistence.store_bot_data = True - base_persistence.store_chat_data = True - base_persistence.store_user_data = True - base_persistence.store_callback_data = True + base_persistence.store_data = PersistenceInput() return u -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def job_queue(bot): jq = JobQueue() yield jq @@ -215,40 +237,40 @@ def assert_data_in_cache(callback_data_cache: CallbackDataCache, data): class TestBasePersistence: test_flag = False - @pytest.fixture(scope='function', autouse=True) + @pytest.fixture(scope="function", autouse=True) def reset(self): self.test_flag = False - def test_slot_behaviour(self, bot_persistence, mro_slots, recwarn): + def test_slot_behaviour(self, bot_persistence, mro_slots): inst = bot_persistence for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" # assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" # The below test fails if the child class doesn't define __slots__ (not a cause of concern) assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.store_user_data, inst.custom = {}, "custom persistence shouldn't warn" - assert len(recwarn) == 0, recwarn.list - assert '__dict__' not in BasePersistence.__slots__ if py_ver < (3, 7) else True, 'has dict' def test_creation(self, base_persistence): - assert base_persistence.store_chat_data - assert base_persistence.store_user_data - assert base_persistence.store_bot_data + assert base_persistence.store_data.chat_data + assert base_persistence.store_data.user_data + assert base_persistence.store_data.bot_data + assert base_persistence.store_data.callback_data def test_abstract_methods(self, base_persistence): with pytest.raises( TypeError, match=( - 'get_bot_data, get_chat_data, get_conversations, ' - 'get_user_data, update_bot_data, update_chat_data, ' - 'update_conversation, update_user_data' + "flush, get_bot_data, get_callback_data, " + "get_chat_data, get_conversations, " + "get_user_data, refresh_bot_data, refresh_chat_data, " + "refresh_user_data, update_bot_data, update_callback_data, " + "update_chat_data, update_conversation, update_user_data" ), ): BasePersistence() with pytest.raises(NotImplementedError): base_persistence.get_callback_data() with pytest.raises(NotImplementedError): - base_persistence.update_callback_data((None, {'foo': 'bar'})) + base_persistence.update_callback_data((None, {"foo": "bar"})) def test_implementation(self, updater, base_persistence): dp = updater.dispatcher @@ -314,13 +336,13 @@ def get_callback_data(): assert u.dispatcher.chat_data == chat_data assert u.dispatcher.user_data == user_data assert u.dispatcher.bot.callback_data_cache.persistence_data == callback_data - u.dispatcher.chat_data[442233]['test5'] = 'test6' - assert u.dispatcher.chat_data[442233]['test5'] == 'test6' + u.dispatcher.chat_data[442233]["test5"] = "test6" + assert u.dispatcher.chat_data[442233]["test5"] == "test6" - @pytest.mark.parametrize('run_async', [True, False], ids=['synchronous', 'run_async']) + @pytest.mark.parametrize("run_async", [True, False], ids=["synchronous", "run_async"]) def test_dispatcher_integration_handlers( self, - cdp, + dp, caplog, bot, base_persistence, @@ -351,68 +373,65 @@ def get_callback_data(): base_persistence.refresh_bot_data = lambda x: x base_persistence.refresh_chat_data = lambda x, y: x base_persistence.refresh_user_data = lambda x, y: x - updater = Updater(bot=bot, persistence=base_persistence, use_context=True) + updater = Updater(bot=bot, persistence=base_persistence) dp = updater.dispatcher def callback_known_user(update, context): - if not context.user_data['test1'] == 'test2': - pytest.fail('user_data corrupt') + if not context.user_data["test1"] == "test2": + pytest.fail("user_data corrupt") if not context.bot_data == bot_data: - pytest.fail('bot_data corrupt') + pytest.fail("bot_data corrupt") def callback_known_chat(update, context): - if not context.chat_data['test3'] == 'test4': - pytest.fail('chat_data corrupt') + if not context.chat_data["test3"] == "test4": + pytest.fail("chat_data corrupt") if not context.bot_data == bot_data: - pytest.fail('bot_data corrupt') + pytest.fail("bot_data corrupt") def callback_unknown_user_or_chat(update, context): if not context.user_data == {}: - pytest.fail('user_data corrupt') + pytest.fail("user_data corrupt") if not context.chat_data == {}: - pytest.fail('chat_data corrupt') + pytest.fail("chat_data corrupt") if not context.bot_data == bot_data: - pytest.fail('bot_data corrupt') - context.user_data[1] = 'test7' - context.chat_data[2] = 'test8' - context.bot_data['test0'] = 'test0' - context.bot.callback_data_cache.put('test0') + pytest.fail("bot_data corrupt") + context.user_data[1] = "test7" + context.chat_data[2] = "test8" + context.bot_data["test0"] = "test0" + context.bot.callback_data_cache.put("test0") known_user = MessageHandler( Filters.user(user_id=12345), callback_known_user, - pass_chat_data=True, - pass_user_data=True, ) known_chat = MessageHandler( Filters.chat(chat_id=-67890), callback_known_chat, - pass_chat_data=True, - pass_user_data=True, ) unknown = MessageHandler( - Filters.all, callback_unknown_user_or_chat, pass_chat_data=True, pass_user_data=True + Filters.all, + callback_unknown_user_or_chat, ) dp.add_handler(known_user) dp.add_handler(known_chat) dp.add_handler(unknown) - user1 = User(id=12345, first_name='test user', is_bot=False) - user2 = User(id=54321, first_name='test user', is_bot=False) - chat1 = Chat(id=-67890, type='group') - chat2 = Chat(id=-987654, type='group') + user1 = User(id=12345, first_name="test user", is_bot=False) + user2 = User(id=54321, first_name="test user", is_bot=False) + chat1 = Chat(id=-67890, type="group") + chat2 = Chat(id=-987654, type="group") m = Message(1, None, chat2, from_user=user1) u = Update(0, m) with caplog.at_level(logging.ERROR): dp.process_update(u) rec = caplog.records[-1] - assert rec.getMessage() == 'No error handlers are registered, logging exception.' - assert rec.levelname == 'ERROR' + assert rec.getMessage() == "No error handlers are registered, logging exception." + assert rec.levelname == "ERROR" rec = caplog.records[-2] - assert rec.getMessage() == 'No error handlers are registered, logging exception.' - assert rec.levelname == 'ERROR' + assert rec.getMessage() == "No error handlers are registered, logging exception." + assert rec.levelname == "ERROR" rec = caplog.records[-3] - assert rec.getMessage() == 'No error handlers are registered, logging exception.' - assert rec.levelname == 'ERROR' + assert rec.getMessage() == "No error handlers are registered, logging exception." + assert rec.levelname == "ERROR" m.from_user = user2 m.chat = chat1 u = Update(1, m) @@ -421,7 +440,7 @@ def callback_unknown_user_or_chat(update, context): u = Update(2, m) def save_bot_data(data): - if 'test0' not in data: + if "test0" not in data: pytest.fail() def save_chat_data(data): @@ -433,7 +452,7 @@ def save_user_data(data): pytest.fail() def save_callback_data(data): - if not assert_data_in_cache(dp.bot.callback_data, 'test0'): + if not assert_data_in_cache(dp.bot.callback_data, "test0"): pytest.fail() base_persistence.update_chat_data = save_chat_data @@ -442,24 +461,24 @@ def save_callback_data(data): base_persistence.update_callback_data = save_callback_data dp.process_update(u) - assert dp.user_data[54321][1] == 'test7' - assert dp.chat_data[-987654][2] == 'test8' - assert dp.bot_data['test0'] == 'test0' - assert assert_data_in_cache(dp.bot.callback_data_cache, 'test0') + assert dp.user_data[54321][1] == "test7" + assert dp.chat_data[-987654][2] == "test8" + assert dp.bot_data["test0"] == "test0" + assert assert_data_in_cache(dp.bot.callback_data_cache, "test0") @pytest.mark.parametrize( - 'store_user_data', [True, False], ids=['store_user_data-True', 'store_user_data-False'] + "store_user_data", [True, False], ids=["store_user_data-True", "store_user_data-False"] ) @pytest.mark.parametrize( - 'store_chat_data', [True, False], ids=['store_chat_data-True', 'store_chat_data-False'] + "store_chat_data", [True, False], ids=["store_chat_data-True", "store_chat_data-False"] ) @pytest.mark.parametrize( - 'store_bot_data', [True, False], ids=['store_bot_data-True', 'store_bot_data-False'] + "store_bot_data", [True, False], ids=["store_bot_data-True", "store_bot_data-False"] ) - @pytest.mark.parametrize('run_async', [True, False], ids=['synchronous', 'run_async']) + @pytest.mark.parametrize("run_async", [True, False], ids=["synchronous", "run_async"]) def test_persistence_dispatcher_integration_refresh_data( self, - cdp, + dp, base_persistence, chat_data, bot_data, @@ -470,69 +489,65 @@ def test_persistence_dispatcher_integration_refresh_data( run_async, ): base_persistence.refresh_bot_data = lambda x: x.setdefault( - 'refreshed', x.get('refreshed', 0) + 1 + "refreshed", x.get("refreshed", 0) + 1 ) # x is the user/chat_id - base_persistence.refresh_chat_data = lambda x, y: y.setdefault('refreshed', x) - base_persistence.refresh_user_data = lambda x, y: y.setdefault('refreshed', x) - base_persistence.store_bot_data = store_bot_data - base_persistence.store_chat_data = store_chat_data - base_persistence.store_user_data = store_user_data - cdp.persistence = base_persistence + base_persistence.refresh_chat_data = lambda x, y: y.setdefault("refreshed", x) + base_persistence.refresh_user_data = lambda x, y: y.setdefault("refreshed", x) + base_persistence.store_data = PersistenceInput( + bot_data=store_bot_data, chat_data=store_chat_data, user_data=store_user_data + ) + dp.persistence = base_persistence self.test_flag = True def callback_with_user_and_chat(update, context): if store_user_data: - if context.user_data.get('refreshed') != update.effective_user.id: - self.test_flag = 'user_data was not refreshed' + if context.user_data.get("refreshed") != update.effective_user.id: + self.test_flag = "user_data was not refreshed" else: - if 'refreshed' in context.user_data: - self.test_flag = 'user_data was wrongly refreshed' + if "refreshed" in context.user_data: + self.test_flag = "user_data was wrongly refreshed" if store_chat_data: - if context.chat_data.get('refreshed') != update.effective_chat.id: - self.test_flag = 'chat_data was not refreshed' + if context.chat_data.get("refreshed") != update.effective_chat.id: + self.test_flag = "chat_data was not refreshed" else: - if 'refreshed' in context.chat_data: - self.test_flag = 'chat_data was wrongly refreshed' + if "refreshed" in context.chat_data: + self.test_flag = "chat_data was wrongly refreshed" if store_bot_data: - if context.bot_data.get('refreshed') != 1: - self.test_flag = 'bot_data was not refreshed' + if context.bot_data.get("refreshed") != 1: + self.test_flag = "bot_data was not refreshed" else: - if 'refreshed' in context.bot_data: - self.test_flag = 'bot_data was wrongly refreshed' + if "refreshed" in context.bot_data: + self.test_flag = "bot_data was wrongly refreshed" def callback_without_user_and_chat(_, context): if store_bot_data: - if context.bot_data.get('refreshed') != 1: - self.test_flag = 'bot_data was not refreshed' + if context.bot_data.get("refreshed") != 1: + self.test_flag = "bot_data was not refreshed" else: - if 'refreshed' in context.bot_data: - self.test_flag = 'bot_data was wrongly refreshed' + if "refreshed" in context.bot_data: + self.test_flag = "bot_data was wrongly refreshed" with_user_and_chat = MessageHandler( Filters.user(user_id=12345), callback_with_user_and_chat, - pass_chat_data=True, - pass_user_data=True, run_async=run_async, ) without_user_and_chat = MessageHandler( Filters.all, callback_without_user_and_chat, - pass_chat_data=True, - pass_user_data=True, run_async=run_async, ) - cdp.add_handler(with_user_and_chat) - cdp.add_handler(without_user_and_chat) - user = User(id=12345, first_name='test user', is_bot=False) - chat = Chat(id=-987654, type='group') + dp.add_handler(with_user_and_chat) + dp.add_handler(without_user_and_chat) + user = User(id=12345, first_name="test user", is_bot=False) + chat = Chat(id=-987654, type="group") m = Message(1, None, chat, from_user=user) # has user and chat u = Update(0, m) - cdp.process_update(u) + dp.process_update(u) assert self.test_flag is True @@ -540,7 +555,7 @@ def callback_without_user_and_chat(_, context): m.from_user = None m.chat = None u = Update(1, m) - cdp.process_update(u) + dp.process_update(u) assert self.test_flag is True @@ -559,11 +574,11 @@ class MyUpdate: with caplog.at_level(logging.ERROR): dp.process_update(MyUpdate()) - assert 'An uncaught error was raised while processing the update' not in caplog.text + assert "An uncaught error was raised while processing the update" not in caplog.text def test_bot_replace_insert_bot(self, bot, bot_persistence): class CustomSlottedClass: - __slots__ = ('bot', '__dict__') + __slots__ = ("bot", "__dict__") def __init__(self): self.bot = bot @@ -646,7 +661,7 @@ def __eq__(self, other): assert persistence.user_data[123][1].bot == BasePersistence.REPLACED_BOT assert persistence.user_data[123][1] == cc.replace_bot() - persistence.update_callback_data(([('1', 2, {0: cc})], {'1': '2'})) + persistence.update_callback_data(([("1", 2, {0: cc})], {"1": "2"})) assert persistence.callback_data[0][0][2][0].bot == BasePersistence.REPLACED_BOT assert persistence.callback_data[0][0][2][0] == cc.replace_bot() @@ -666,7 +681,7 @@ def test_bot_replace_insert_bot_unpickable_objects(self, bot, bot_persistence, r class CustomClass: def __copy__(self): - raise TypeError('UnhandledException') + raise TypeError("UnhandledException") lock = Lock() @@ -676,7 +691,7 @@ def __copy__(self): assert persistence.chat_data[123][1] is lock persistence.update_user_data(123, {1: lock}) assert persistence.user_data[123][1] is lock - persistence.update_callback_data(([('1', 2, {0: lock})], {'1': '2'})) + persistence.update_callback_data(([("1", 2, {0: lock})], {"1": "2"})) assert persistence.callback_data[0][0][2][0] is lock assert persistence.get_bot_data()[1] is lock @@ -692,7 +707,7 @@ def __copy__(self): assert persistence.chat_data[123][1] is cc persistence.update_user_data(123, {1: cc}) assert persistence.user_data[123][1] is cc - persistence.update_callback_data(([('1', 2, {0: cc})], {'1': '2'})) + persistence.update_callback_data(([("1", 2, {0: cc})], {"1": "2"})) assert persistence.callback_data[0][0][2][0] is cc assert persistence.get_bot_data()[1] is cc @@ -722,7 +737,7 @@ def test_bot_replace_insert_bot_unparsable_objects(self, bot, bot_persistence, r assert persistence.chat_data[123][1] is uuid_obj persistence.update_user_data(123, {1: uuid_obj}) assert persistence.user_data[123][1] is uuid_obj - persistence.update_callback_data(([('1', 2, {0: uuid_obj})], {'1': '2'})) + persistence.update_callback_data(([("1", 2, {0: uuid_obj})], {"1": "2"})) assert persistence.callback_data[0][0][2][0] is uuid_obj assert persistence.get_bot_data()[1] is uuid_obj @@ -777,8 +792,8 @@ def __init__(self, data): def __eq__(self, other): raise RuntimeError("Can't be compared") - cc = CustomClass({1: bot, 2: 'foo'}) - expected = {1: BasePersistence.REPLACED_BOT, 2: 'foo'} + cc = CustomClass({1: bot, 2: "foo"}) + expected = {1: BasePersistence.REPLACED_BOT, 2: "foo"} persistence.update_bot_data({1: cc}) assert persistence.bot_data[1].data == expected @@ -786,23 +801,23 @@ def __eq__(self, other): assert persistence.chat_data[123][1].data == expected persistence.update_user_data(123, {1: cc}) assert persistence.user_data[123][1].data == expected - persistence.update_callback_data(([('1', 2, {0: cc})], {'1': '2'})) + persistence.update_callback_data(([("1", 2, {0: cc})], {"1": "2"})) assert persistence.callback_data[0][0][2][0].data == expected - expected = {1: bot, 2: 'foo'} + expected = {1: bot, 2: "foo"} assert persistence.get_bot_data()[1].data == expected assert persistence.get_chat_data()[123][1].data == expected assert persistence.get_user_data()[123][1].data == expected assert persistence.get_callback_data()[0][0][2][0].data == expected - @pytest.mark.filterwarnings('ignore:BasePersistence') + @pytest.mark.filterwarnings("ignore:BasePersistence") def test_replace_insert_bot_item_identity(self, bot, bot_persistence): persistence = bot_persistence persistence.set_bot(bot) class CustomSlottedClass: - __slots__ = ('value',) + __slots__ = ("value",) def __init__(self): self.value = 5 @@ -818,29 +833,29 @@ class CustomClass: dict_ = {1: slot_object, 2: dict_object} data = { - 'bot_1': bot, - 'bot_2': bot, - 'list_1': list_, - 'list_2': list_, - 'tuple_1': tuple_, - 'tuple_2': tuple_, - 'dict_1': dict_, - 'dict_2': dict_, + "bot_1": bot, + "bot_2": bot, + "list_1": list_, + "list_2": list_, + "tuple_1": tuple_, + "tuple_2": tuple_, + "dict_1": dict_, + "dict_2": dict_, } def make_assertion(data_): return ( - data_['bot_1'] is data_['bot_2'] - and data_['list_1'] is data_['list_2'] - and data_['list_1'][0] is data_['list_2'][0] - and data_['list_1'][1] is data_['list_2'][1] - and data_['list_1'][2] is data_['list_2'][2] - and data_['tuple_1'] is data_['tuple_2'] - and data_['dict_1'] is data_['dict_2'] - and data_['dict_1'][1] is data_['dict_2'][1] - and data_['dict_1'][1] is data_['list_1'][0] - and data_['dict_1'][2] is data_['list_1'][1] - and data_['dict_1'][2] is data_['dict_2'][2] + data_["bot_1"] is data_["bot_2"] + and data_["list_1"] is data_["list_2"] + and data_["list_1"][0] is data_["list_2"][0] + and data_["list_1"][1] is data_["list_2"][1] + and data_["list_1"][2] is data_["list_2"][2] + and data_["tuple_1"] is data_["tuple_2"] + and data_["dict_1"] is data_["dict_2"] + and data_["dict_1"][1] is data_["dict_2"][1] + and data_["dict_1"][1] is data_["list_1"][0] + and data_["dict_1"][2] is data_["list_1"][1] + and data_["dict_1"][2] is data_["dict_2"][2] ) persistence.update_bot_data(data) @@ -849,178 +864,162 @@ def make_assertion(data_): def test_set_bot_exception(self, bot): non_ext_bot = Bot(bot.token) - persistence = OwnPersistence(store_callback_data=True) - with pytest.raises(TypeError, match='store_callback_data can only be used'): + persistence = OwnPersistence() + with pytest.raises(TypeError, match="callback_data can only be stored"): persistence.set_bot(non_ext_bot) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_persistence(): return PicklePersistence( - filename='pickletest', - store_user_data=True, - store_chat_data=True, - store_bot_data=True, - store_callback_data=True, + filename="pickletest", single_file=False, on_flush=False, ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_persistence_only_bot(): return PicklePersistence( - filename='pickletest', - store_user_data=False, - store_chat_data=False, - store_bot_data=True, - store_callback_data=False, + filename="pickletest", + store_data=PersistenceInput(callback_data=False, user_data=False, chat_data=False), single_file=False, on_flush=False, ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_persistence_only_chat(): return PicklePersistence( - filename='pickletest', - store_user_data=False, - store_chat_data=True, - store_bot_data=False, - store_callback_data=False, + filename="pickletest", + store_data=PersistenceInput(callback_data=False, user_data=False, bot_data=False), single_file=False, on_flush=False, ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_persistence_only_user(): return PicklePersistence( - filename='pickletest', - store_user_data=True, - store_chat_data=False, - store_bot_data=False, - store_callback_data=False, + filename="pickletest", + store_data=PersistenceInput(callback_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_persistence_only_callback(): return PicklePersistence( - filename='pickletest', - store_user_data=False, - store_chat_data=False, - store_bot_data=False, - store_callback_data=True, + filename="pickletest", + store_data=PersistenceInput(user_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def bad_pickle_files(): for name in [ - 'pickletest_user_data', - 'pickletest_chat_data', - 'pickletest_bot_data', - 'pickletest_callback_data', - 'pickletest_conversations', - 'pickletest', + "pickletest_user_data", + "pickletest_chat_data", + "pickletest_bot_data", + "pickletest_callback_data", + "pickletest_conversations", + "pickletest", ]: - with open(name, 'w') as f: - f.write('(())') + with open(name, "w") as f: + f.write("(())") yield True -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def invalid_pickle_files(): for name in [ - 'pickletest_user_data', - 'pickletest_chat_data', - 'pickletest_bot_data', - 'pickletest_callback_data', - 'pickletest_conversations', - 'pickletest', + "pickletest_user_data", + "pickletest_chat_data", + "pickletest_bot_data", + "pickletest_callback_data", + "pickletest_conversations", + "pickletest", ]: # Just a random way to trigger pickle.UnpicklingError # see https://stackoverflow.com/a/44422239/10606962 - with gzip.open(name, 'wb') as file: + with gzip.open(name, "wb") as file: pickle.dump([1, 2, 3], file) yield True -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def good_pickle_files(user_data, chat_data, bot_data, callback_data, conversations): data = { - 'user_data': user_data, - 'chat_data': chat_data, - 'bot_data': bot_data, - 'callback_data': callback_data, - 'conversations': conversations, + "user_data": user_data, + "chat_data": chat_data, + "bot_data": bot_data, + "callback_data": callback_data, + "conversations": conversations, } - with open('pickletest_user_data', 'wb') as f: + with open("pickletest_user_data", "wb") as f: pickle.dump(user_data, f) - with open('pickletest_chat_data', 'wb') as f: + with open("pickletest_chat_data", "wb") as f: pickle.dump(chat_data, f) - with open('pickletest_bot_data', 'wb') as f: + with open("pickletest_bot_data", "wb") as f: pickle.dump(bot_data, f) - with open('pickletest_callback_data', 'wb') as f: + with open("pickletest_callback_data", "wb") as f: pickle.dump(callback_data, f) - with open('pickletest_conversations', 'wb') as f: + with open("pickletest_conversations", "wb") as f: pickle.dump(conversations, f) - with open('pickletest', 'wb') as f: + with open("pickletest", "wb") as f: pickle.dump(data, f) yield True -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_files_wo_bot_data(user_data, chat_data, callback_data, conversations): data = { - 'user_data': user_data, - 'chat_data': chat_data, - 'conversations': conversations, - 'callback_data': callback_data, + "user_data": user_data, + "chat_data": chat_data, + "conversations": conversations, + "callback_data": callback_data, } - with open('pickletest_user_data', 'wb') as f: + with open("pickletest_user_data", "wb") as f: pickle.dump(user_data, f) - with open('pickletest_chat_data', 'wb') as f: + with open("pickletest_chat_data", "wb") as f: pickle.dump(chat_data, f) - with open('pickletest_callback_data', 'wb') as f: + with open("pickletest_callback_data", "wb") as f: pickle.dump(callback_data, f) - with open('pickletest_conversations', 'wb') as f: + with open("pickletest_conversations", "wb") as f: pickle.dump(conversations, f) - with open('pickletest', 'wb') as f: + with open("pickletest", "wb") as f: pickle.dump(data, f) yield True -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def pickle_files_wo_callback_data(user_data, chat_data, bot_data, conversations): data = { - 'user_data': user_data, - 'chat_data': chat_data, - 'bot_data': bot_data, - 'conversations': conversations, + "user_data": user_data, + "chat_data": chat_data, + "bot_data": bot_data, + "conversations": conversations, } - with open('pickletest_user_data', 'wb') as f: + with open("pickletest_user_data", "wb") as f: pickle.dump(user_data, f) - with open('pickletest_chat_data', 'wb') as f: + with open("pickletest_chat_data", "wb") as f: pickle.dump(chat_data, f) - with open('pickletest_bot_data', 'wb') as f: + with open("pickletest_bot_data", "wb") as f: pickle.dump(bot_data, f) - with open('pickletest_conversations', 'wb') as f: + with open("pickletest_conversations", "wb") as f: pickle.dump(conversations, f) - with open('pickletest', 'wb') as f: + with open("pickletest", "wb") as f: pickle.dump(data, f) yield True -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def update(bot): - user = User(id=321, first_name='test_user', is_bot=False) - chat = Chat(id=123, type='group') + user = User(id=321, first_name="test_user", is_bot=False) + chat = Chat(id=123, type="group") message = Message(1, None, chat, from_user=user, text="Hi there", bot=bot) return Update(0, message=message) @@ -1030,18 +1029,15 @@ class CustomMapping(defaultdict): class TestPicklePersistence: - def test_slot_behaviour(self, mro_slots, recwarn, pickle_persistence): + def test_slot_behaviour(self, mro_slots, pickle_persistence): inst = pickle_persistence for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - # assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.store_user_data = 'should give warning', {} - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_pickle_behaviour_with_slots(self, pickle_persistence): bot_data = pickle_persistence.get_bot_data() - bot_data['message'] = Message(3, None, Chat(2, type='supergroup')) + bot_data["message"] = Message(3, None, Chat(2, type="supergroup")) pickle_persistence.update_bot_data(bot_data) retrieved = pickle_persistence.get_bot_data() assert retrieved == bot_data @@ -1054,8 +1050,8 @@ def test_no_files_present_multi_file(self, pickle_persistence): assert pickle_persistence.get_bot_data() == {} assert pickle_persistence.get_bot_data() == {} assert pickle_persistence.get_callback_data() is None - assert pickle_persistence.get_conversations('noname') == {} - assert pickle_persistence.get_conversations('noname') == {} + assert pickle_persistence.get_conversations("noname") == {} + assert pickle_persistence.get_conversations("noname") == {} def test_no_files_present_single_file(self, pickle_persistence): pickle_persistence.single_file = True @@ -1063,89 +1059,89 @@ def test_no_files_present_single_file(self, pickle_persistence): assert pickle_persistence.get_chat_data() == defaultdict(dict) assert pickle_persistence.get_bot_data() == {} assert pickle_persistence.get_callback_data() is None - assert pickle_persistence.get_conversations('noname') == {} + assert pickle_persistence.get_conversations("noname") == {} def test_with_bad_multi_file(self, pickle_persistence, bad_pickle_files): - with pytest.raises(TypeError, match='pickletest_user_data'): + with pytest.raises(TypeError, match="pickletest_user_data"): pickle_persistence.get_user_data() - with pytest.raises(TypeError, match='pickletest_chat_data'): + with pytest.raises(TypeError, match="pickletest_chat_data"): pickle_persistence.get_chat_data() - with pytest.raises(TypeError, match='pickletest_bot_data'): + with pytest.raises(TypeError, match="pickletest_bot_data"): pickle_persistence.get_bot_data() - with pytest.raises(TypeError, match='pickletest_callback_data'): + with pytest.raises(TypeError, match="pickletest_callback_data"): pickle_persistence.get_callback_data() - with pytest.raises(TypeError, match='pickletest_conversations'): - pickle_persistence.get_conversations('name') + with pytest.raises(TypeError, match="pickletest_conversations"): + pickle_persistence.get_conversations("name") def test_with_invalid_multi_file(self, pickle_persistence, invalid_pickle_files): - with pytest.raises(TypeError, match='pickletest_user_data does not contain'): + with pytest.raises(TypeError, match="pickletest_user_data does not contain"): pickle_persistence.get_user_data() - with pytest.raises(TypeError, match='pickletest_chat_data does not contain'): + with pytest.raises(TypeError, match="pickletest_chat_data does not contain"): pickle_persistence.get_chat_data() - with pytest.raises(TypeError, match='pickletest_bot_data does not contain'): + with pytest.raises(TypeError, match="pickletest_bot_data does not contain"): pickle_persistence.get_bot_data() - with pytest.raises(TypeError, match='pickletest_callback_data does not contain'): + with pytest.raises(TypeError, match="pickletest_callback_data does not contain"): pickle_persistence.get_callback_data() - with pytest.raises(TypeError, match='pickletest_conversations does not contain'): - pickle_persistence.get_conversations('name') + with pytest.raises(TypeError, match="pickletest_conversations does not contain"): + pickle_persistence.get_conversations("name") def test_with_bad_single_file(self, pickle_persistence, bad_pickle_files): pickle_persistence.single_file = True - with pytest.raises(TypeError, match='pickletest'): + with pytest.raises(TypeError, match="pickletest"): pickle_persistence.get_user_data() - with pytest.raises(TypeError, match='pickletest'): + with pytest.raises(TypeError, match="pickletest"): pickle_persistence.get_chat_data() - with pytest.raises(TypeError, match='pickletest'): + with pytest.raises(TypeError, match="pickletest"): pickle_persistence.get_bot_data() - with pytest.raises(TypeError, match='pickletest'): + with pytest.raises(TypeError, match="pickletest"): pickle_persistence.get_callback_data() - with pytest.raises(TypeError, match='pickletest'): - pickle_persistence.get_conversations('name') + with pytest.raises(TypeError, match="pickletest"): + pickle_persistence.get_conversations("name") def test_with_invalid_single_file(self, pickle_persistence, invalid_pickle_files): pickle_persistence.single_file = True - with pytest.raises(TypeError, match='pickletest does not contain'): + with pytest.raises(TypeError, match="pickletest does not contain"): pickle_persistence.get_user_data() - with pytest.raises(TypeError, match='pickletest does not contain'): + with pytest.raises(TypeError, match="pickletest does not contain"): pickle_persistence.get_chat_data() - with pytest.raises(TypeError, match='pickletest does not contain'): + with pytest.raises(TypeError, match="pickletest does not contain"): pickle_persistence.get_bot_data() - with pytest.raises(TypeError, match='pickletest does not contain'): + with pytest.raises(TypeError, match="pickletest does not contain"): pickle_persistence.get_callback_data() - with pytest.raises(TypeError, match='pickletest does not contain'): - pickle_persistence.get_conversations('name') + with pytest.raises(TypeError, match="pickletest does not contain"): + pickle_persistence.get_conversations("name") def test_with_good_multi_file(self, pickle_persistence, good_pickle_files): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) - assert bot_data['test1'] == 'test2' - assert bot_data['test3']['test4'] == 'test5' - assert 'test0' not in bot_data + assert bot_data["test1"] == "test2" + assert bot_data["test3"]["test4"] == "test5" + assert "test0" not in bot_data callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) - assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] - assert callback_data[1] == {'test1': 'test2'} + assert callback_data[0] == [("test1", 1000, {"button1": "test0", "button2": "test1"})] + assert callback_data[1] == {"test1": "test2"} - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = pickle_persistence.get_conversations('name2') + conversation2 = pickle_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -1156,34 +1152,34 @@ def test_with_good_single_file(self, pickle_persistence, good_pickle_files): pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) - assert bot_data['test1'] == 'test2' - assert bot_data['test3']['test4'] == 'test5' - assert 'test0' not in bot_data + assert bot_data["test1"] == "test2" + assert bot_data["test3"]["test4"] == "test5" + assert "test0" not in bot_data callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) - assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] - assert callback_data[1] == {'test1': 'test2'} + assert callback_data[0] == [("test1", 1000, {"button1": "test0", "button2": "test1"})] + assert callback_data[1] == {"test1": "test2"} - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = pickle_persistence.get_conversations('name2') + conversation2 = pickle_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -1193,14 +1189,14 @@ def test_with_good_single_file(self, pickle_persistence, good_pickle_files): def test_with_multi_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_bot_data): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = pickle_persistence.get_bot_data() @@ -1209,16 +1205,16 @@ def test_with_multi_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_b callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) - assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] - assert callback_data[1] == {'test1': 'test2'} + assert callback_data[0] == [("test1", 1000, {"button1": "test0", "button2": "test1"})] + assert callback_data[1] == {"test1": "test2"} - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = pickle_persistence.get_conversations('name2') + conversation2 = pickle_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -1230,32 +1226,32 @@ def test_with_multi_file_wo_callback_data( ): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) - assert bot_data['test1'] == 'test2' - assert bot_data['test3']['test4'] == 'test5' - assert 'test0' not in bot_data + assert bot_data["test1"] == "test2" + assert bot_data["test3"]["test4"] == "test5" + assert "test0" not in bot_data callback_data = pickle_persistence.get_callback_data() assert callback_data is None - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = pickle_persistence.get_conversations('name2') + conversation2 = pickle_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -1266,14 +1262,14 @@ def test_with_single_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_ pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = pickle_persistence.get_bot_data() @@ -1282,16 +1278,16 @@ def test_with_single_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_ callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) - assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] - assert callback_data[1] == {'test1': 'test2'} + assert callback_data[0] == [("test1", 1000, {"button1": "test0", "button2": "test1"})] + assert callback_data[1] == {"test1": "test2"} - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = pickle_persistence.get_conversations('name2') + conversation2 = pickle_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -1303,32 +1299,32 @@ def test_with_single_file_wo_callback_data( ): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) - assert bot_data['test1'] == 'test2' - assert bot_data['test3']['test4'] == 'test5' - assert 'test0' not in bot_data + assert bot_data["test1"] == "test2" + assert bot_data["test3"]["test4"] == "test5" + assert "test0" not in bot_data callback_data = pickle_persistence.get_callback_data() assert callback_data is None - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = pickle_persistence.get_conversations('name2') + conversation2 = pickle_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -1337,133 +1333,133 @@ def test_with_single_file_wo_callback_data( def test_updating_multi_file(self, pickle_persistence, good_pickle_files): user_data = pickle_persistence.get_user_data() - user_data[12345]['test3']['test4'] = 'test6' + user_data[12345]["test3"]["test4"] = "test6" assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) - user_data[12345]['test3']['test4'] = 'test7' + user_data[12345]["test3"]["test4"] = "test7" assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) assert pickle_persistence.user_data == user_data - with open('pickletest_user_data', 'rb') as f: + with open("pickletest_user_data", "rb") as f: user_data_test = defaultdict(dict, pickle.load(f)) assert user_data_test == user_data chat_data = pickle_persistence.get_chat_data() - chat_data[-12345]['test3']['test4'] = 'test6' + chat_data[-12345]["test3"]["test4"] = "test6" assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) - chat_data[-12345]['test3']['test4'] = 'test7' + chat_data[-12345]["test3"]["test4"] = "test7" assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) assert pickle_persistence.chat_data == chat_data - with open('pickletest_chat_data', 'rb') as f: + with open("pickletest_chat_data", "rb") as f: chat_data_test = defaultdict(dict, pickle.load(f)) assert chat_data_test == chat_data bot_data = pickle_persistence.get_bot_data() - bot_data['test3']['test4'] = 'test6' + bot_data["test3"]["test4"] = "test6" assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) - bot_data['test3']['test4'] = 'test7' + bot_data["test3"]["test4"] = "test7" assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data - with open('pickletest_bot_data', 'rb') as f: + with open("pickletest_bot_data", "rb") as f: bot_data_test = pickle.load(f) assert bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() - callback_data[1]['test3'] = 'test4' + callback_data[1]["test3"] = "test4" assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) - callback_data[1]['test3'] = 'test5' + callback_data[1]["test3"] = "test5" assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data - with open('pickletest_callback_data', 'rb') as f: + with open("pickletest_callback_data", "rb") as f: callback_data_test = pickle.load(f) assert callback_data_test == callback_data - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations['name1'] == conversation1 - pickle_persistence.update_conversation('name1', (123, 123), 5) - assert pickle_persistence.conversations['name1'] == conversation1 - assert pickle_persistence.get_conversations('name1') == conversation1 - with open('pickletest_conversations', 'rb') as f: + assert not pickle_persistence.conversations["name1"] == conversation1 + pickle_persistence.update_conversation("name1", (123, 123), 5) + assert pickle_persistence.conversations["name1"] == conversation1 + assert pickle_persistence.get_conversations("name1") == conversation1 + with open("pickletest_conversations", "rb") as f: conversations_test = defaultdict(dict, pickle.load(f)) - assert conversations_test['name1'] == conversation1 + assert conversations_test["name1"] == conversation1 pickle_persistence.conversations = None - pickle_persistence.update_conversation('name1', (123, 123), 5) - assert pickle_persistence.conversations['name1'] == {(123, 123): 5} - assert pickle_persistence.get_conversations('name1') == {(123, 123): 5} + pickle_persistence.update_conversation("name1", (123, 123), 5) + assert pickle_persistence.conversations["name1"] == {(123, 123): 5} + assert pickle_persistence.get_conversations("name1") == {(123, 123): 5} def test_updating_single_file(self, pickle_persistence, good_pickle_files): pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() - user_data[12345]['test3']['test4'] = 'test6' + user_data[12345]["test3"]["test4"] = "test6" assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) - user_data[12345]['test3']['test4'] = 'test7' + user_data[12345]["test3"]["test4"] = "test7" assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) assert pickle_persistence.user_data == user_data - with open('pickletest', 'rb') as f: - user_data_test = defaultdict(dict, pickle.load(f)['user_data']) + with open("pickletest", "rb") as f: + user_data_test = defaultdict(dict, pickle.load(f)["user_data"]) assert user_data_test == user_data chat_data = pickle_persistence.get_chat_data() - chat_data[-12345]['test3']['test4'] = 'test6' + chat_data[-12345]["test3"]["test4"] = "test6" assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) - chat_data[-12345]['test3']['test4'] = 'test7' + chat_data[-12345]["test3"]["test4"] = "test7" assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) assert pickle_persistence.chat_data == chat_data - with open('pickletest', 'rb') as f: - chat_data_test = defaultdict(dict, pickle.load(f)['chat_data']) + with open("pickletest", "rb") as f: + chat_data_test = defaultdict(dict, pickle.load(f)["chat_data"]) assert chat_data_test == chat_data bot_data = pickle_persistence.get_bot_data() - bot_data['test3']['test4'] = 'test6' + bot_data["test3"]["test4"] = "test6" assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) - bot_data['test3']['test4'] = 'test7' + bot_data["test3"]["test4"] = "test7" assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data - with open('pickletest', 'rb') as f: - bot_data_test = pickle.load(f)['bot_data'] + with open("pickletest", "rb") as f: + bot_data_test = pickle.load(f)["bot_data"] assert bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() - callback_data[1]['test3'] = 'test4' + callback_data[1]["test3"] = "test4" assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) - callback_data[1]['test3'] = 'test5' + callback_data[1]["test3"] = "test5" assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data - with open('pickletest', 'rb') as f: - callback_data_test = pickle.load(f)['callback_data'] + with open("pickletest", "rb") as f: + callback_data_test = pickle.load(f)["callback_data"] assert callback_data_test == callback_data - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations['name1'] == conversation1 - pickle_persistence.update_conversation('name1', (123, 123), 5) - assert pickle_persistence.conversations['name1'] == conversation1 - assert pickle_persistence.get_conversations('name1') == conversation1 - with open('pickletest', 'rb') as f: - conversations_test = defaultdict(dict, pickle.load(f)['conversations']) - assert conversations_test['name1'] == conversation1 + assert not pickle_persistence.conversations["name1"] == conversation1 + pickle_persistence.update_conversation("name1", (123, 123), 5) + assert pickle_persistence.conversations["name1"] == conversation1 + assert pickle_persistence.get_conversations("name1") == conversation1 + with open("pickletest", "rb") as f: + conversations_test = defaultdict(dict, pickle.load(f)["conversations"]) + assert conversations_test["name1"] == conversation1 pickle_persistence.conversations = None - pickle_persistence.update_conversation('name1', (123, 123), 5) - assert pickle_persistence.conversations['name1'] == {(123, 123): 5} - assert pickle_persistence.get_conversations('name1') == {(123, 123): 5} + pickle_persistence.update_conversation("name1", (123, 123), 5) + assert pickle_persistence.conversations["name1"] == {(123, 123): 5} + assert pickle_persistence.get_conversations("name1") == {(123, 123): 5} def test_updating_single_file_no_data(self, pickle_persistence): pickle_persistence.single_file = True @@ -1477,8 +1473,8 @@ def test_updating_single_file_no_data(self, pickle_persistence): ] ) pickle_persistence.flush() - with pytest.raises(FileNotFoundError, match='pickletest'): - open('pickletest', 'rb') + with pytest.raises(FileNotFoundError, match="pickletest"): + open("pickletest", "rb") def test_save_on_flush_multi_files(self, pickle_persistence, good_pickle_files): # Should run without error @@ -1486,76 +1482,76 @@ def test_save_on_flush_multi_files(self, pickle_persistence, good_pickle_files): pickle_persistence.on_flush = True user_data = pickle_persistence.get_user_data() - user_data[54321]['test9'] = 'test 10' + user_data[54321]["test9"] = "test 10" assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(54321, user_data[54321]) assert pickle_persistence.user_data == user_data - with open('pickletest_user_data', 'rb') as f: + with open("pickletest_user_data", "rb") as f: user_data_test = defaultdict(dict, pickle.load(f)) assert not user_data_test == user_data chat_data = pickle_persistence.get_chat_data() - chat_data[54321]['test9'] = 'test 10' + chat_data[54321]["test9"] = "test 10" assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(54321, chat_data[54321]) assert pickle_persistence.chat_data == chat_data - with open('pickletest_chat_data', 'rb') as f: + with open("pickletest_chat_data", "rb") as f: chat_data_test = defaultdict(dict, pickle.load(f)) assert not chat_data_test == chat_data bot_data = pickle_persistence.get_bot_data() - bot_data['test6'] = 'test 7' + bot_data["test6"] = "test 7" assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data - with open('pickletest_bot_data', 'rb') as f: + with open("pickletest_bot_data", "rb") as f: bot_data_test = pickle.load(f) assert not bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() - callback_data[1]['test3'] = 'test4' + callback_data[1]["test3"] = "test4" assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data - with open('pickletest_callback_data', 'rb') as f: + with open("pickletest_callback_data", "rb") as f: callback_data_test = pickle.load(f) assert not callback_data_test == callback_data - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations['name1'] == conversation1 + assert not pickle_persistence.conversations["name1"] == conversation1 - pickle_persistence.update_conversation('name1', (123, 123), 5) - assert pickle_persistence.conversations['name1'] == conversation1 + pickle_persistence.update_conversation("name1", (123, 123), 5) + assert pickle_persistence.conversations["name1"] == conversation1 - with open('pickletest_conversations', 'rb') as f: + with open("pickletest_conversations", "rb") as f: conversations_test = defaultdict(dict, pickle.load(f)) - assert not conversations_test['name1'] == conversation1 + assert not conversations_test["name1"] == conversation1 pickle_persistence.flush() - with open('pickletest_user_data', 'rb') as f: + with open("pickletest_user_data", "rb") as f: user_data_test = defaultdict(dict, pickle.load(f)) assert user_data_test == user_data - with open('pickletest_chat_data', 'rb') as f: + with open("pickletest_chat_data", "rb") as f: chat_data_test = defaultdict(dict, pickle.load(f)) assert chat_data_test == chat_data - with open('pickletest_bot_data', 'rb') as f: + with open("pickletest_bot_data", "rb") as f: bot_data_test = pickle.load(f) assert bot_data_test == bot_data - with open('pickletest_conversations', 'rb') as f: + with open("pickletest_conversations", "rb") as f: conversations_test = defaultdict(dict, pickle.load(f)) - assert conversations_test['name1'] == conversation1 + assert conversations_test["name1"] == conversation1 def test_save_on_flush_single_files(self, pickle_persistence, good_pickle_files): # Should run without error @@ -1565,69 +1561,69 @@ def test_save_on_flush_single_files(self, pickle_persistence, good_pickle_files) pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() - user_data[54321]['test9'] = 'test 10' + user_data[54321]["test9"] = "test 10" assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(54321, user_data[54321]) assert pickle_persistence.user_data == user_data - with open('pickletest', 'rb') as f: - user_data_test = defaultdict(dict, pickle.load(f)['user_data']) + with open("pickletest", "rb") as f: + user_data_test = defaultdict(dict, pickle.load(f)["user_data"]) assert not user_data_test == user_data chat_data = pickle_persistence.get_chat_data() - chat_data[54321]['test9'] = 'test 10' + chat_data[54321]["test9"] = "test 10" assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(54321, chat_data[54321]) assert pickle_persistence.chat_data == chat_data - with open('pickletest', 'rb') as f: - chat_data_test = defaultdict(dict, pickle.load(f)['chat_data']) + with open("pickletest", "rb") as f: + chat_data_test = defaultdict(dict, pickle.load(f)["chat_data"]) assert not chat_data_test == chat_data bot_data = pickle_persistence.get_bot_data() - bot_data['test6'] = 'test 7' + bot_data["test6"] = "test 7" assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data - with open('pickletest', 'rb') as f: - bot_data_test = pickle.load(f)['bot_data'] + with open("pickletest", "rb") as f: + bot_data_test = pickle.load(f)["bot_data"] assert not bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() - callback_data[1]['test3'] = 'test4' + callback_data[1]["test3"] = "test4" assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data - with open('pickletest', 'rb') as f: - callback_data_test = pickle.load(f)['callback_data'] + with open("pickletest", "rb") as f: + callback_data_test = pickle.load(f)["callback_data"] assert not callback_data_test == callback_data - conversation1 = pickle_persistence.get_conversations('name1') + conversation1 = pickle_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not pickle_persistence.conversations['name1'] == conversation1 - pickle_persistence.update_conversation('name1', (123, 123), 5) - assert pickle_persistence.conversations['name1'] == conversation1 - with open('pickletest', 'rb') as f: - conversations_test = defaultdict(dict, pickle.load(f)['conversations']) - assert not conversations_test['name1'] == conversation1 + assert not pickle_persistence.conversations["name1"] == conversation1 + pickle_persistence.update_conversation("name1", (123, 123), 5) + assert pickle_persistence.conversations["name1"] == conversation1 + with open("pickletest", "rb") as f: + conversations_test = defaultdict(dict, pickle.load(f)["conversations"]) + assert not conversations_test["name1"] == conversation1 pickle_persistence.flush() - with open('pickletest', 'rb') as f: - user_data_test = defaultdict(dict, pickle.load(f)['user_data']) + with open("pickletest", "rb") as f: + user_data_test = defaultdict(dict, pickle.load(f)["user_data"]) assert user_data_test == user_data - with open('pickletest', 'rb') as f: - chat_data_test = defaultdict(dict, pickle.load(f)['chat_data']) + with open("pickletest", "rb") as f: + chat_data_test = defaultdict(dict, pickle.load(f)["chat_data"]) assert chat_data_test == chat_data - with open('pickletest', 'rb') as f: - bot_data_test = pickle.load(f)['bot_data'] + with open("pickletest", "rb") as f: + bot_data_test = pickle.load(f)["bot_data"] assert bot_data_test == bot_data - with open('pickletest', 'rb') as f: - conversations_test = defaultdict(dict, pickle.load(f)['conversations']) - assert conversations_test['name1'] == conversation1 + with open("pickletest", "rb") as f: + conversations_test = defaultdict(dict, pickle.load(f)["conversations"]) + assert conversations_test["name1"] == conversation1 def test_with_handler(self, bot, update, bot_data, pickle_persistence, good_pickle_files): - u = Updater(bot=bot, persistence=pickle_persistence, use_context=True) + u = Updater(bot=bot, persistence=pickle_persistence) dp = u.dispatcher bot.callback_data_cache.clear_callback_data() bot.callback_data_cache.clear_callback_queries() @@ -1641,31 +1637,27 @@ def first(update, context): pytest.fail() if not context.bot.callback_data_cache.persistence_data == ([], {}): pytest.fail() - context.user_data['test1'] = 'test2' - context.chat_data['test3'] = 'test4' - context.bot_data['test1'] = 'test0' - context.bot.callback_data_cache._callback_queries['test1'] = 'test0' + context.user_data["test1"] = "test2" + context.chat_data["test3"] = "test4" + context.bot_data["test1"] = "test0" + context.bot.callback_data_cache._callback_queries["test1"] = "test0" def second(update, context): - if not context.user_data['test1'] == 'test2': + if not context.user_data["test1"] == "test2": pytest.fail() - if not context.chat_data['test3'] == 'test4': + if not context.chat_data["test3"] == "test4": pytest.fail() - if not context.bot_data['test1'] == 'test0': + if not context.bot_data["test1"] == "test0": pytest.fail() - if not context.bot.callback_data_cache.persistence_data == ([], {'test1': 'test0'}): + if not context.bot.callback_data_cache.persistence_data == ([], {"test1": "test0"}): pytest.fail() - h1 = MessageHandler(None, first, pass_user_data=True, pass_chat_data=True) - h2 = MessageHandler(None, second, pass_user_data=True, pass_chat_data=True) + h1 = MessageHandler(None, first) + h2 = MessageHandler(None, second) dp.add_handler(h1) dp.process_update(update) pickle_persistence_2 = PicklePersistence( - filename='pickletest', - store_user_data=True, - store_chat_data=True, - store_bot_data=True, - store_callback_data=True, + filename="pickletest", single_file=False, on_flush=False, ) @@ -1678,69 +1670,59 @@ def test_flush_on_stop(self, bot, update, pickle_persistence): u = Updater(bot=bot, persistence=pickle_persistence) dp = u.dispatcher u.running = True - dp.user_data[4242424242]['my_test'] = 'Working!' - dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - dp.bot_data['test'] = 'Working3!' - dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' + dp.user_data[4242424242]["my_test"] = "Working!" + dp.chat_data[-4242424242]["my_test2"] = "Working2!" + dp.bot_data["test"] = "Working3!" + dp.bot.callback_data_cache._callback_queries["test"] = "Working4!" u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( - filename='pickletest', - store_bot_data=True, - store_user_data=True, - store_chat_data=True, - store_callback_data=True, + filename="pickletest", single_file=False, on_flush=False, ) - assert pickle_persistence_2.get_user_data()[4242424242]['my_test'] == 'Working!' - assert pickle_persistence_2.get_chat_data()[-4242424242]['my_test2'] == 'Working2!' - assert pickle_persistence_2.get_bot_data()['test'] == 'Working3!' + assert pickle_persistence_2.get_user_data()[4242424242]["my_test"] == "Working!" + assert pickle_persistence_2.get_chat_data()[-4242424242]["my_test2"] == "Working2!" + assert pickle_persistence_2.get_bot_data()["test"] == "Working3!" data = pickle_persistence_2.get_callback_data()[1] - assert data['test'] == 'Working4!' + assert data["test"] == "Working4!" def test_flush_on_stop_only_bot(self, bot, update, pickle_persistence_only_bot): u = Updater(bot=bot, persistence=pickle_persistence_only_bot) dp = u.dispatcher u.running = True - dp.user_data[4242424242]['my_test'] = 'Working!' - dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - dp.bot_data['my_test3'] = 'Working3!' - dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' + dp.user_data[4242424242]["my_test"] = "Working!" + dp.chat_data[-4242424242]["my_test2"] = "Working2!" + dp.bot_data["my_test3"] = "Working3!" + dp.bot.callback_data_cache._callback_queries["test"] = "Working4!" u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( - filename='pickletest', - store_user_data=False, - store_chat_data=False, - store_bot_data=True, - store_callback_data=False, + filename="pickletest", + store_data=PersistenceInput(callback_data=False, chat_data=False, user_data=False), single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data() == {} assert pickle_persistence_2.get_chat_data() == {} - assert pickle_persistence_2.get_bot_data()['my_test3'] == 'Working3!' + assert pickle_persistence_2.get_bot_data()["my_test3"] == "Working3!" assert pickle_persistence_2.get_callback_data() is None def test_flush_on_stop_only_chat(self, bot, update, pickle_persistence_only_chat): u = Updater(bot=bot, persistence=pickle_persistence_only_chat) dp = u.dispatcher u.running = True - dp.user_data[4242424242]['my_test'] = 'Working!' - dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - dp.bot_data['my_test3'] = 'Working3!' - dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' + dp.user_data[4242424242]["my_test"] = "Working!" + dp.chat_data[-4242424242]["my_test2"] = "Working2!" + dp.bot_data["my_test3"] = "Working3!" + dp.bot.callback_data_cache._callback_queries["test"] = "Working4!" u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( - filename='pickletest', - store_user_data=False, - store_chat_data=True, - store_bot_data=False, - store_callback_data=False, + filename="pickletest", + store_data=PersistenceInput(callback_data=False, user_data=False, bot_data=False), single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data() == {} - assert pickle_persistence_2.get_chat_data()[-4242424242]['my_test2'] == 'Working2!' + assert pickle_persistence_2.get_chat_data()[-4242424242]["my_test2"] == "Working2!" assert pickle_persistence_2.get_bot_data() == {} assert pickle_persistence_2.get_callback_data() is None @@ -1748,21 +1730,18 @@ def test_flush_on_stop_only_user(self, bot, update, pickle_persistence_only_user u = Updater(bot=bot, persistence=pickle_persistence_only_user) dp = u.dispatcher u.running = True - dp.user_data[4242424242]['my_test'] = 'Working!' - dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - dp.bot_data['my_test3'] = 'Working3!' - dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' + dp.user_data[4242424242]["my_test"] = "Working!" + dp.chat_data[-4242424242]["my_test2"] = "Working2!" + dp.bot_data["my_test3"] = "Working3!" + dp.bot.callback_data_cache._callback_queries["test"] = "Working4!" u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( - filename='pickletest', - store_user_data=True, - store_chat_data=False, - store_bot_data=False, - store_callback_data=False, + filename="pickletest", + store_data=PersistenceInput(callback_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) - assert pickle_persistence_2.get_user_data()[4242424242]['my_test'] == 'Working!' + assert pickle_persistence_2.get_user_data()[4242424242]["my_test"] == "Working!" assert pickle_persistence_2.get_chat_data() == {} assert pickle_persistence_2.get_bot_data() == {} assert pickle_persistence_2.get_callback_data() is None @@ -1771,20 +1750,17 @@ def test_flush_on_stop_only_callback(self, bot, update, pickle_persistence_only_ u = Updater(bot=bot, persistence=pickle_persistence_only_callback) dp = u.dispatcher u.running = True - dp.user_data[4242424242]['my_test'] = 'Working!' - dp.chat_data[-4242424242]['my_test2'] = 'Working2!' - dp.bot_data['my_test3'] = 'Working3!' - dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' + dp.user_data[4242424242]["my_test"] = "Working!" + dp.chat_data[-4242424242]["my_test2"] = "Working2!" + dp.bot_data["my_test3"] = "Working3!" + dp.bot.callback_data_cache._callback_queries["test"] = "Working4!" u._signal_handler(signal.SIGINT, None) del dp del u del pickle_persistence_only_callback pickle_persistence_2 = PicklePersistence( - filename='pickletest', - store_user_data=False, - store_chat_data=False, - store_bot_data=False, - store_callback_data=True, + filename="pickletest", + store_data=PersistenceInput(user_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) @@ -1792,17 +1768,16 @@ def test_flush_on_stop_only_callback(self, bot, update, pickle_persistence_only_ assert pickle_persistence_2.get_chat_data() == {} assert pickle_persistence_2.get_bot_data() == {} data = pickle_persistence_2.get_callback_data()[1] - assert data['test'] == 'Working4!' + assert data["test"] == "Working4!" def test_with_conversation_handler(self, dp, update, good_pickle_files, pickle_persistence): dp.persistence = pickle_persistence - dp.use_context = True NEXT, NEXT2 = range(2) def start(update, context): return NEXT - start = CommandHandler('start', start) + start = CommandHandler("start", start) def next_callback(update, context): return NEXT2 @@ -1815,29 +1790,28 @@ def next2(update, context): next2 = MessageHandler(None, next2) ch = ConversationHandler( - [start], {NEXT: [next_handler], 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 dp.process_update(update) assert ch._get_key(update) not in ch.conversations - update.message.text = '/start' + update.message.text = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 0 - assert ch.conversations == pickle_persistence.conversations['name2'] + assert ch.conversations == pickle_persistence.conversations["name2"] def test_with_nested_conversationHandler( self, dp, update, good_pickle_files, pickle_persistence ): dp.persistence = pickle_persistence - dp.use_context = True NEXT2, NEXT3 = range(1, 3) def start(update, context): return NEXT2 - start = CommandHandler('start', start) + start = CommandHandler("start", start) def next_callback(update, context): return NEXT2 @@ -1853,13 +1827,13 @@ def next2(update, context): [next_handler], {NEXT2: [next2]}, [], - name='name3', + name="name3", persistent=True, map_to_parent={ConversationHandler.END: ConversationHandler.END}, ) ch = ConversationHandler( - [start], {NEXT2: [nested_ch], NEXT3: []}, [], name='name2', persistent=True + [start], {NEXT2: [nested_ch], NEXT3: []}, [], name="name2", persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 @@ -1867,48 +1841,48 @@ def next2(update, context): dp.process_update(update) assert ch._get_key(update) not in ch.conversations assert nested_ch._get_key(update) not in nested_ch.conversations - update.message.text = '/start' + update.message.text = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 - assert ch.conversations == pickle_persistence.conversations['name2'] + assert ch.conversations == pickle_persistence.conversations["name2"] assert nested_ch._get_key(update) not in nested_ch.conversations dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 - assert ch.conversations == pickle_persistence.conversations['name2'] + assert ch.conversations == pickle_persistence.conversations["name2"] assert nested_ch.conversations[nested_ch._get_key(update)] == 1 - assert nested_ch.conversations == pickle_persistence.conversations['name3'] + assert nested_ch.conversations == pickle_persistence.conversations["name3"] - def test_with_job(self, job_queue, cdp, pickle_persistence): - cdp.bot.arbitrary_callback_data = True + def test_with_job(self, job_queue, dp, pickle_persistence): + dp.bot.arbitrary_callback_data = True def job_callback(context): - context.bot_data['test1'] = '456' - context.dispatcher.chat_data[123]['test2'] = '789' - context.dispatcher.user_data[789]['test3'] = '123' - context.bot.callback_data_cache._callback_queries['test'] = 'Working4!' + context.bot_data["test1"] = "456" + context.dispatcher.chat_data[123]["test2"] = "789" + context.dispatcher.user_data[789]["test3"] = "123" + context.bot.callback_data_cache._callback_queries["test"] = "Working4!" - cdp.persistence = pickle_persistence - job_queue.set_dispatcher(cdp) + dp.persistence = pickle_persistence + job_queue.set_dispatcher(dp) job_queue.start() job_queue.run_once(job_callback, 0.01) sleep(0.5) bot_data = pickle_persistence.get_bot_data() - assert bot_data == {'test1': '456'} + assert bot_data == {"test1": "456"} chat_data = pickle_persistence.get_chat_data() - assert chat_data[123] == {'test2': '789'} + assert chat_data[123] == {"test2": "789"} user_data = pickle_persistence.get_user_data() - assert user_data[789] == {'test3': '123'} + assert user_data[789] == {"test3": "123"} data = pickle_persistence.get_callback_data()[1] - assert data['test'] == 'Working4!' + assert data["test"] == "Working4!" - @pytest.mark.parametrize('singlefile', [True, False]) - @pytest.mark.parametrize('ud', [int, float, complex]) - @pytest.mark.parametrize('cd', [int, float, complex]) - @pytest.mark.parametrize('bd', [int, float, complex]) + @pytest.mark.parametrize("singlefile", [True, False]) + @pytest.mark.parametrize("ud", [int, float, complex]) + @pytest.mark.parametrize("cd", [int, float, complex]) + @pytest.mark.parametrize("bd", [int, float, complex]) def test_with_context_types(self, ud, cd, bd, singlefile): cc = ContextTypes(user_data=ud, chat_data=cd, bot_data=bd) - persistence = PicklePersistence('pickletest', single_file=singlefile, context_types=cc) + persistence = PicklePersistence("pickletest", single_file=singlefile, context_types=cc) assert isinstance(persistence.get_user_data()[1], ud) assert persistence.get_user_data()[1] == 0 @@ -1927,7 +1901,7 @@ def test_with_context_types(self, ud, cd, bd, singlefile): assert persistence.get_bot_data() == 1 persistence.flush() - persistence = PicklePersistence('pickletest', single_file=singlefile, context_types=cc) + persistence = PicklePersistence("pickletest", single_file=singlefile, context_types=cc) assert isinstance(persistence.get_user_data()[1], ud) assert persistence.get_user_data()[1] == 1 assert isinstance(persistence.get_chat_data()[1], cd) @@ -1936,27 +1910,27 @@ def test_with_context_types(self, ud, cd, bd, singlefile): assert persistence.get_bot_data() == 1 -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def user_data_json(user_data): return json.dumps(user_data) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def chat_data_json(chat_data): return json.dumps(chat_data) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def bot_data_json(bot_data): return json.dumps(bot_data) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def callback_data_json(callback_data): return json.dumps(callback_data) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def conversations_json(conversations): return """{"name1": {"[123, 123]": 3, "[456, 654]": 4}, "name2": {"[123, 321]": 1, "[890, 890]": 2}, "name3": @@ -1967,11 +1941,8 @@ class TestDictPersistence: def test_slot_behaviour(self, mro_slots, recwarn): inst = DictPersistence() for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - # assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.store_user_data = 'should give warning', {} - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_no_json_given(self): dict_persistence = DictPersistence() @@ -1979,23 +1950,23 @@ def test_no_json_given(self): assert dict_persistence.get_chat_data() == defaultdict(dict) assert dict_persistence.get_bot_data() == {} assert dict_persistence.get_callback_data() is None - assert dict_persistence.get_conversations('noname') == {} + assert dict_persistence.get_conversations("noname") == {} def test_bad_json_string_given(self): - bad_user_data = 'thisisnojson99900()))(' - bad_chat_data = 'thisisnojson99900()))(' - bad_bot_data = 'thisisnojson99900()))(' - bad_callback_data = 'thisisnojson99900()))(' - bad_conversations = 'thisisnojson99900()))(' - with pytest.raises(TypeError, match='user_data'): + bad_user_data = "thisisnojson99900()))(" + bad_chat_data = "thisisnojson99900()))(" + bad_bot_data = "thisisnojson99900()))(" + bad_callback_data = "thisisnojson99900()))(" + bad_conversations = "thisisnojson99900()))(" + with pytest.raises(TypeError, match="user_data"): DictPersistence(user_data_json=bad_user_data) - with pytest.raises(TypeError, match='chat_data'): + with pytest.raises(TypeError, match="chat_data"): DictPersistence(chat_data_json=bad_chat_data) - with pytest.raises(TypeError, match='bot_data'): + with pytest.raises(TypeError, match="bot_data"): DictPersistence(bot_data_json=bad_bot_data) - with pytest.raises(TypeError, match='callback_data'): + with pytest.raises(TypeError, match="callback_data"): DictPersistence(callback_data_json=bad_callback_data) - with pytest.raises(TypeError, match='conversations'): + with pytest.raises(TypeError, match="conversations"): DictPersistence(conversations_json=bad_conversations) def test_invalid_json_string_given(self, pickle_persistence, bad_pickle_files): @@ -2008,11 +1979,11 @@ def test_invalid_json_string_given(self, pickle_persistence, bad_pickle_files): bad_callback_data_3 = '[[[{"not": "a str"}, 3.14, {"di": "ct"}]], {"di": "ct"}]' bad_callback_data_4 = '[[["wrong", "length"]], {"di": "ct"}]' bad_callback_data_5 = '["this", "is", "json"]' - with pytest.raises(TypeError, match='user_data'): + with pytest.raises(TypeError, match="user_data"): DictPersistence(user_data_json=bad_user_data) - with pytest.raises(TypeError, match='chat_data'): + with pytest.raises(TypeError, match="chat_data"): DictPersistence(chat_data_json=bad_chat_data) - with pytest.raises(TypeError, match='bot_data'): + with pytest.raises(TypeError, match="bot_data"): DictPersistence(bot_data_json=bad_bot_data) for bad_callback_data in [ bad_callback_data_1, @@ -2021,9 +1992,9 @@ def test_invalid_json_string_given(self, pickle_persistence, bad_pickle_files): bad_callback_data_4, bad_callback_data_5, ]: - with pytest.raises(TypeError, match='callback_data'): + with pytest.raises(TypeError, match="callback_data"): DictPersistence(callback_data_json=bad_callback_data) - with pytest.raises(TypeError, match='conversations'): + with pytest.raises(TypeError, match="conversations"): DictPersistence(conversations_json=bad_conversations) def test_good_json_input( @@ -2038,35 +2009,35 @@ def test_good_json_input( ) user_data = dict_persistence.get_user_data() assert isinstance(user_data, defaultdict) - assert user_data[12345]['test1'] == 'test2' - assert user_data[67890][3] == 'test4' + assert user_data[12345]["test1"] == "test2" + assert user_data[67890][3] == "test4" assert user_data[54321] == {} chat_data = dict_persistence.get_chat_data() assert isinstance(chat_data, defaultdict) - assert chat_data[-12345]['test1'] == 'test2' - assert chat_data[-67890][3] == 'test4' + assert chat_data[-12345]["test1"] == "test2" + assert chat_data[-67890][3] == "test4" assert chat_data[-54321] == {} bot_data = dict_persistence.get_bot_data() assert isinstance(bot_data, dict) - assert bot_data['test1'] == 'test2' - assert bot_data['test3']['test4'] == 'test5' - assert 'test6' not in bot_data + assert bot_data["test1"] == "test2" + assert bot_data["test3"]["test4"] == "test5" + assert "test6" not in bot_data callback_data = dict_persistence.get_callback_data() assert isinstance(callback_data, tuple) - assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] - assert callback_data[1] == {'test1': 'test2'} + assert callback_data[0] == [("test1", 1000, {"button1": "test0", "button2": "test1"})] + assert callback_data[1] == {"test1": "test2"} - conversation1 = dict_persistence.get_conversations('name1') + conversation1 = dict_persistence.get_conversations("name1") assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] - conversation2 = dict_persistence.get_conversations('name2') + conversation2 = dict_persistence.get_conversations("name2") assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 @@ -2074,9 +2045,9 @@ def test_good_json_input( conversation2[(123, 123)] def test_good_json_input_callback_data_none(self): - dict_persistence = DictPersistence(callback_data_json='null') + dict_persistence = DictPersistence(callback_data_json="null") assert dict_persistence.callback_data is None - assert dict_persistence.callback_data_json == 'null' + assert dict_persistence.callback_data_json == "null" def test_dict_outputs( self, @@ -2134,15 +2105,14 @@ def test_updating( bot_data_json=bot_data_json, callback_data_json=callback_data_json, conversations_json=conversations_json, - store_callback_data=True, ) user_data = dict_persistence.get_user_data() - user_data[12345]['test3']['test4'] = 'test6' + user_data[12345]["test3"]["test4"] = "test6" assert not dict_persistence.user_data == user_data assert not dict_persistence.user_data_json == json.dumps(user_data) dict_persistence.update_user_data(12345, user_data[12345]) - user_data[12345]['test3']['test4'] = 'test7' + user_data[12345]["test3"]["test4"] = "test7" assert not dict_persistence.user_data == user_data assert not dict_persistence.user_data_json == json.dumps(user_data) dict_persistence.update_user_data(12345, user_data[12345]) @@ -2150,11 +2120,11 @@ def test_updating( assert dict_persistence.user_data_json == json.dumps(user_data) chat_data = dict_persistence.get_chat_data() - chat_data[-12345]['test3']['test4'] = 'test6' + chat_data[-12345]["test3"]["test4"] = "test6" assert not dict_persistence.chat_data == chat_data assert not dict_persistence.chat_data_json == json.dumps(chat_data) dict_persistence.update_chat_data(-12345, chat_data[-12345]) - chat_data[-12345]['test3']['test4'] = 'test7' + chat_data[-12345]["test3"]["test4"] = "test7" assert not dict_persistence.chat_data == chat_data assert not dict_persistence.chat_data_json == json.dumps(chat_data) dict_persistence.update_chat_data(-12345, chat_data[-12345]) @@ -2162,11 +2132,11 @@ def test_updating( assert dict_persistence.chat_data_json == json.dumps(chat_data) bot_data = dict_persistence.get_bot_data() - bot_data['test3']['test4'] = 'test6' + bot_data["test3"]["test4"] = "test6" assert not dict_persistence.bot_data == bot_data assert not dict_persistence.bot_data_json == json.dumps(bot_data) dict_persistence.update_bot_data(bot_data) - bot_data['test3']['test4'] = 'test7' + bot_data["test3"]["test4"] = "test7" assert not dict_persistence.bot_data == bot_data assert not dict_persistence.bot_data_json == json.dumps(bot_data) dict_persistence.update_bot_data(bot_data) @@ -2174,39 +2144,39 @@ def test_updating( assert dict_persistence.bot_data_json == json.dumps(bot_data) callback_data = dict_persistence.get_callback_data() - callback_data[1]['test3'] = 'test4' - callback_data[0][0][2]['button2'] = 'test41' + callback_data[1]["test3"] = "test4" + callback_data[0][0][2]["button2"] = "test41" assert not dict_persistence.callback_data == callback_data assert not dict_persistence.callback_data_json == json.dumps(callback_data) dict_persistence.update_callback_data(callback_data) - callback_data[1]['test3'] = 'test5' - callback_data[0][0][2]['button2'] = 'test42' + callback_data[1]["test3"] = "test5" + callback_data[0][0][2]["button2"] = "test42" assert not dict_persistence.callback_data == callback_data assert not dict_persistence.callback_data_json == json.dumps(callback_data) dict_persistence.update_callback_data(callback_data) assert dict_persistence.callback_data == callback_data assert dict_persistence.callback_data_json == json.dumps(callback_data) - conversation1 = dict_persistence.get_conversations('name1') + conversation1 = dict_persistence.get_conversations("name1") conversation1[(123, 123)] = 5 - assert not dict_persistence.conversations['name1'] == conversation1 - dict_persistence.update_conversation('name1', (123, 123), 5) - assert dict_persistence.conversations['name1'] == conversation1 - conversations['name1'][(123, 123)] = 5 + assert not dict_persistence.conversations["name1"] == conversation1 + dict_persistence.update_conversation("name1", (123, 123), 5) + assert dict_persistence.conversations["name1"] == conversation1 + conversations["name1"][(123, 123)] = 5 assert dict_persistence.conversations_json == encode_conversations_to_json(conversations) - assert dict_persistence.get_conversations('name1') == conversation1 + assert dict_persistence.get_conversations("name1") == conversation1 dict_persistence._conversations = None - dict_persistence.update_conversation('name1', (123, 123), 5) - assert dict_persistence.conversations['name1'] == {(123, 123): 5} - assert dict_persistence.get_conversations('name1') == {(123, 123): 5} + dict_persistence.update_conversation("name1", (123, 123), 5) + assert dict_persistence.conversations["name1"] == {(123, 123): 5} + assert dict_persistence.get_conversations("name1") == {(123, 123): 5} assert dict_persistence.conversations_json == encode_conversations_to_json( {"name1": {(123, 123): 5}} ) def test_with_handler(self, bot, update): - dict_persistence = DictPersistence(store_callback_data=True) - u = Updater(bot=bot, persistence=dict_persistence, use_context=True) + dict_persistence = DictPersistence() + u = Updater(bot=bot, persistence=dict_persistence) dp = u.dispatcher def first(update, context): @@ -2218,19 +2188,19 @@ def first(update, context): pytest.fail() if not context.bot.callback_data_cache.persistence_data == ([], {}): pytest.fail() - context.user_data['test1'] = 'test2' - context.chat_data[3] = 'test4' - context.bot_data['test1'] = 'test0' - context.bot.callback_data_cache._callback_queries['test1'] = 'test0' + context.user_data["test1"] = "test2" + context.chat_data[3] = "test4" + context.bot_data["test1"] = "test0" + context.bot.callback_data_cache._callback_queries["test1"] = "test0" def second(update, context): - if not context.user_data['test1'] == 'test2': + if not context.user_data["test1"] == "test2": pytest.fail() - if not context.chat_data[3] == 'test4': + if not context.chat_data[3] == "test4": pytest.fail() - if not context.bot_data['test1'] == 'test0': + if not context.bot_data["test1"] == "test0": pytest.fail() - if not context.bot.callback_data_cache.persistence_data == ([], {'test1': 'test0'}): + if not context.bot.callback_data_cache.persistence_data == ([], {"test1": "test0"}): pytest.fail() h1 = MessageHandler(Filters.all, first) @@ -2246,7 +2216,6 @@ def second(update, context): chat_data_json=chat_data, bot_data_json=bot_data, callback_data_json=callback_data, - store_callback_data=True, ) u = Updater(bot=bot, persistence=dict_persistence_2) @@ -2257,13 +2226,12 @@ def second(update, context): def test_with_conversationHandler(self, dp, update, conversations_json): dict_persistence = DictPersistence(conversations_json=conversations_json) dp.persistence = dict_persistence - dp.use_context = True NEXT, NEXT2 = range(2) def start(update, context): return NEXT - start = CommandHandler('start', start) + start = CommandHandler("start", start) def next_callback(update, context): return NEXT2 @@ -2276,28 +2244,27 @@ def next2(update, context): next2 = MessageHandler(None, next2) ch = ConversationHandler( - [start], {NEXT: [next_handler], 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 dp.process_update(update) assert ch._get_key(update) not in ch.conversations - update.message.text = '/start' + update.message.text = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 0 - assert ch.conversations == dict_persistence.conversations['name2'] + assert ch.conversations == dict_persistence.conversations["name2"] def test_with_nested_conversationHandler(self, dp, update, conversations_json): dict_persistence = DictPersistence(conversations_json=conversations_json) dp.persistence = dict_persistence - dp.use_context = True NEXT2, NEXT3 = range(1, 3) def start(update, context): return NEXT2 - start = CommandHandler('start', start) + start = CommandHandler("start", start) def next_callback(update, context): return NEXT2 @@ -2313,13 +2280,13 @@ def next2(update, context): [next_handler], {NEXT2: [next2]}, [], - name='name3', + name="name3", persistent=True, map_to_parent={ConversationHandler.END: ConversationHandler.END}, ) ch = ConversationHandler( - [start], {NEXT2: [nested_ch], NEXT3: []}, [], name='name2', persistent=True + [start], {NEXT2: [nested_ch], NEXT3: []}, [], name="name2", persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 @@ -2327,38 +2294,38 @@ def next2(update, context): dp.process_update(update) assert ch._get_key(update) not in ch.conversations assert nested_ch._get_key(update) not in nested_ch.conversations - update.message.text = '/start' + update.message.text = "/start" update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 - assert ch.conversations == dict_persistence.conversations['name2'] + assert ch.conversations == dict_persistence.conversations["name2"] assert nested_ch._get_key(update) not in nested_ch.conversations dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 - assert ch.conversations == dict_persistence.conversations['name2'] + assert ch.conversations == dict_persistence.conversations["name2"] assert nested_ch.conversations[nested_ch._get_key(update)] == 1 - assert nested_ch.conversations == dict_persistence.conversations['name3'] + assert nested_ch.conversations == dict_persistence.conversations["name3"] - def test_with_job(self, job_queue, cdp): - cdp.bot.arbitrary_callback_data = True + def test_with_job(self, job_queue, dp): + dp.bot.arbitrary_callback_data = True def job_callback(context): - context.bot_data['test1'] = '456' - context.dispatcher.chat_data[123]['test2'] = '789' - context.dispatcher.user_data[789]['test3'] = '123' - context.bot.callback_data_cache._callback_queries['test'] = 'Working4!' - - dict_persistence = DictPersistence(store_callback_data=True) - cdp.persistence = dict_persistence - job_queue.set_dispatcher(cdp) + context.bot_data["test1"] = "456" + context.dispatcher.chat_data[123]["test2"] = "789" + context.dispatcher.user_data[789]["test3"] = "123" + context.bot.callback_data_cache._callback_queries["test"] = "Working4!" + + dict_persistence = DictPersistence() + dp.persistence = dict_persistence + job_queue.set_dispatcher(dp) job_queue.start() job_queue.run_once(job_callback, 0.01) sleep(0.8) bot_data = dict_persistence.get_bot_data() - assert bot_data == {'test1': '456'} + assert bot_data == {"test1": "456"} chat_data = dict_persistence.get_chat_data() - assert chat_data[123] == {'test2': '789'} + assert chat_data[123] == {"test2": "789"} user_data = dict_persistence.get_user_data() - assert user_data[789] == {'test3': '123'} + assert user_data[789] == {"test3": "123"} data = dict_persistence.get_callback_data()[1] - assert data['test'] == 'Working4!' + assert data["test"] == "Working4!" diff --git a/tests/test_photo.py b/tests/test_photo.py index d6096056df5..2f162bca473 100644 --- a/tests/test_photo.py +++ b/tests/test_photo.py @@ -33,28 +33,28 @@ ) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def photo_file(): - f = open('tests/data/telegram.jpg', 'rb') + f = open("tests/data/telegram.jpg", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def _photo(bot, chat_id): def func(): - with open('tests/data/telegram.jpg', 'rb') as f: + with open("tests/data/telegram.jpg", "rb") as f: return bot.send_photo(chat_id, photo=f, timeout=50).photo - return expect_bad_request(func, 'Type of file mismatch', 'Telegram did not accept the file.') + return expect_bad_request(func, "Type of file mismatch", "Telegram did not accept the file.") -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def thumb(_photo): return _photo[0] -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def photo(_photo): return _photo[1] @@ -62,31 +62,28 @@ def photo(_photo): class TestPhoto: width = 320 height = 320 - caption = 'PhotoTest - *Caption*' - photo_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.jpg' + caption = "PhotoTest - *Caption*" + photo_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg" file_size = 29176 - def test_slot_behaviour(self, photo, recwarn, mro_slots): + def test_slot_behaviour(self, photo, mro_slots): for attr in photo.__slots__: - assert getattr(photo, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not photo.__dict__, f"got missing slot(s): {photo.__dict__}" + assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(photo)) == len(set(mro_slots(photo))), "duplicate slot" - photo.custom, photo.width = 'should give warning', self.width - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, thumb, photo): # Make sure file has been uploaded. assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != '' - assert photo.file_unique_id != '' + assert photo.file_id != "" + assert photo.file_unique_id != "" assert isinstance(thumb, PhotoSize) assert isinstance(thumb.file_id, str) assert isinstance(thumb.file_unique_id, str) - assert thumb.file_id != '' - assert thumb.file_unique_id != '' + assert thumb.file_id != "" + assert thumb.file_unique_id != "" def test_expected_values(self, photo, thumb): # We used to test for file_size as well, but TG apparently at some point apparently changed @@ -103,14 +100,14 @@ def test_send_photo_all_args(self, bot, chat_id, photo_file, thumb, photo): photo_file, caption=self.caption, disable_notification=False, - parse_mode='Markdown', + parse_mode="Markdown", ) assert isinstance(message.photo[0], PhotoSize) assert isinstance(message.photo[0].file_id, str) assert isinstance(message.photo[0].file_unique_id, str) - assert message.photo[0].file_id != '' - assert message.photo[0].file_unique_id != '' + assert message.photo[0].file_id != "" + assert message.photo[0].file_unique_id != "" assert message.photo[0].width == thumb.width assert message.photo[0].height == thumb.height assert message.photo[0].file_size == thumb.file_size @@ -118,31 +115,31 @@ def test_send_photo_all_args(self, bot, chat_id, photo_file, thumb, photo): assert isinstance(message.photo[1], PhotoSize) assert isinstance(message.photo[1].file_id, str) assert isinstance(message.photo[1].file_unique_id, str) - assert message.photo[1].file_id != '' - assert message.photo[1].file_unique_id != '' + assert message.photo[1].file_id != "" + assert message.photo[1].file_unique_id != "" assert message.photo[1].width == photo.width assert message.photo[1].height == photo.height assert message.photo[1].file_size == photo.file_size - assert message.caption == TestPhoto.caption.replace('*', '') + assert message.caption == TestPhoto.caption.replace("*", "") @flaky(3, 1) def test_send_photo_custom_filename(self, bot, chat_id, photo_file, monkeypatch): def make_assertion(url, data, **kwargs): - return data['photo'].filename == 'custom_filename' + return data["photo"].filename == "custom_filename" - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) - assert bot.send_photo(chat_id, photo_file, filename='custom_filename') + assert bot.send_photo(chat_id, photo_file, filename="custom_filename") @flaky(3, 1) def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file, thumb, photo): - message = bot.send_photo(chat_id, photo_file, caption=self.caption, parse_mode='Markdown') + message = bot.send_photo(chat_id, photo_file, caption=self.caption, parse_mode="Markdown") assert isinstance(message.photo[0], PhotoSize) assert isinstance(message.photo[0].file_id, str) assert isinstance(message.photo[0].file_unique_id, str) - assert message.photo[0].file_id != '' - assert message.photo[0].file_unique_id != '' + assert message.photo[0].file_id != "" + assert message.photo[0].file_unique_id != "" assert message.photo[0].width == thumb.width assert message.photo[0].height == thumb.height assert message.photo[0].file_size == thumb.file_size @@ -150,23 +147,23 @@ def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file, thumb, p assert isinstance(message.photo[1], PhotoSize) assert isinstance(message.photo[1].file_id, str) assert isinstance(message.photo[1].file_unique_id, str) - assert message.photo[1].file_id != '' - assert message.photo[1].file_unique_id != '' + assert message.photo[1].file_id != "" + assert message.photo[1].file_unique_id != "" assert message.photo[1].width == photo.width assert message.photo[1].height == photo.height assert message.photo[1].file_size == photo.file_size - assert message.caption == TestPhoto.caption.replace('*', '') + assert message.caption == TestPhoto.caption.replace("*", "") assert len(message.caption_entities) == 1 @flaky(3, 1) def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file, thumb, photo): - message = bot.send_photo(chat_id, photo_file, caption=self.caption, parse_mode='HTML') + message = bot.send_photo(chat_id, photo_file, caption=self.caption, parse_mode="HTML") assert isinstance(message.photo[0], PhotoSize) assert isinstance(message.photo[0].file_id, str) assert isinstance(message.photo[0].file_unique_id, str) - assert message.photo[0].file_id != '' - assert message.photo[0].file_unique_id != '' + assert message.photo[0].file_id != "" + assert message.photo[0].file_unique_id != "" assert message.photo[0].width == thumb.width assert message.photo[0].height == thumb.height assert message.photo[0].file_size == thumb.file_size @@ -174,18 +171,18 @@ def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file, thumb, photo assert isinstance(message.photo[1], PhotoSize) assert isinstance(message.photo[1].file_id, str) assert isinstance(message.photo[1].file_unique_id, str) - assert message.photo[1].file_id != '' - assert message.photo[1].file_unique_id != '' + assert message.photo[1].file_id != "" + assert message.photo[1].file_unique_id != "" assert message.photo[1].width == photo.width assert message.photo[1].height == photo.height assert message.photo[1].file_size == photo.file_size - assert message.caption == TestPhoto.caption.replace('', '').replace('', '') + assert message.caption == TestPhoto.caption.replace("", "").replace("", "") assert len(message.caption_entities) == 1 @flaky(3, 1) def test_send_photo_caption_entities(self, bot, chat_id, photo_file, thumb, photo): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -199,19 +196,19 @@ def test_send_photo_caption_entities(self, bot, chat_id, photo_file, thumb, phot assert message.caption_entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_photo_default_parse_mode_1(self, default_bot, chat_id, photo_file, thumb, photo): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_photo(chat_id, photo_file, caption=test_markdown_string) assert message.caption_markdown == test_markdown_string assert message.caption == test_string @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_photo_default_parse_mode_2(self, default_bot, chat_id, photo_file, thumb, photo): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_photo( chat_id, photo_file, caption=test_markdown_string, parse_mode=None @@ -220,12 +217,12 @@ def test_send_photo_default_parse_mode_2(self, default_bot, chat_id, photo_file, assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_photo_default_parse_mode_3(self, default_bot, chat_id, photo_file, thumb, photo): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_photo( - chat_id, photo_file, caption=test_markdown_string, parse_mode='HTML' + chat_id, photo_file, caption=test_markdown_string, parse_mode="HTML" ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @@ -233,32 +230,32 @@ def test_send_photo_default_parse_mode_3(self, default_bot, chat_id, photo_file, def test_send_photo_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('photo') == expected + test_flag = data.get("photo") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_photo(chat_id, file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_photo_default_allow_sending_without_reply( self, default_bot, chat_id, photo_file, thumb, photo, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_photo( @@ -274,7 +271,7 @@ def test_send_photo_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_photo( chat_id, photo_file, reply_to_message_id=reply_to_message.message_id ) @@ -285,11 +282,11 @@ def test_get_and_download(self, bot, photo): assert new_file.file_size == photo.file_size assert new_file.file_unique_id == photo.file_unique_id - assert new_file.file_path.startswith('https://') is True + assert new_file.file_path.startswith("https://") is True - new_file.download('telegram.jpg') + new_file.download("telegram.jpg") - assert os.path.isfile('telegram.jpg') is True + assert os.path.isfile("telegram.jpg") is True @flaky(3, 1) def test_send_url_jpg_file(self, bot, chat_id, thumb, photo): @@ -298,23 +295,23 @@ def test_send_url_jpg_file(self, bot, chat_id, thumb, photo): assert isinstance(message.photo[0], PhotoSize) assert isinstance(message.photo[0].file_id, str) assert isinstance(message.photo[0].file_unique_id, str) - assert message.photo[0].file_id != '' - assert message.photo[0].file_unique_id != '' + assert message.photo[0].file_id != "" + assert message.photo[0].file_unique_id != "" # We used to test for width, height and file_size, but TG apparently started to treat # sending by URL and sending by upload differently and it's not really our job anyway ... assert isinstance(message.photo[1], PhotoSize) assert isinstance(message.photo[1].file_id, str) assert isinstance(message.photo[1].file_unique_id, str) - assert message.photo[1].file_id != '' - assert message.photo[1].file_unique_id != '' + assert message.photo[1].file_id != "" + assert message.photo[1].file_unique_id != "" # We used to test for width, height and file_size, but TG apparently started to treat # sending by URL and sending by upload differently and it's not really our job anyway ... @flaky(3, 1) def test_send_url_png_file(self, bot, chat_id): message = bot.send_photo( - photo='http://dummyimage.com/600x400/000/fff.png&text=telegram', chat_id=chat_id + photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id ) photo = message.photo[-1] @@ -322,13 +319,13 @@ def test_send_url_png_file(self, bot, chat_id): assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != '' - assert photo.file_unique_id != '' + assert photo.file_id != "" + assert photo.file_unique_id != "" @flaky(3, 1) def test_send_url_gif_file(self, bot, chat_id): message = bot.send_photo( - photo='http://dummyimage.com/600x400/000/fff.png&text=telegram', chat_id=chat_id + photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id ) photo = message.photo[-1] @@ -336,15 +333,15 @@ def test_send_url_gif_file(self, bot, chat_id): assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != '' - assert photo.file_unique_id != '' + assert photo.file_id != "" + assert photo.file_unique_id != "" @flaky(3, 1) def test_send_file_unicode_filename(self, bot, chat_id): """ Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/1202 """ - with open('tests/data/ζ΅‹θ―•.png', 'rb') as f: + with open("tests/data/ζ΅‹θ―•.png", "rb") as f: message = bot.send_photo(photo=f, chat_id=chat_id) photo = message.photo[-1] @@ -352,32 +349,32 @@ def test_send_file_unicode_filename(self, bot, chat_id): assert isinstance(photo, PhotoSize) assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != '' - assert photo.file_unique_id != '' + assert photo.file_id != "" + assert photo.file_unique_id != "" @flaky(3, 1) def test_send_bytesio_jpg_file(self, bot, chat_id): - file_name = 'tests/data/telegram_no_standard_header.jpg' + file_name = "tests/data/telegram_no_standard_header.jpg" # raw image bytes - raw_bytes = BytesIO(open(file_name, 'rb').read()) + raw_bytes = BytesIO(open(file_name, "rb").read()) input_file = InputFile(raw_bytes) - assert input_file.mimetype == 'application/octet-stream' + assert input_file.mimetype == "application/octet-stream" # raw image bytes with name info - raw_bytes = BytesIO(open(file_name, 'rb').read()) + raw_bytes = BytesIO(open(file_name, "rb").read()) raw_bytes.name = file_name input_file = InputFile(raw_bytes) - assert input_file.mimetype == 'image/jpeg' + assert input_file.mimetype == "image/jpeg" # send raw photo - raw_bytes = BytesIO(open(file_name, 'rb').read()) + raw_bytes = BytesIO(open(file_name, "rb").read()) message = bot.send_photo(chat_id, photo=raw_bytes) photo = message.photo[-1] assert isinstance(photo.file_id, str) assert isinstance(photo.file_unique_id, str) - assert photo.file_id != '' - assert photo.file_unique_id != '' + assert photo.file_id != "" + assert photo.file_unique_id != "" assert isinstance(photo, PhotoSize) assert photo.width == 1280 assert photo.height == 720 @@ -385,9 +382,9 @@ def test_send_bytesio_jpg_file(self, bot, chat_id): def test_send_with_photosize(self, monkeypatch, bot, chat_id, photo): def test(url, data, **kwargs): - return data['photo'] == photo.file_id + return data["photo"] == photo.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_photo(photo=photo, chat_id=chat_id) assert message @@ -400,8 +397,8 @@ def test_resend(self, bot, chat_id, photo): assert isinstance(message.photo[0], PhotoSize) assert isinstance(message.photo[0].file_id, str) assert isinstance(message.photo[0].file_unique_id, str) - assert message.photo[0].file_id != '' - assert message.photo[0].file_unique_id != '' + assert message.photo[0].file_id != "" + assert message.photo[0].file_unique_id != "" assert message.photo[0].width == thumb.width assert message.photo[0].height == thumb.height assert message.photo[0].file_size == thumb.file_size @@ -409,19 +406,19 @@ def test_resend(self, bot, chat_id, photo): assert isinstance(message.photo[1], PhotoSize) assert isinstance(message.photo[1].file_id, str) assert isinstance(message.photo[1].file_unique_id, str) - assert message.photo[1].file_id != '' - assert message.photo[1].file_unique_id != '' + assert message.photo[1].file_id != "" + assert message.photo[1].file_unique_id != "" assert message.photo[1].width == photo.width assert message.photo[1].height == photo.height assert message.photo[1].file_size == photo.file_size def test_de_json(self, bot, photo): json_dict = { - 'file_id': photo.file_id, - 'file_unique_id': photo.file_unique_id, - 'width': self.width, - 'height': self.height, - 'file_size': self.file_size, + "file_id": photo.file_id, + "file_unique_id": photo.file_unique_id, + "width": self.width, + "height": self.height, + "file_size": self.file_size, } json_photo = PhotoSize.de_json(json_dict, bot) @@ -435,21 +432,21 @@ def test_to_dict(self, photo): photo_dict = photo.to_dict() assert isinstance(photo_dict, dict) - assert photo_dict['file_id'] == photo.file_id - assert photo_dict['file_unique_id'] == photo.file_unique_id - assert photo_dict['width'] == photo.width - assert photo_dict['height'] == photo.height - assert photo_dict['file_size'] == photo.file_size + assert photo_dict["file_id"] == photo.file_id + assert photo_dict["file_unique_id"] == photo.file_unique_id + assert photo_dict["width"] == photo.width + assert photo_dict["height"] == photo.height + assert photo_dict["file_size"] == photo.file_size @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_photo(chat_id=chat_id, photo=open(os.devnull, 'rb')) + bot.send_photo(chat_id=chat_id, photo=open(os.devnull, "rb")) @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_photo(chat_id=chat_id, photo='') + bot.send_photo(chat_id=chat_id, photo="") def test_error_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -457,20 +454,20 @@ def test_error_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, photo): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == photo.file_id + return kwargs["file_id"] == photo.file_id - assert check_shortcut_signature(PhotoSize.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(photo.get_file, photo.bot, 'get_file') + assert check_shortcut_signature(PhotoSize.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(photo.get_file, photo.bot, "get_file") assert check_defaults_handling(photo.get_file, photo.bot) - monkeypatch.setattr(photo.bot, 'get_file', make_assertion) + monkeypatch.setattr(photo.bot, "get_file", make_assertion) assert photo.get_file() def test_equality(self, photo): a = PhotoSize(photo.file_id, photo.file_unique_id, self.width, self.height) - b = PhotoSize('', photo.file_unique_id, self.width, self.height) + b = PhotoSize("", photo.file_unique_id, self.width, self.height) c = PhotoSize(photo.file_id, photo.file_unique_id, 0, 0) - d = PhotoSize('', '', self.width, self.height) + d = PhotoSize("", "", self.width, self.height) e = Sticker(photo.file_id, photo.file_unique_id, self.width, self.height, False) assert a == b diff --git a/tests/test_poll.py b/tests/test_poll.py index cd93f907ca1..df3b5ceec00 100644 --- a/tests/test_poll.py +++ b/tests/test_poll.py @@ -33,16 +33,13 @@ class TestPollOption: text = "test option" voter_count = 3 - def test_slot_behaviour(self, poll_option, mro_slots, recwarn): + def test_slot_behaviour(self, poll_option, mro_slots): for attr in poll_option.__slots__: - assert getattr(poll_option, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not poll_option.__dict__, f"got missing slot(s): {poll_option.__dict__}" + assert getattr(poll_option, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(poll_option)) == len(set(mro_slots(poll_option))), "duplicate slot" - poll_option.custom, poll_option.text = 'should give warning', self.text - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self): - json_dict = {'text': self.text, 'voter_count': self.voter_count} + json_dict = {"text": self.text, "voter_count": self.voter_count} poll_option = PollOption.de_json(json_dict, None) assert poll_option.text == self.text @@ -52,15 +49,15 @@ def test_to_dict(self, poll_option): poll_option_dict = poll_option.to_dict() assert isinstance(poll_option_dict, dict) - assert poll_option_dict['text'] == poll_option.text - assert poll_option_dict['voter_count'] == poll_option.voter_count + assert poll_option_dict["text"] == poll_option.text + assert poll_option_dict["voter_count"] == poll_option.voter_count def test_equality(self): - a = PollOption('text', 1) - b = PollOption('text', 1) - c = PollOption('text_1', 1) - d = PollOption('text', 2) - e = Poll(123, 'question', ['O1', 'O2'], 1, False, True, Poll.REGULAR, True) + a = PollOption("text", 1) + b = PollOption("text", 1) + c = PollOption("text_1", 1) + d = PollOption("text", 2) + e = Poll(123, "question", ["O1", "O2"], 1, False, True, Poll.REGULAR, True) assert a == b assert hash(a) == hash(b) @@ -83,15 +80,15 @@ def poll_answer(): class TestPollAnswer: - poll_id = 'id' - user = User(1, '', False) + poll_id = "id" + user = User(1, "", False) option_ids = [2] def test_de_json(self): json_dict = { - 'poll_id': self.poll_id, - 'user': self.user.to_dict(), - 'option_ids': self.option_ids, + "poll_id": self.poll_id, + "user": self.user.to_dict(), + "option_ids": self.option_ids, } poll_answer = PollAnswer.de_json(json_dict, None) @@ -103,16 +100,16 @@ def test_to_dict(self, poll_answer): poll_answer_dict = poll_answer.to_dict() assert isinstance(poll_answer_dict, dict) - assert poll_answer_dict['poll_id'] == poll_answer.poll_id - assert poll_answer_dict['user'] == poll_answer.user.to_dict() - assert poll_answer_dict['option_ids'] == poll_answer.option_ids + assert poll_answer_dict["poll_id"] == poll_answer.poll_id + assert poll_answer_dict["user"] == poll_answer.user.to_dict() + assert poll_answer_dict["option_ids"] == poll_answer.option_ids def test_equality(self): a = PollAnswer(123, self.user, [2]) - b = PollAnswer(123, User(1, 'first', False), [2]) + b = PollAnswer(123, User(1, "first", False), [2]) c = PollAnswer(123, self.user, [1, 2]) d = PollAnswer(456, self.user, [2]) - e = PollOption('Text', 1) + e = PollOption("Text", 1) assert a == b assert hash(a) == hash(b) @@ -127,7 +124,7 @@ def test_equality(self): assert hash(a) != hash(e) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def poll(): return Poll( TestPoll.id_, @@ -146,36 +143,36 @@ def poll(): class TestPoll: - id_ = 'id' - question = 'Test?' - options = [PollOption('test', 10), PollOption('test2', 11)] + id_ = "id" + question = "Test?" + options = [PollOption("test", 10), PollOption("test2", 11)] total_voter_count = 0 is_closed = True is_anonymous = False type = Poll.REGULAR allows_multiple_answers = True explanation = ( - b'\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467' - b'\\u200d\\U0001f467\\U0001f431http://google.com' - ).decode('unicode-escape') + b"\\U0001f469\\u200d\\U0001f469\\u200d\\U0001f467" + b"\\u200d\\U0001f467\\U0001f431http://google.com" + ).decode("unicode-escape") explanation_entities = [MessageEntity(13, 17, MessageEntity.URL)] open_period = 42 close_date = datetime.utcnow() def test_de_json(self, bot): json_dict = { - 'id': self.id_, - 'question': self.question, - 'options': [o.to_dict() for o in self.options], - 'total_voter_count': self.total_voter_count, - 'is_closed': self.is_closed, - 'is_anonymous': self.is_anonymous, - 'type': self.type, - 'allows_multiple_answers': self.allows_multiple_answers, - 'explanation': self.explanation, - 'explanation_entities': [self.explanation_entities[0].to_dict()], - 'open_period': self.open_period, - 'close_date': to_timestamp(self.close_date), + "id": self.id_, + "question": self.question, + "options": [o.to_dict() for o in self.options], + "total_voter_count": self.total_voter_count, + "is_closed": self.is_closed, + "is_anonymous": self.is_anonymous, + "type": self.type, + "allows_multiple_answers": self.allows_multiple_answers, + "explanation": self.explanation, + "explanation_entities": [self.explanation_entities[0].to_dict()], + "open_period": self.open_period, + "close_date": to_timestamp(self.close_date), } poll = Poll.de_json(json_dict, bot) @@ -201,38 +198,38 @@ def test_to_dict(self, poll): poll_dict = poll.to_dict() assert isinstance(poll_dict, dict) - assert poll_dict['id'] == poll.id - assert poll_dict['question'] == poll.question - assert poll_dict['options'] == [o.to_dict() for o in poll.options] - assert poll_dict['total_voter_count'] == poll.total_voter_count - assert poll_dict['is_closed'] == poll.is_closed - assert poll_dict['is_anonymous'] == poll.is_anonymous - assert poll_dict['type'] == poll.type - assert poll_dict['allows_multiple_answers'] == poll.allows_multiple_answers - assert poll_dict['explanation'] == poll.explanation - assert poll_dict['explanation_entities'] == [poll.explanation_entities[0].to_dict()] - assert poll_dict['open_period'] == poll.open_period - assert poll_dict['close_date'] == to_timestamp(poll.close_date) + assert poll_dict["id"] == poll.id + assert poll_dict["question"] == poll.question + assert poll_dict["options"] == [o.to_dict() for o in poll.options] + assert poll_dict["total_voter_count"] == poll.total_voter_count + assert poll_dict["is_closed"] == poll.is_closed + assert poll_dict["is_anonymous"] == poll.is_anonymous + assert poll_dict["type"] == poll.type + assert poll_dict["allows_multiple_answers"] == poll.allows_multiple_answers + assert poll_dict["explanation"] == poll.explanation + assert poll_dict["explanation_entities"] == [poll.explanation_entities[0].to_dict()] + assert poll_dict["open_period"] == poll.open_period + assert poll_dict["close_date"] == to_timestamp(poll.close_date) def test_parse_entity(self, poll): entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) poll.explanation_entities = [entity] - assert poll.parse_explanation_entity(entity) == 'http://google.com' + assert poll.parse_explanation_entity(entity) == "http://google.com" def test_parse_entities(self, poll): entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17) entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1) poll.explanation_entities = [entity_2, entity] - assert poll.parse_explanation_entities(MessageEntity.URL) == {entity: 'http://google.com'} - assert poll.parse_explanation_entities() == {entity: 'http://google.com', entity_2: 'h'} + assert poll.parse_explanation_entities(MessageEntity.URL) == {entity: "http://google.com"} + assert poll.parse_explanation_entities() == {entity: "http://google.com", entity_2: "h"} def test_equality(self): - a = Poll(123, 'question', ['O1', 'O2'], 1, False, True, Poll.REGULAR, True) - b = Poll(123, 'question', ['o1', 'o2'], 1, True, False, Poll.REGULAR, True) - c = Poll(456, 'question', ['o1', 'o2'], 1, True, False, Poll.REGULAR, True) - d = PollOption('Text', 1) + a = Poll(123, "question", ["O1", "O2"], 1, False, True, Poll.REGULAR, True) + b = Poll(123, "question", ["o1", "o2"], 1, True, False, Poll.REGULAR, True) + c = Poll(456, "question", ["o1", "o2"], 1, True, False, Poll.REGULAR, True) + d = PollOption("Text", 1) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_pollanswerhandler.py b/tests/test_pollanswerhandler.py index a944c09a308..aafad434334 100644 --- a/tests/test_pollanswerhandler.py +++ b/tests/test_pollanswerhandler.py @@ -34,76 +34,56 @@ ) from telegram.ext import PollAnswerHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def poll_answer(bot): - return Update(0, poll_answer=PollAnswer(1, User(2, 'test user', False), [0, 1])) + return Update(0, poll_answer=PollAnswer(1, User(2, "test user", False), [0, 1])) class TestPollAnswerHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - handler = PollAnswerHandler(self.callback_basic) + def test_slot_behaviour(self, mro_slots): + handler = PollAnswerHandler(self.callback_context) for attr in handler.__slots__: - assert getattr(handler, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not handler.__dict__, f"got missing slot(s): {handler.__dict__}" + assert getattr(handler, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(handler)) == len(set(mro_slots(handler))), "duplicate slot" - handler.custom, handler.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -117,70 +97,13 @@ def callback_context(self, update, context): and isinstance(update.poll_answer, PollAnswer) ) - def test_basic(self, dp, poll_answer): - handler = PollAnswerHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(poll_answer) - - dp.process_update(poll_answer) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, poll_answer): - handler = PollAnswerHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(poll_answer) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollAnswerHandler(self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll_answer) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollAnswerHandler(self.callback_data_2, pass_chat_data=True, pass_user_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll_answer) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, poll_answer): - handler = PollAnswerHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(poll_answer) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollAnswerHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll_answer) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollAnswerHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll_answer) - assert self.test_flag - def test_other_update_types(self, false_update): - handler = PollAnswerHandler(self.callback_basic) + handler = PollAnswerHandler(self.callback_context) assert not handler.check_update(false_update) - def test_context(self, cdp, poll_answer): + def test_context(self, dp, poll_answer): handler = PollAnswerHandler(self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(poll_answer) + dp.process_update(poll_answer) assert self.test_flag diff --git a/tests/test_pollhandler.py b/tests/test_pollhandler.py index e4b52148b91..4e1add5a595 100644 --- a/tests/test_pollhandler.py +++ b/tests/test_pollhandler.py @@ -35,46 +35,46 @@ ) from telegram.ext import PollHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=2, **request.param) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def poll(bot): return Update( 0, poll=Poll( 1, - 'question', - [PollOption('1', 0), PollOption('2', 0)], + "question", + [PollOption("1", 0), PollOption("2", 0)], 0, False, False, @@ -87,36 +87,16 @@ def poll(bot): class TestPollHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - inst = PollHandler(self.callback_basic) + def test_slot_behaviour(self, mro_slots): + inst = PollHandler(self.callback_context) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -130,68 +110,13 @@ def callback_context(self, update, context): and isinstance(update.poll, Poll) ) - def test_basic(self, dp, poll): - handler = PollHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(poll) - - dp.process_update(poll) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, poll): - handler = PollHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(poll) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollHandler(self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollHandler(self.callback_data_2, pass_chat_data=True, pass_user_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, poll): - handler = PollHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(poll) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll) - assert self.test_flag - - dp.remove_handler(handler) - handler = PollHandler(self.callback_queue_2, pass_job_queue=True, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(poll) - assert self.test_flag - def test_other_update_types(self, false_update): - handler = PollHandler(self.callback_basic) + handler = PollHandler(self.callback_context) assert not handler.check_update(false_update) - def test_context(self, cdp, poll): + def test_context(self, dp, poll): handler = PollHandler(self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(poll) + dp.process_update(poll) assert self.test_flag diff --git a/tests/test_precheckoutquery.py b/tests/test_precheckoutquery.py index d9efd8e07ad..794b331e072 100644 --- a/tests/test_precheckoutquery.py +++ b/tests/test_precheckoutquery.py @@ -22,7 +22,7 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def pre_checkout_query(bot): return PreCheckoutQuery( TestPreCheckoutQuery.id_, @@ -38,31 +38,28 @@ def pre_checkout_query(bot): class TestPreCheckoutQuery: id_ = 5 - invoice_payload = 'invoice_payload' - shipping_option_id = 'shipping_option_id' - currency = 'EUR' + invoice_payload = "invoice_payload" + shipping_option_id = "shipping_option_id" + currency = "EUR" total_amount = 100 - from_user = User(0, '', False) + from_user = User(0, "", False) order_info = OrderInfo() - def test_slot_behaviour(self, pre_checkout_query, recwarn, mro_slots): + def test_slot_behaviour(self, pre_checkout_query, mro_slots): inst = pre_checkout_query for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'id': self.id_, - 'invoice_payload': self.invoice_payload, - 'shipping_option_id': self.shipping_option_id, - 'currency': self.currency, - 'total_amount': self.total_amount, - 'from': self.from_user.to_dict(), - 'order_info': self.order_info.to_dict(), + "id": self.id_, + "invoice_payload": self.invoice_payload, + "shipping_option_id": self.shipping_option_id, + "currency": self.currency, + "total_amount": self.total_amount, + "from": self.from_user.to_dict(), + "order_info": self.order_info.to_dict(), } pre_checkout_query = PreCheckoutQuery.de_json(json_dict, bot) @@ -78,30 +75,30 @@ def test_to_dict(self, pre_checkout_query): pre_checkout_query_dict = pre_checkout_query.to_dict() assert isinstance(pre_checkout_query_dict, dict) - assert pre_checkout_query_dict['id'] == pre_checkout_query.id - assert pre_checkout_query_dict['invoice_payload'] == pre_checkout_query.invoice_payload + assert pre_checkout_query_dict["id"] == pre_checkout_query.id + assert pre_checkout_query_dict["invoice_payload"] == pre_checkout_query.invoice_payload assert ( - pre_checkout_query_dict['shipping_option_id'] == pre_checkout_query.shipping_option_id + pre_checkout_query_dict["shipping_option_id"] == pre_checkout_query.shipping_option_id ) - assert pre_checkout_query_dict['currency'] == pre_checkout_query.currency - assert pre_checkout_query_dict['from'] == pre_checkout_query.from_user.to_dict() - assert pre_checkout_query_dict['order_info'] == pre_checkout_query.order_info.to_dict() + assert pre_checkout_query_dict["currency"] == pre_checkout_query.currency + assert pre_checkout_query_dict["from"] == pre_checkout_query.from_user.to_dict() + assert pre_checkout_query_dict["order_info"] == pre_checkout_query.order_info.to_dict() def test_answer(self, monkeypatch, pre_checkout_query): def make_assertion(*_, **kwargs): - return kwargs['pre_checkout_query_id'] == pre_checkout_query.id + return kwargs["pre_checkout_query_id"] == pre_checkout_query.id assert check_shortcut_signature( - PreCheckoutQuery.answer, Bot.answer_pre_checkout_query, ['pre_checkout_query_id'], [] + PreCheckoutQuery.answer, Bot.answer_pre_checkout_query, ["pre_checkout_query_id"], [] ) assert check_shortcut_call( pre_checkout_query.answer, pre_checkout_query.bot, - 'answer_pre_checkout_query', + "answer_pre_checkout_query", ) assert check_defaults_handling(pre_checkout_query.answer, pre_checkout_query.bot) - monkeypatch.setattr(pre_checkout_query.bot, 'answer_pre_checkout_query', make_assertion) + monkeypatch.setattr(pre_checkout_query.bot, "answer_pre_checkout_query", make_assertion) assert pre_checkout_query.answer(ok=True) def test_equality(self): @@ -111,7 +108,7 @@ def test_equality(self): b = PreCheckoutQuery( self.id_, self.from_user, self.currency, self.total_amount, self.invoice_payload ) - c = PreCheckoutQuery(self.id_, None, '', 0, '') + c = PreCheckoutQuery(self.id_, None, "", 0, "") d = PreCheckoutQuery( 0, self.from_user, self.currency, self.total_amount, self.invoice_payload ) diff --git a/tests/test_precheckoutqueryhandler.py b/tests/test_precheckoutqueryhandler.py index 642a77e3623..295f134b82a 100644 --- a/tests/test_precheckoutqueryhandler.py +++ b/tests/test_precheckoutqueryhandler.py @@ -34,44 +34,44 @@ ) from telegram.ext import PreCheckoutQueryHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "inline_query", + "chosen_inline_result", + "shipping_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=1, **request.param) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def pre_checkout_query(): return Update( 1, pre_checkout_query=PreCheckoutQuery( - 'id', User(1, 'test user', False), 'EUR', 223, 'invoice_payload' + "id", User(1, "test user", False), "EUR", 223, "invoice_payload" ), ) @@ -79,36 +79,16 @@ def pre_checkout_query(): class TestPreCheckoutQueryHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - inst = PreCheckoutQueryHandler(self.callback_basic) + def test_slot_behaviour(self, mro_slots): + inst = PreCheckoutQueryHandler(self.callback_context) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -122,71 +102,13 @@ def callback_context(self, update, context): and isinstance(update.pre_checkout_query, PreCheckoutQuery) ) - def test_basic(self, dp, pre_checkout_query): - handler = PreCheckoutQueryHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(pre_checkout_query) - dp.process_update(pre_checkout_query) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, pre_checkout_query): - handler = PreCheckoutQueryHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(pre_checkout_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = PreCheckoutQueryHandler(self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(pre_checkout_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = PreCheckoutQueryHandler( - self.callback_data_2, pass_chat_data=True, pass_user_data=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(pre_checkout_query) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, pre_checkout_query): - handler = PreCheckoutQueryHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(pre_checkout_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = PreCheckoutQueryHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(pre_checkout_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = PreCheckoutQueryHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(pre_checkout_query) - assert self.test_flag - def test_other_update_types(self, false_update): - handler = PreCheckoutQueryHandler(self.callback_basic) + handler = PreCheckoutQueryHandler(self.callback_context) assert not handler.check_update(false_update) - def test_context(self, cdp, pre_checkout_query): + def test_context(self, dp, pre_checkout_query): handler = PreCheckoutQueryHandler(self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(pre_checkout_query) + dp.process_update(pre_checkout_query) assert self.test_flag diff --git a/tests/test_promise.py b/tests/test_promise.py index ceb9f196e7d..330d33a439d 100644 --- a/tests/test_promise.py +++ b/tests/test_promise.py @@ -30,14 +30,11 @@ class TestPromise: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): inst = Promise(self.test_call, [], {}) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.args = 'should give warning', [] - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): @@ -55,7 +52,7 @@ def callback(): def test_run_with_exception(self): def callback(): - raise TelegramError('Error') + raise TelegramError("Error") promise = Promise(callback, [], {}) promise.run() @@ -66,12 +63,12 @@ def callback(): def test_wait_for_exception(self): def callback(): - raise TelegramError('Error') + raise TelegramError("Error") promise = Promise(callback, [], {}) promise.run() - with pytest.raises(TelegramError, match='Error'): + with pytest.raises(TelegramError, match="Error"): promise.result() def test_done_cb_after_run(self): @@ -139,7 +136,7 @@ def done_callback(_): def test_done_cb_not_run_on_excp(self): def callback(): - raise TelegramError('Error') + raise TelegramError("Error") def done_callback(_): self.test_flag = True diff --git a/tests/test_proximityalerttriggered.py b/tests/test_proximityalerttriggered.py index 8e09cc00d2b..41512292106 100644 --- a/tests/test_proximityalerttriggered.py +++ b/tests/test_proximityalerttriggered.py @@ -31,24 +31,21 @@ def proximity_alert_triggered(): class TestProximityAlertTriggered: - traveler = User(1, 'foo', False) - watcher = User(2, 'bar', False) + traveler = User(1, "foo", False) + watcher = User(2, "bar", False) distance = 42 - def test_slot_behaviour(self, proximity_alert_triggered, mro_slots, recwarn): + def test_slot_behaviour(self, proximity_alert_triggered, mro_slots): inst = proximity_alert_triggered for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.traveler = 'should give warning', self.traveler - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'traveler': self.traveler.to_dict(), - 'watcher': self.watcher.to_dict(), - 'distance': self.distance, + "traveler": self.traveler.to_dict(), + "watcher": self.watcher.to_dict(), + "distance": self.distance, } proximity_alert_triggered = ProximityAlertTriggered.de_json(json_dict, bot) @@ -63,22 +60,22 @@ def test_to_dict(self, proximity_alert_triggered): assert isinstance(proximity_alert_triggered_dict, dict) assert ( - proximity_alert_triggered_dict['traveler'] + proximity_alert_triggered_dict["traveler"] == proximity_alert_triggered.traveler.to_dict() ) assert ( - proximity_alert_triggered_dict['watcher'] + proximity_alert_triggered_dict["watcher"] == proximity_alert_triggered.watcher.to_dict() ) - assert proximity_alert_triggered_dict['distance'] == proximity_alert_triggered.distance + assert proximity_alert_triggered_dict["distance"] == proximity_alert_triggered.distance def test_equality(self, proximity_alert_triggered): a = proximity_alert_triggered - b = ProximityAlertTriggered(User(1, 'John', False), User(2, 'Doe', False), 42) - c = ProximityAlertTriggered(User(3, 'John', False), User(2, 'Doe', False), 42) - d = ProximityAlertTriggered(User(1, 'John', False), User(3, 'Doe', False), 42) - e = ProximityAlertTriggered(User(1, 'John', False), User(2, 'Doe', False), 43) - f = BotCommand('start', 'description') + b = ProximityAlertTriggered(User(1, "John", False), User(2, "Doe", False), 42) + c = ProximityAlertTriggered(User(3, "John", False), User(2, "Doe", False), 42) + d = ProximityAlertTriggered(User(1, "John", False), User(3, "Doe", False), 42) + e = ProximityAlertTriggered(User(1, "John", False), User(2, "Doe", False), 43) + f = BotCommand("start", "description") assert a == b assert hash(a) == hash(b) diff --git a/tests/test_regexhandler.py b/tests/test_regexhandler.py index 03ee1751fec..e69de29bb2d 100644 --- a/tests/test_regexhandler.py +++ b/tests/test_regexhandler.py @@ -1,292 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. -from queue import Queue - -import pytest -from telegram.utils.deprecate import TelegramDeprecationWarning - -from telegram import ( - Message, - Update, - Chat, - Bot, - User, - CallbackQuery, - InlineQuery, - ChosenInlineResult, - ShippingQuery, - PreCheckoutQuery, -) -from telegram.ext import RegexHandler, CallbackContext, JobQueue - -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') - -params = [ - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, -] - -ids = ( - 'callback_query', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', -) - - -@pytest.fixture(scope='class', params=params, ids=ids) -def false_update(request): - return Update(update_id=1, **request.param) - - -@pytest.fixture(scope='class') -def message(bot): - return Message( - 1, None, Chat(1, ''), from_user=User(1, '', False), text='test message', bot=bot - ) - - -class TestRegexHandler: - test_flag = False - - def test_slot_behaviour(self, recwarn, mro_slots): - inst = RegexHandler("", self.callback_basic) - for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" - assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert 'custom' in str(recwarn[-1].message), recwarn.list - - @pytest.fixture(autouse=True) - def reset(self): - self.test_flag = False - - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - - def callback_group(self, bot, update, groups=None, groupdict=None): - if groups is not None: - self.test_flag = groups == ('t', ' message') - if groupdict is not None: - self.test_flag = groupdict == {'begin': 't', 'end': ' message'} - - def callback_context(self, update, context): - self.test_flag = ( - isinstance(context, CallbackContext) - and isinstance(context.bot, Bot) - and isinstance(update, Update) - and isinstance(context.update_queue, Queue) - and isinstance(context.job_queue, JobQueue) - and isinstance(context.user_data, dict) - and isinstance(context.chat_data, dict) - and isinstance(context.bot_data, dict) - and isinstance(update.message, Message) - ) - - def callback_context_pattern(self, update, context): - if context.matches[0].groups(): - self.test_flag = context.matches[0].groups() == ('t', ' message') - if context.matches[0].groupdict(): - self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' message'} - - def test_deprecation_Warning(self): - with pytest.warns(TelegramDeprecationWarning, match='RegexHandler is deprecated.'): - RegexHandler('.*', self.callback_basic) - - def test_basic(self, dp, message): - handler = RegexHandler('.*', self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(Update(0, message)) - dp.process_update(Update(0, message)) - assert self.test_flag - - def test_pattern(self, message): - handler = RegexHandler('.*est.*', self.callback_basic) - - assert handler.check_update(Update(0, message)) - - handler = RegexHandler('.*not in here.*', self.callback_basic) - assert not handler.check_update(Update(0, message)) - - def test_with_passing_group_dict(self, dp, message): - handler = RegexHandler( - '(?P.*)est(?P.*)', self.callback_group, pass_groups=True - ) - dp.add_handler(handler) - dp.process_update(Update(0, message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = RegexHandler( - '(?P.*)est(?P.*)', self.callback_group, pass_groupdict=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message)) - assert self.test_flag - - def test_edited(self, message): - handler = RegexHandler( - '.*', - self.callback_basic, - edited_updates=True, - message_updates=False, - channel_post_updates=False, - ) - - assert handler.check_update(Update(0, edited_message=message)) - assert not handler.check_update(Update(0, message=message)) - assert not handler.check_update(Update(0, channel_post=message)) - assert handler.check_update(Update(0, edited_channel_post=message)) - - def test_channel_post(self, message): - handler = RegexHandler( - '.*', - self.callback_basic, - edited_updates=False, - message_updates=False, - channel_post_updates=True, - ) - - assert not handler.check_update(Update(0, edited_message=message)) - assert not handler.check_update(Update(0, message=message)) - assert handler.check_update(Update(0, channel_post=message)) - assert not handler.check_update(Update(0, edited_channel_post=message)) - - def test_multiple_flags(self, message): - handler = RegexHandler( - '.*', - self.callback_basic, - edited_updates=True, - message_updates=True, - channel_post_updates=True, - ) - - assert handler.check_update(Update(0, edited_message=message)) - assert handler.check_update(Update(0, message=message)) - assert handler.check_update(Update(0, channel_post=message)) - assert handler.check_update(Update(0, edited_channel_post=message)) - - def test_none_allowed(self): - with pytest.raises(ValueError, match='are all False'): - RegexHandler( - '.*', - self.callback_basic, - message_updates=False, - channel_post_updates=False, - edited_updates=False, - ) - - def test_pass_user_or_chat_data(self, dp, message): - handler = RegexHandler('.*', self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = RegexHandler('.*', self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = RegexHandler( - '.*', self.callback_data_2, pass_chat_data=True, pass_user_data=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, message): - handler = RegexHandler('.*', self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = RegexHandler('.*', self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - dp.remove_handler(handler) - handler = RegexHandler( - '.*', self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(Update(0, message=message)) - assert self.test_flag - - def test_other_update_types(self, false_update): - handler = RegexHandler('.*', self.callback_basic, edited_updates=True) - assert not handler.check_update(false_update) - - def test_context(self, cdp, message): - handler = RegexHandler(r'(t)est(.*)', self.callback_context) - cdp.add_handler(handler) - - cdp.process_update(Update(0, message=message)) - assert self.test_flag - - def test_context_pattern(self, cdp, message): - handler = RegexHandler(r'(t)est(.*)', self.callback_context_pattern) - cdp.add_handler(handler) - - cdp.process_update(Update(0, message=message)) - assert self.test_flag - - cdp.remove_handler(handler) - handler = RegexHandler(r'(t)est(.*)', self.callback_context_pattern) - cdp.add_handler(handler) - - cdp.process_update(Update(0, message=message)) - assert self.test_flag diff --git a/tests/test_replykeyboardmarkup.py b/tests/test_replykeyboardmarkup.py index 67587a49bd7..3e3ee511d8e 100644 --- a/tests/test_replykeyboardmarkup.py +++ b/tests/test_replykeyboardmarkup.py @@ -22,7 +22,7 @@ from telegram import ReplyKeyboardMarkup, KeyboardButton, InlineKeyboardMarkup -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def reply_keyboard_markup(): return ReplyKeyboardMarkup( TestReplyKeyboardMarkup.keyboard, @@ -34,64 +34,61 @@ def reply_keyboard_markup(): class TestReplyKeyboardMarkup: - keyboard = [[KeyboardButton('button1'), KeyboardButton('button2')]] + keyboard = [[KeyboardButton("button1"), KeyboardButton("button2")]] resize_keyboard = True one_time_keyboard = True selective = True - input_field_placeholder = 'lol a keyboard' + input_field_placeholder = "lol a keyboard" - def test_slot_behaviour(self, reply_keyboard_markup, mro_slots, recwarn): + def test_slot_behaviour(self, reply_keyboard_markup, mro_slots): inst = reply_keyboard_markup for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.selective = 'should give warning', self.selective - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @flaky(3, 1) def test_send_message_with_reply_keyboard_markup(self, bot, chat_id, reply_keyboard_markup): - message = bot.send_message(chat_id, 'Text', reply_markup=reply_keyboard_markup) + message = bot.send_message(chat_id, "Text", reply_markup=reply_keyboard_markup) - assert message.text == 'Text' + assert message.text == "Text" @flaky(3, 1) def test_send_message_with_data_markup(self, bot, chat_id): - message = bot.send_message(chat_id, 'text 2', reply_markup={'keyboard': [['1', '2']]}) + message = bot.send_message(chat_id, "text 2", reply_markup={"keyboard": [["1", "2"]]}) - assert message.text == 'text 2' + assert message.text == "text 2" def test_from_button(self): reply_keyboard_markup = ReplyKeyboardMarkup.from_button( - KeyboardButton(text='button1') + KeyboardButton(text="button1") ).keyboard assert len(reply_keyboard_markup) == 1 assert len(reply_keyboard_markup[0]) == 1 - reply_keyboard_markup = ReplyKeyboardMarkup.from_button('button1').keyboard + reply_keyboard_markup = ReplyKeyboardMarkup.from_button("button1").keyboard assert len(reply_keyboard_markup) == 1 assert len(reply_keyboard_markup[0]) == 1 def test_from_row(self): reply_keyboard_markup = ReplyKeyboardMarkup.from_row( - [KeyboardButton(text='button1'), KeyboardButton(text='button2')] + [KeyboardButton(text="button1"), KeyboardButton(text="button2")] ).keyboard assert len(reply_keyboard_markup) == 1 assert len(reply_keyboard_markup[0]) == 2 - reply_keyboard_markup = ReplyKeyboardMarkup.from_row(['button1', 'button2']).keyboard + reply_keyboard_markup = ReplyKeyboardMarkup.from_row(["button1", "button2"]).keyboard assert len(reply_keyboard_markup) == 1 assert len(reply_keyboard_markup[0]) == 2 def test_from_column(self): reply_keyboard_markup = ReplyKeyboardMarkup.from_column( - [KeyboardButton(text='button1'), KeyboardButton(text='button2')] + [KeyboardButton(text="button1"), KeyboardButton(text="button2")] ).keyboard assert len(reply_keyboard_markup) == 2 assert len(reply_keyboard_markup[0]) == 1 assert len(reply_keyboard_markup[1]) == 1 - reply_keyboard_markup = ReplyKeyboardMarkup.from_column(['button1', 'button2']).keyboard + reply_keyboard_markup = ReplyKeyboardMarkup.from_column(["button1", "button2"]).keyboard assert len(reply_keyboard_markup) == 2 assert len(reply_keyboard_markup[0]) == 1 assert len(reply_keyboard_markup[1]) == 1 @@ -110,35 +107,35 @@ def test_to_dict(self, reply_keyboard_markup): assert isinstance(reply_keyboard_markup_dict, dict) assert ( - reply_keyboard_markup_dict['keyboard'][0][0] + reply_keyboard_markup_dict["keyboard"][0][0] == reply_keyboard_markup.keyboard[0][0].to_dict() ) assert ( - reply_keyboard_markup_dict['keyboard'][0][1] + reply_keyboard_markup_dict["keyboard"][0][1] == reply_keyboard_markup.keyboard[0][1].to_dict() ) assert ( - reply_keyboard_markup_dict['resize_keyboard'] == reply_keyboard_markup.resize_keyboard + reply_keyboard_markup_dict["resize_keyboard"] == reply_keyboard_markup.resize_keyboard ) assert ( - reply_keyboard_markup_dict['one_time_keyboard'] + reply_keyboard_markup_dict["one_time_keyboard"] == reply_keyboard_markup.one_time_keyboard ) - assert reply_keyboard_markup_dict['selective'] == reply_keyboard_markup.selective + assert reply_keyboard_markup_dict["selective"] == reply_keyboard_markup.selective assert ( - reply_keyboard_markup_dict['input_field_placeholder'] + reply_keyboard_markup_dict["input_field_placeholder"] == reply_keyboard_markup.input_field_placeholder ) def test_equality(self): - a = ReplyKeyboardMarkup.from_column(['button1', 'button2', 'button3']) + a = ReplyKeyboardMarkup.from_column(["button1", "button2", "button3"]) b = ReplyKeyboardMarkup.from_column( - [KeyboardButton(text) for text in ['button1', 'button2', 'button3']] + [KeyboardButton(text) for text in ["button1", "button2", "button3"]] ) - c = ReplyKeyboardMarkup.from_column(['button1', 'button2']) - d = ReplyKeyboardMarkup.from_column(['button1', 'button2', 'button3.1']) - e = ReplyKeyboardMarkup([['button1', 'button1'], ['button2'], ['button3.1']]) - f = InlineKeyboardMarkup.from_column(['button1', 'button2', 'button3']) + c = ReplyKeyboardMarkup.from_column(["button1", "button2"]) + d = ReplyKeyboardMarkup.from_column(["button1", "button2", "button3.1"]) + e = ReplyKeyboardMarkup([["button1", "button1"], ["button2"], ["button3.1"]]) + f = InlineKeyboardMarkup.from_column(["button1", "button2", "button3"]) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_replykeyboardremove.py b/tests/test_replykeyboardremove.py index c948b04e3dd..9caec5f61f1 100644 --- a/tests/test_replykeyboardremove.py +++ b/tests/test_replykeyboardremove.py @@ -22,7 +22,7 @@ from telegram import ReplyKeyboardRemove -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def reply_keyboard_remove(): return ReplyKeyboardRemove(selective=TestReplyKeyboardRemove.selective) @@ -31,20 +31,17 @@ class TestReplyKeyboardRemove: remove_keyboard = True selective = True - def test_slot_behaviour(self, reply_keyboard_remove, recwarn, mro_slots): + def test_slot_behaviour(self, reply_keyboard_remove, mro_slots): inst = reply_keyboard_remove for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.selective = 'should give warning', self.selective - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @flaky(3, 1) def test_send_message_with_reply_keyboard_remove(self, bot, chat_id, reply_keyboard_remove): - message = bot.send_message(chat_id, 'Text', reply_markup=reply_keyboard_remove) + message = bot.send_message(chat_id, "Text", reply_markup=reply_keyboard_remove) - assert message.text == 'Text' + assert message.text == "Text" def test_expected_values(self, reply_keyboard_remove): assert reply_keyboard_remove.remove_keyboard == self.remove_keyboard @@ -54,6 +51,6 @@ def test_to_dict(self, reply_keyboard_remove): reply_keyboard_remove_dict = reply_keyboard_remove.to_dict() assert ( - reply_keyboard_remove_dict['remove_keyboard'] == reply_keyboard_remove.remove_keyboard + reply_keyboard_remove_dict["remove_keyboard"] == reply_keyboard_remove.remove_keyboard ) - assert reply_keyboard_remove_dict['selective'] == reply_keyboard_remove.selective + assert reply_keyboard_remove_dict["selective"] == reply_keyboard_remove.selective diff --git a/tests/test_request.py b/tests/test_request.py index 4442320c855..f5faea13c8d 100644 --- a/tests/test_request.py +++ b/tests/test_request.py @@ -22,14 +22,11 @@ from telegram.utils.request import Request -def test_slot_behaviour(recwarn, mro_slots): +def test_slot_behaviour(mro_slots): inst = Request() for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst._connect_timeout = 'should give warning', 10 - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_replaced_unprintable_char(): @@ -39,7 +36,7 @@ def test_replaced_unprintable_char(): """ server_response = b'{"invalid utf-8": "\x80", "result": "KUKU"}' - assert Request._parse(server_response) == 'KUKU' + assert Request._parse(server_response) == "KUKU" def test_parse_illegal_json(): @@ -49,5 +46,5 @@ def test_parse_illegal_json(): """ server_response = b'{"invalid utf-8": "\x80", result: "KUKU"}' - with pytest.raises(TelegramError, match='Invalid server response'): + with pytest.raises(TelegramError, match="Invalid server response"): Request._parse(server_response) diff --git a/tests/test_shippingaddress.py b/tests/test_shippingaddress.py index 4146cdad019..ed1f0f6caaf 100644 --- a/tests/test_shippingaddress.py +++ b/tests/test_shippingaddress.py @@ -21,7 +21,7 @@ from telegram import ShippingAddress -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def shipping_address(): return ShippingAddress( TestShippingAddress.country_code, @@ -34,30 +34,27 @@ def shipping_address(): class TestShippingAddress: - country_code = 'GB' - state = 'state' - city = 'London' - street_line1 = '12 Grimmauld Place' - street_line2 = 'street_line2' - post_code = 'WC1' - - def test_slot_behaviour(self, shipping_address, recwarn, mro_slots): + country_code = "GB" + state = "state" + city = "London" + street_line1 = "12 Grimmauld Place" + street_line2 = "street_line2" + post_code = "WC1" + + def test_slot_behaviour(self, shipping_address, mro_slots): inst = shipping_address for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.state = 'should give warning', self.state - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'country_code': self.country_code, - 'state': self.state, - 'city': self.city, - 'street_line1': self.street_line1, - 'street_line2': self.street_line2, - 'post_code': self.post_code, + "country_code": self.country_code, + "state": self.state, + "city": self.city, + "street_line1": self.street_line1, + "street_line2": self.street_line2, + "post_code": self.post_code, } shipping_address = ShippingAddress.de_json(json_dict, bot) @@ -72,12 +69,12 @@ def test_to_dict(self, shipping_address): shipping_address_dict = shipping_address.to_dict() assert isinstance(shipping_address_dict, dict) - assert shipping_address_dict['country_code'] == shipping_address.country_code - assert shipping_address_dict['state'] == shipping_address.state - assert shipping_address_dict['city'] == shipping_address.city - assert shipping_address_dict['street_line1'] == shipping_address.street_line1 - assert shipping_address_dict['street_line2'] == shipping_address.street_line2 - assert shipping_address_dict['post_code'] == shipping_address.post_code + assert shipping_address_dict["country_code"] == shipping_address.country_code + assert shipping_address_dict["state"] == shipping_address.state + assert shipping_address_dict["city"] == shipping_address.city + assert shipping_address_dict["street_line1"] == shipping_address.street_line1 + assert shipping_address_dict["street_line2"] == shipping_address.street_line2 + assert shipping_address_dict["post_code"] == shipping_address.post_code def test_equality(self): a = ShippingAddress( @@ -97,22 +94,22 @@ def test_equality(self): self.post_code, ) d = ShippingAddress( - '', self.state, self.city, self.street_line1, self.street_line2, self.post_code + "", self.state, self.city, self.street_line1, self.street_line2, self.post_code ) d2 = ShippingAddress( - self.country_code, '', self.city, self.street_line1, self.street_line2, self.post_code + self.country_code, "", self.city, self.street_line1, self.street_line2, self.post_code ) d3 = ShippingAddress( - self.country_code, self.state, '', self.street_line1, self.street_line2, self.post_code + self.country_code, self.state, "", self.street_line1, self.street_line2, self.post_code ) d4 = ShippingAddress( - self.country_code, self.state, self.city, '', self.street_line2, self.post_code + self.country_code, self.state, self.city, "", self.street_line2, self.post_code ) d5 = ShippingAddress( - self.country_code, self.state, self.city, self.street_line1, '', self.post_code + self.country_code, self.state, self.city, self.street_line1, "", self.post_code ) d6 = ShippingAddress( - self.country_code, self.state, self.city, self.street_line1, self.street_line2, '' + self.country_code, self.state, self.city, self.street_line1, self.street_line2, "" ) assert a == b diff --git a/tests/test_shippingoption.py b/tests/test_shippingoption.py index 7f0f0f3fbd0..f4d3d148d38 100644 --- a/tests/test_shippingoption.py +++ b/tests/test_shippingoption.py @@ -21,7 +21,7 @@ from telegram import LabeledPrice, ShippingOption, Voice -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def shipping_option(): return ShippingOption( TestShippingOption.id_, TestShippingOption.title, TestShippingOption.prices @@ -29,18 +29,15 @@ def shipping_option(): class TestShippingOption: - id_ = 'id' - title = 'title' - prices = [LabeledPrice('Fish Container', 100), LabeledPrice('Premium Fish Container', 1000)] + id_ = "id" + title = "title" + prices = [LabeledPrice("Fish Container", 100), LabeledPrice("Premium Fish Container", 1000)] - def test_slot_behaviour(self, shipping_option, recwarn, mro_slots): + def test_slot_behaviour(self, shipping_option, mro_slots): inst = shipping_option for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self, shipping_option): assert shipping_option.id == self.id_ @@ -51,17 +48,17 @@ def test_to_dict(self, shipping_option): shipping_option_dict = shipping_option.to_dict() assert isinstance(shipping_option_dict, dict) - assert shipping_option_dict['id'] == shipping_option.id - assert shipping_option_dict['title'] == shipping_option.title - assert shipping_option_dict['prices'][0] == shipping_option.prices[0].to_dict() - assert shipping_option_dict['prices'][1] == shipping_option.prices[1].to_dict() + assert shipping_option_dict["id"] == shipping_option.id + assert shipping_option_dict["title"] == shipping_option.title + assert shipping_option_dict["prices"][0] == shipping_option.prices[0].to_dict() + assert shipping_option_dict["prices"][1] == shipping_option.prices[1].to_dict() def test_equality(self): a = ShippingOption(self.id_, self.title, self.prices) b = ShippingOption(self.id_, self.title, self.prices) - c = ShippingOption(self.id_, '', []) + c = ShippingOption(self.id_, "", []) d = ShippingOption(0, self.title, self.prices) - e = Voice(self.id_, 'someid', 0) + e = Voice(self.id_, "someid", 0) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_shippingquery.py b/tests/test_shippingquery.py index 58a4783ed58..d6147be7a2f 100644 --- a/tests/test_shippingquery.py +++ b/tests/test_shippingquery.py @@ -22,7 +22,7 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def shipping_query(bot): return ShippingQuery( TestShippingQuery.id_, @@ -35,25 +35,22 @@ def shipping_query(bot): class TestShippingQuery: id_ = 5 - invoice_payload = 'invoice_payload' - from_user = User(0, '', False) - shipping_address = ShippingAddress('GB', '', 'London', '12 Grimmauld Place', '', 'WC1') + invoice_payload = "invoice_payload" + from_user = User(0, "", False) + shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1") - def test_slot_behaviour(self, shipping_query, recwarn, mro_slots): + def test_slot_behaviour(self, shipping_query, mro_slots): inst = shipping_query for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'id': TestShippingQuery.id_, - 'invoice_payload': TestShippingQuery.invoice_payload, - 'from': TestShippingQuery.from_user.to_dict(), - 'shipping_address': TestShippingQuery.shipping_address.to_dict(), + "id": TestShippingQuery.id_, + "invoice_payload": TestShippingQuery.invoice_payload, + "from": TestShippingQuery.from_user.to_dict(), + "shipping_address": TestShippingQuery.shipping_address.to_dict(), } shipping_query = ShippingQuery.de_json(json_dict, bot) @@ -67,30 +64,30 @@ def test_to_dict(self, shipping_query): shipping_query_dict = shipping_query.to_dict() assert isinstance(shipping_query_dict, dict) - assert shipping_query_dict['id'] == shipping_query.id - assert shipping_query_dict['invoice_payload'] == shipping_query.invoice_payload - assert shipping_query_dict['from'] == shipping_query.from_user.to_dict() - assert shipping_query_dict['shipping_address'] == shipping_query.shipping_address.to_dict() + assert shipping_query_dict["id"] == shipping_query.id + assert shipping_query_dict["invoice_payload"] == shipping_query.invoice_payload + assert shipping_query_dict["from"] == shipping_query.from_user.to_dict() + assert shipping_query_dict["shipping_address"] == shipping_query.shipping_address.to_dict() def test_answer(self, monkeypatch, shipping_query): def make_assertion(*_, **kwargs): - return kwargs['shipping_query_id'] == shipping_query.id + return kwargs["shipping_query_id"] == shipping_query.id assert check_shortcut_signature( - ShippingQuery.answer, Bot.answer_shipping_query, ['shipping_query_id'], [] + ShippingQuery.answer, Bot.answer_shipping_query, ["shipping_query_id"], [] ) assert check_shortcut_call( - shipping_query.answer, shipping_query.bot, 'answer_shipping_query' + shipping_query.answer, shipping_query.bot, "answer_shipping_query" ) assert check_defaults_handling(shipping_query.answer, shipping_query.bot) - monkeypatch.setattr(shipping_query.bot, 'answer_shipping_query', make_assertion) + monkeypatch.setattr(shipping_query.bot, "answer_shipping_query", make_assertion) assert shipping_query.answer(ok=True) def test_equality(self): a = ShippingQuery(self.id_, self.from_user, self.invoice_payload, self.shipping_address) b = ShippingQuery(self.id_, self.from_user, self.invoice_payload, self.shipping_address) - c = ShippingQuery(self.id_, None, '', None) + c = ShippingQuery(self.id_, None, "", None) d = ShippingQuery(0, self.from_user, self.invoice_payload, self.shipping_address) e = Update(self.id_) diff --git a/tests/test_shippingqueryhandler.py b/tests/test_shippingqueryhandler.py index cfa9ecbbdca..a060c1b6bc4 100644 --- a/tests/test_shippingqueryhandler.py +++ b/tests/test_shippingqueryhandler.py @@ -35,47 +35,47 @@ ) from telegram.ext import ShippingQueryHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'chosen_inline_result', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "inline_query", + "chosen_inline_result", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=1, **request.param) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def shiping_query(): return Update( 1, shipping_query=ShippingQuery( 42, - User(1, 'test user', False), - 'invoice_payload', - ShippingAddress('EN', 'my_state', 'my_city', 'steer_1', '', 'post_code'), + User(1, "test user", False), + "invoice_payload", + ShippingAddress("EN", "my_state", "my_city", "steer_1", "", "post_code"), ), ) @@ -83,36 +83,16 @@ def shiping_query(): class TestShippingQueryHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - inst = ShippingQueryHandler(self.callback_basic) + def test_slot_behaviour(self, mro_slots): + inst = ShippingQueryHandler(self.callback_context) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, Update) - self.test_flag = test_bot and test_update - - def callback_data_1(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) or (chat_data is not None) - - def callback_data_2(self, bot, update, user_data=None, chat_data=None): - self.test_flag = (user_data is not None) and (chat_data is not None) - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -126,71 +106,13 @@ def callback_context(self, update, context): and isinstance(update.shipping_query, ShippingQuery) ) - def test_basic(self, dp, shiping_query): - handler = ShippingQueryHandler(self.callback_basic) - dp.add_handler(handler) - - assert handler.check_update(shiping_query) - dp.process_update(shiping_query) - assert self.test_flag - - def test_pass_user_or_chat_data(self, dp, shiping_query): - handler = ShippingQueryHandler(self.callback_data_1, pass_user_data=True) - dp.add_handler(handler) - - dp.process_update(shiping_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = ShippingQueryHandler(self.callback_data_1, pass_chat_data=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(shiping_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = ShippingQueryHandler( - self.callback_data_2, pass_chat_data=True, pass_user_data=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(shiping_query) - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp, shiping_query): - handler = ShippingQueryHandler(self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update(shiping_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = ShippingQueryHandler(self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(shiping_query) - assert self.test_flag - - dp.remove_handler(handler) - handler = ShippingQueryHandler( - self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update(shiping_query) - assert self.test_flag - def test_other_update_types(self, false_update): - handler = ShippingQueryHandler(self.callback_basic) + handler = ShippingQueryHandler(self.callback_context) assert not handler.check_update(false_update) - def test_context(self, cdp, shiping_query): + def test_context(self, dp, shiping_query): handler = ShippingQueryHandler(self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update(shiping_query) + dp.process_update(shiping_query) assert self.test_flag diff --git a/tests/test_slots.py b/tests/test_slots.py index f7579b08e7c..c6bdaf744be 100644 --- a/tests/test_slots.py +++ b/tests/test_slots.py @@ -24,29 +24,22 @@ import inspect -excluded = { - 'telegram.error', - '_ConversationTimeoutContext', - 'DispatcherHandlerStop', - 'Days', - 'telegram.deprecate', - 'TelegramDecryptionError', - 'ContextTypes', - 'CallbackDataCache', - 'InvalidCallbackData', - '_KeyboardData', -} # These modules/classes intentionally don't have __dict__. +included = { # These modules/classes intentionally have __dict__. + "CallbackContext", + "BasePersistence", + "Dispatcher", +} -def test_class_has_slots_and_dict(mro_slots): - tg_paths = [p for p in iglob("telegram/**/*.py", recursive=True) if 'vendor' not in p] +def test_class_has_slots_and_no_dict(): + tg_paths = [p for p in iglob("telegram/**/*.py", recursive=True) if "vendor" not in p] for path in tg_paths: # windows uses backslashes: - if os.name == 'nt': - split_path = path.split('\\') + if os.name == "nt": + split_path = path.split("\\") else: - split_path = path.split('/') + split_path = path.split("/") mod_name = f"telegram{'.ext.' if split_path[1] == 'ext' else '.'}{split_path[-1][:-3]}" spec = importlib.util.spec_from_file_location(mod_name, path) module = importlib.util.module_from_spec(spec) @@ -54,30 +47,22 @@ def test_class_has_slots_and_dict(mro_slots): for name, cls in inspect.getmembers(module, inspect.isclass): if cls.__module__ != module.__name__ or any( # exclude 'imported' modules - x in name for x in {'__class__', '__init__', 'Queue', 'Webhook'} + x in name for x in {"__class__", "__init__", "Queue", "Webhook"} ): continue - assert '__slots__' in cls.__dict__, f"class '{name}' in {path} doesn't have __slots__" - if cls.__module__ in excluded or name in excluded: - continue - assert '__dict__' in get_slots(cls), f"class '{name}' in {path} doesn't have __dict__" - -def get_slots(_class): - slots = [attr for cls in _class.__mro__ if hasattr(cls, '__slots__') for attr in cls.__slots__] + assert "__slots__" in cls.__dict__, f"class '{name}' in {path} doesn't have __slots__" + # if the class slots is a string, then mro_slots() iterates through that string (bad). + assert not isinstance(cls.__slots__, str), f"{name!r}s slots shouldn't be strings" - # We're a bit hacky here to handle cases correctly, where we can't read the parents slots from - # the mro - if '__dict__' not in slots: - try: + # specify if a certain module/class/base class should have dict- + if any(i in included for i in {cls.__module__, name, cls.__base__.__name__}): + assert "__dict__" in get_slots(cls), f"class {name!r} ({path}) has no __dict__" + continue - class Subclass(_class): - __slots__ = ('__dict__',) + assert "__dict__" not in get_slots(cls), f"class '{name}' in {path} has __dict__" - except TypeError as exc: - if '__dict__ slot disallowed: we already got one' in str(exc): - slots.append('__dict__') - else: - raise exc +def get_slots(_class): + slots = [attr for cls in _class.__mro__ if hasattr(cls, "__slots__") for attr in cls.__slots__] return slots diff --git a/tests/test_sticker.py b/tests/test_sticker.py index bb614b939e5..d6656541fc5 100644 --- a/tests/test_sticker.py +++ b/tests/test_sticker.py @@ -28,29 +28,29 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def sticker_file(): - f = open('tests/data/telegram.webp', 'rb') + f = open("tests/data/telegram.webp", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def sticker(bot, chat_id): - with open('tests/data/telegram.webp', 'rb') as f: + with open("tests/data/telegram.webp", "rb") as f: return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def animated_sticker_file(): - f = open('tests/data/telegram_animated_sticker.tgs', 'rb') + f = open("tests/data/telegram_animated_sticker.tgs", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def animated_sticker(bot, chat_id): - with open('tests/data/telegram_animated_sticker.tgs', 'rb') as f: + with open("tests/data/telegram_animated_sticker.tgs", "rb") as f: return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker @@ -58,11 +58,11 @@ class TestSticker: # sticker_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.webp' # Serving sticker from gh since our server sends wrong content_type sticker_file_url = ( - 'https://github.com/python-telegram-bot/python-telegram-bot/blob/master' - '/tests/data/telegram.webp?raw=true' + "https://github.com/python-telegram-bot/python-telegram-bot/blob/master" + "/tests/data/telegram.webp?raw=true" ) - emoji = 'πŸ’ͺ' + emoji = "πŸ’ͺ" width = 510 height = 512 is_animated = False @@ -71,29 +71,26 @@ class TestSticker: thumb_height = 320 thumb_file_size = 21472 - sticker_file_id = '5a3128a4d2a04750b5b58397f3b5e812' - sticker_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + sticker_file_id = "5a3128a4d2a04750b5b58397f3b5e812" + sticker_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" def test_slot_behaviour(self, sticker, mro_slots, recwarn): for attr in sticker.__slots__: - assert getattr(sticker, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not sticker.__dict__, f"got missing slot(s): {sticker.__dict__}" + assert getattr(sticker, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(sticker)) == len(set(mro_slots(sticker))), "duplicate slot" - sticker.custom, sticker.emoji = 'should give warning', self.emoji - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, sticker): # Make sure file has been uploaded. assert isinstance(sticker, Sticker) assert isinstance(sticker.file_id, str) assert isinstance(sticker.file_unique_id, str) - assert sticker.file_id != '' - assert sticker.file_unique_id != '' + assert sticker.file_id != "" + assert sticker.file_unique_id != "" assert isinstance(sticker.thumb, PhotoSize) assert isinstance(sticker.thumb.file_id, str) assert isinstance(sticker.thumb.file_unique_id, str) - assert sticker.thumb.file_id != '' - assert sticker.thumb.file_unique_id != '' + assert sticker.thumb.file_id != "" + assert sticker.thumb.file_unique_id != "" def test_expected_values(self, sticker): assert sticker.width == self.width @@ -111,8 +108,8 @@ def test_send_all_args(self, bot, chat_id, sticker_file, sticker): assert isinstance(message.sticker, Sticker) assert isinstance(message.sticker.file_id, str) assert isinstance(message.sticker.file_unique_id, str) - assert message.sticker.file_id != '' - assert message.sticker.file_unique_id != '' + assert message.sticker.file_id != "" + assert message.sticker.file_unique_id != "" assert message.sticker.width == sticker.width assert message.sticker.height == sticker.height assert message.sticker.is_animated == sticker.is_animated @@ -121,8 +118,8 @@ def test_send_all_args(self, bot, chat_id, sticker_file, sticker): assert isinstance(message.sticker.thumb, PhotoSize) assert isinstance(message.sticker.thumb.file_id, str) assert isinstance(message.sticker.thumb.file_unique_id, str) - assert message.sticker.thumb.file_id != '' - assert message.sticker.thumb.file_unique_id != '' + assert message.sticker.thumb.file_id != "" + assert message.sticker.thumb.file_unique_id != "" assert message.sticker.thumb.width == sticker.thumb.width assert message.sticker.thumb.height == sticker.thumb.height assert message.sticker.thumb.file_size == sticker.thumb.file_size @@ -134,11 +131,11 @@ def test_get_and_download(self, bot, sticker): assert new_file.file_size == sticker.file_size assert new_file.file_id == sticker.file_id assert new_file.file_unique_id == sticker.file_unique_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.webp') + new_file.download("telegram.webp") - assert os.path.isfile('telegram.webp') + assert os.path.isfile("telegram.webp") @flaky(3, 1) def test_resend(self, bot, chat_id, sticker): @@ -148,7 +145,7 @@ def test_resend(self, bot, chat_id, sticker): @flaky(3, 1) def test_send_on_server_emoji(self, bot, chat_id): - server_file_id = 'CAADAQADHAADyIsGAAFZfq1bphjqlgI' + server_file_id = "CAADAQADHAADyIsGAAFZfq1bphjqlgI" message = bot.send_sticker(chat_id=chat_id, sticker=server_file_id) sticker = message.sticker assert sticker.emoji == self.emoji @@ -161,8 +158,8 @@ def test_send_from_url(self, bot, chat_id): assert isinstance(message.sticker, Sticker) assert isinstance(message.sticker.file_id, str) assert isinstance(message.sticker.file_unique_id, str) - assert message.sticker.file_id != '' - assert message.sticker.file_unique_id != '' + assert message.sticker.file_id != "" + assert message.sticker.file_unique_id != "" assert message.sticker.width == sticker.width assert message.sticker.height == sticker.height assert message.sticker.is_animated == sticker.is_animated @@ -171,22 +168,22 @@ def test_send_from_url(self, bot, chat_id): assert isinstance(message.sticker.thumb, PhotoSize) assert isinstance(message.sticker.thumb.file_id, str) assert isinstance(message.sticker.thumb.file_unique_id, str) - assert message.sticker.thumb.file_id != '' - assert message.sticker.thumb.file_unique_id != '' + assert message.sticker.thumb.file_id != "" + assert message.sticker.thumb.file_unique_id != "" assert message.sticker.thumb.width == sticker.thumb.width assert message.sticker.thumb.height == sticker.thumb.height assert message.sticker.thumb.file_size == sticker.thumb.file_size def test_de_json(self, bot, sticker): json_dict = { - 'file_id': self.sticker_file_id, - 'file_unique_id': self.sticker_file_unique_id, - 'width': self.width, - 'height': self.height, - 'is_animated': self.is_animated, - 'thumb': sticker.thumb.to_dict(), - 'emoji': self.emoji, - 'file_size': self.file_size, + "file_id": self.sticker_file_id, + "file_unique_id": self.sticker_file_unique_id, + "width": self.width, + "height": self.height, + "is_animated": self.is_animated, + "thumb": sticker.thumb.to_dict(), + "emoji": self.emoji, + "file_size": self.file_size, } json_sticker = Sticker.de_json(json_dict, bot) @@ -201,41 +198,41 @@ def test_de_json(self, bot, sticker): def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker): def test(url, data, **kwargs): - return data['sticker'] == sticker.file_id + return data["sticker"] == sticker.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_sticker(sticker=sticker, chat_id=chat_id) assert message def test_send_sticker_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('sticker') == expected + test_flag = data.get("sticker") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_sticker(chat_id, file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_sticker_default_allow_sending_without_reply( self, default_bot, chat_id, sticker, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_sticker( @@ -251,7 +248,7 @@ def test_send_sticker_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_sticker( chat_id, sticker, reply_to_message_id=reply_to_message.message_id ) @@ -260,23 +257,23 @@ def test_to_dict(self, sticker): sticker_dict = sticker.to_dict() assert isinstance(sticker_dict, dict) - assert sticker_dict['file_id'] == sticker.file_id - assert sticker_dict['file_unique_id'] == sticker.file_unique_id - assert sticker_dict['width'] == sticker.width - assert sticker_dict['height'] == sticker.height - assert sticker_dict['is_animated'] == sticker.is_animated - assert sticker_dict['file_size'] == sticker.file_size - assert sticker_dict['thumb'] == sticker.thumb.to_dict() + assert sticker_dict["file_id"] == sticker.file_id + assert sticker_dict["file_unique_id"] == sticker.file_unique_id + assert sticker_dict["width"] == sticker.width + assert sticker_dict["height"] == sticker.height + assert sticker_dict["is_animated"] == sticker.is_animated + assert sticker_dict["file_size"] == sticker.file_size + assert sticker_dict["thumb"] == sticker.thumb.to_dict() @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_sticker(chat_id, open(os.devnull, 'rb')) + bot.send_sticker(chat_id, open(os.devnull, "rb")) @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_sticker(chat_id, '') + bot.send_sticker(chat_id, "") def test_error_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -286,9 +283,9 @@ def test_equality(self, sticker): a = Sticker( sticker.file_id, sticker.file_unique_id, self.width, self.height, self.is_animated ) - b = Sticker('', sticker.file_unique_id, self.width, self.height, self.is_animated) + b = Sticker("", sticker.file_unique_id, self.width, self.height, self.is_animated) c = Sticker(sticker.file_id, sticker.file_unique_id, 0, 0, False) - d = Sticker('', '', self.width, self.height, self.is_animated) + d = Sticker("", "", self.width, self.height, self.is_animated) e = PhotoSize( sticker.file_id, sticker.file_unique_id, self.width, self.height, self.is_animated ) @@ -307,57 +304,57 @@ def test_equality(self, sticker): assert hash(a) != hash(e) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def sticker_set(bot): - ss = bot.get_sticker_set(f'test_by_{bot.username}') + ss = bot.get_sticker_set(f"test_by_{bot.username}") if len(ss.stickers) > 100: try: for i in range(1, 50): bot.delete_sticker_from_set(ss.stickers[-i].file_id) except BadRequest as e: - if e.message == 'Stickerset_not_modified': + if e.message == "Stickerset_not_modified": return ss - raise Exception('stickerset is growing too large.') + raise Exception("stickerset is growing too large.") return ss -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def animated_sticker_set(bot): - ss = bot.get_sticker_set(f'animated_test_by_{bot.username}') + ss = bot.get_sticker_set(f"animated_test_by_{bot.username}") if len(ss.stickers) > 100: try: for i in range(1, 50): bot.delete_sticker_from_set(ss.stickers[-i].file_id) except BadRequest as e: - if e.message == 'Stickerset_not_modified': + if e.message == "Stickerset_not_modified": return ss - raise Exception('stickerset is growing too large.') + raise Exception("stickerset is growing too large.") return ss -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def sticker_set_thumb_file(): - f = open('tests/data/sticker_set_thumb.png', 'rb') + f = open("tests/data/sticker_set_thumb.png", "rb") yield f f.close() class TestStickerSet: - title = 'Test stickers' + title = "Test stickers" is_animated = True contains_masks = False - stickers = [Sticker('file_id', 'file_un_id', 512, 512, True)] - name = 'NOTAREALNAME' + stickers = [Sticker("file_id", "file_un_id", 512, 512, True)] + name = "NOTAREALNAME" def test_de_json(self, bot, sticker): - name = f'test_by_{bot.username}' + name = f"test_by_{bot.username}" json_dict = { - 'name': name, - 'title': self.title, - 'is_animated': self.is_animated, - 'contains_masks': self.contains_masks, - 'stickers': [x.to_dict() for x in self.stickers], - 'thumb': sticker.thumb.to_dict(), + "name": name, + "title": self.title, + "is_animated": self.is_animated, + "contains_masks": self.contains_masks, + "stickers": [x.to_dict() for x in self.stickers], + "thumb": sticker.thumb.to_dict(), } sticker_set = StickerSet.de_json(json_dict, bot) @@ -370,18 +367,18 @@ def test_de_json(self, bot, sticker): @flaky(3, 1) def test_bot_methods_1_png(self, bot, chat_id, sticker_file): - with open('tests/data/telegram_sticker.png', 'rb') as f: + with open("tests/data/telegram_sticker.png", "rb") as f: file = bot.upload_sticker_file(95205500, f) assert file assert bot.add_sticker_to_set( - chat_id, f'test_by_{bot.username}', png_sticker=file.file_id, emojis='πŸ˜„' + chat_id, f"test_by_{bot.username}", png_sticker=file.file_id, emojis="πŸ˜„" ) # Also test with file input and mask assert bot.add_sticker_to_set( chat_id, - f'test_by_{bot.username}', + f"test_by_{bot.username}", png_sticker=sticker_file, - emojis='πŸ˜„', + emojis="πŸ˜„", mask_position=MaskPosition(MaskPosition.EYES, -1, 1, 2), ) @@ -389,20 +386,20 @@ def test_bot_methods_1_png(self, bot, chat_id, sticker_file): def test_bot_methods_1_tgs(self, bot, chat_id): assert bot.add_sticker_to_set( chat_id, - f'animated_test_by_{bot.username}', - tgs_sticker=open('tests/data/telegram_animated_sticker.tgs', 'rb'), - emojis='πŸ˜„', + f"animated_test_by_{bot.username}", + tgs_sticker=open("tests/data/telegram_animated_sticker.tgs", "rb"), + emojis="πŸ˜„", ) def test_sticker_set_to_dict(self, sticker_set): sticker_set_dict = sticker_set.to_dict() assert isinstance(sticker_set_dict, dict) - assert sticker_set_dict['name'] == sticker_set.name - assert sticker_set_dict['title'] == sticker_set.title - assert sticker_set_dict['is_animated'] == sticker_set.is_animated - assert sticker_set_dict['contains_masks'] == sticker_set.contains_masks - assert sticker_set_dict['stickers'][0] == sticker_set.stickers[0].to_dict() + assert sticker_set_dict["name"] == sticker_set.name + assert sticker_set_dict["title"] == sticker_set.title + assert sticker_set_dict["is_animated"] == sticker_set.is_animated + assert sticker_set_dict["contains_masks"] == sticker_set.contains_masks + assert sticker_set_dict["stickers"][0] == sticker_set.stickers[0].to_dict() @flaky(3, 1) def test_bot_methods_2_png(self, bot, sticker_set): @@ -418,18 +415,18 @@ def test_bot_methods_2_tgs(self, bot, animated_sticker_set): def test_bot_methods_3_png(self, bot, chat_id, sticker_set_thumb_file): sleep(1) assert bot.set_sticker_set_thumb( - f'test_by_{bot.username}', chat_id, sticker_set_thumb_file + f"test_by_{bot.username}", chat_id, sticker_set_thumb_file ) @flaky(10, 1) def test_bot_methods_3_tgs(self, bot, chat_id, animated_sticker_file, animated_sticker_set): sleep(1) assert bot.set_sticker_set_thumb( - f'animated_test_by_{bot.username}', chat_id, animated_sticker_file + f"animated_test_by_{bot.username}", chat_id, animated_sticker_file ) file_id = animated_sticker_set.stickers[-1].file_id # also test with file input and mask - assert bot.set_sticker_set_thumb(f'animated_test_by_{bot.username}', chat_id, file_id) + assert bot.set_sticker_set_thumb(f"animated_test_by_{bot.username}", chat_id, file_id) @flaky(10, 1) def test_bot_methods_4_png(self, bot, sticker_set): @@ -446,82 +443,82 @@ def test_bot_methods_4_tgs(self, bot, animated_sticker_set): def test_upload_sticker_file_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('png_sticker') == expected + test_flag = data.get("png_sticker") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.upload_sticker_file(chat_id, file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") def test_create_new_sticker_set_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('png_sticker') == expected and data.get('tgs_sticker') == expected + test_flag = data.get("png_sticker") == expected and data.get("tgs_sticker") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.create_new_sticker_set( - chat_id, 'name', 'title', 'emoji', png_sticker=file, tgs_sticker=file + chat_id, "name", "title", "emoji", png_sticker=file, tgs_sticker=file ) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") def test_add_sticker_to_set_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('png_sticker') == expected and data.get('tgs_sticker') == expected + test_flag = data.get("png_sticker") == expected and data.get("tgs_sticker") == expected - monkeypatch.setattr(bot, '_post', make_assertion) - bot.add_sticker_to_set(chat_id, 'name', 'emoji', png_sticker=file, tgs_sticker=file) + monkeypatch.setattr(bot, "_post", make_assertion) + bot.add_sticker_to_set(chat_id, "name", "emoji", png_sticker=file, tgs_sticker=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") def test_set_sticker_set_thumb_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('thumb') == expected + test_flag = data.get("thumb") == expected - monkeypatch.setattr(bot, '_post', make_assertion) - bot.set_sticker_set_thumb('name', chat_id, thumb=file) + monkeypatch.setattr(bot, "_post", make_assertion) + bot.set_sticker_set_thumb("name", chat_id, thumb=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") def test_get_file_instance_method(self, monkeypatch, sticker): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == sticker.file_id + return kwargs["file_id"] == sticker.file_id - assert check_shortcut_signature(Sticker.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(sticker.get_file, sticker.bot, 'get_file') + assert check_shortcut_signature(Sticker.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(sticker.get_file, sticker.bot, "get_file") assert check_defaults_handling(sticker.get_file, sticker.bot) - monkeypatch.setattr(sticker.bot, 'get_file', make_assertion) + monkeypatch.setattr(sticker.bot, "get_file", make_assertion) assert sticker.get_file() def test_equality(self): a = StickerSet(self.name, self.title, self.is_animated, self.contains_masks, self.stickers) b = StickerSet(self.name, self.title, self.is_animated, self.contains_masks, self.stickers) c = StickerSet(self.name, None, None, None, None) - d = StickerSet('blah', self.title, self.is_animated, self.contains_masks, self.stickers) - e = Audio(self.name, '', 0, None, None) + d = StickerSet("blah", self.title, self.is_animated, self.contains_masks, self.stickers) + e = Audio(self.name, "", 0, None, None) assert a == b assert hash(a) == hash(b) @@ -537,7 +534,7 @@ def test_equality(self): assert hash(a) != hash(e) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def mask_position(): return MaskPosition( TestMaskPosition.point, @@ -555,10 +552,10 @@ class TestMaskPosition: def test_mask_position_de_json(self, bot): json_dict = { - 'point': self.point, - 'x_shift': self.x_shift, - 'y_shift': self.y_shift, - 'scale': self.scale, + "point": self.point, + "x_shift": self.x_shift, + "y_shift": self.y_shift, + "scale": self.scale, } mask_position = MaskPosition.de_json(json_dict, bot) @@ -571,17 +568,17 @@ def test_mask_position_to_dict(self, mask_position): mask_position_dict = mask_position.to_dict() assert isinstance(mask_position_dict, dict) - assert mask_position_dict['point'] == mask_position.point - assert mask_position_dict['x_shift'] == mask_position.x_shift - assert mask_position_dict['y_shift'] == mask_position.y_shift - assert mask_position_dict['scale'] == mask_position.scale + assert mask_position_dict["point"] == mask_position.point + assert mask_position_dict["x_shift"] == mask_position.x_shift + assert mask_position_dict["y_shift"] == mask_position.y_shift + assert mask_position_dict["scale"] == mask_position.scale def test_equality(self): a = MaskPosition(self.point, self.x_shift, self.y_shift, self.scale) b = MaskPosition(self.point, self.x_shift, self.y_shift, self.scale) c = MaskPosition(MaskPosition.FOREHEAD, self.x_shift, self.y_shift, self.scale) d = MaskPosition(self.point, 0, 0, self.scale) - e = Audio('', '', 0, None, None) + e = Audio("", "", 0, None, None) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_stringcommandhandler.py b/tests/test_stringcommandhandler.py index 1fd7ea04881..08aa0657142 100644 --- a/tests/test_stringcommandhandler.py +++ b/tests/test_stringcommandhandler.py @@ -34,36 +34,36 @@ ) from telegram.ext import StringCommandHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "inline_query", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=1, **request.param) @@ -71,36 +71,16 @@ def false_update(request): class TestStringCommandHandler: test_flag = False - def test_slot_behaviour(self, recwarn, mro_slots): - inst = StringCommandHandler('sleepy', self.callback_basic) + def test_slot_behaviour(self, mro_slots): + inst = StringCommandHandler("sleepy", self.callback_context) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, str) - self.test_flag = test_bot and test_update - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - - def sch_callback_args(self, bot, update, args): - if update == '/test': - self.test_flag = len(args) == 0 - else: - self.test_flag = args == ['one', 'two'] - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -114,77 +94,25 @@ def callback_context(self, update, context): ) def callback_context_args(self, update, context): - self.test_flag = context.args == ['one', 'two'] - - def test_basic(self, dp): - handler = StringCommandHandler('test', self.callback_basic) - dp.add_handler(handler) - - check = handler.check_update('/test') - assert check is not None and check is not False - dp.process_update('/test') - assert self.test_flag - - check = handler.check_update('/nottest') - assert check is None or check is False - check = handler.check_update('not /test in front') - assert check is None or check is False - check = handler.check_update('/test followed by text') - assert check is not None and check is not False - - def test_pass_args(self, dp): - handler = StringCommandHandler('test', self.sch_callback_args, pass_args=True) - dp.add_handler(handler) - - dp.process_update('/test') - assert self.test_flag - - self.test_flag = False - dp.process_update('/test one two') - assert self.test_flag - - def test_pass_job_or_update_queue(self, dp): - handler = StringCommandHandler('test', self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update('/test') - assert self.test_flag - - dp.remove_handler(handler) - handler = StringCommandHandler('test', self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update('/test') - assert self.test_flag - - dp.remove_handler(handler) - handler = StringCommandHandler( - 'test', self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update('/test') - assert self.test_flag + self.test_flag = context.args == ["one", "two"] def test_other_update_types(self, false_update): - handler = StringCommandHandler('test', self.callback_basic) + handler = StringCommandHandler("test", self.callback_context) assert not handler.check_update(false_update) - def test_context(self, cdp): - handler = StringCommandHandler('test', self.callback_context) - cdp.add_handler(handler) + def test_context(self, dp): + handler = StringCommandHandler("test", self.callback_context) + dp.add_handler(handler) - cdp.process_update('/test') + dp.process_update("/test") assert self.test_flag - def test_context_args(self, cdp): - handler = StringCommandHandler('test', self.callback_context_args) - cdp.add_handler(handler) + def test_context_args(self, dp): + handler = StringCommandHandler("test", self.callback_context_args) + dp.add_handler(handler) - cdp.process_update('/test') + dp.process_update("/test") assert not self.test_flag - cdp.process_update('/test one two') + dp.process_update("/test one two") assert self.test_flag diff --git a/tests/test_stringregexhandler.py b/tests/test_stringregexhandler.py index 160514c4e8c..1c166a72aed 100644 --- a/tests/test_stringregexhandler.py +++ b/tests/test_stringregexhandler.py @@ -34,36 +34,36 @@ ) from telegram.ext import StringRegexHandler, CallbackContext, JobQueue -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, ] ids = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'callback_query_without_message', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "inline_query", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "callback_query_without_message", ) -@pytest.fixture(scope='class', params=params, ids=ids) +@pytest.fixture(scope="class", params=params, ids=ids) def false_update(request): return Update(update_id=1, **request.param) @@ -71,36 +71,16 @@ def false_update(request): class TestStringRegexHandler: test_flag = False - def test_slot_behaviour(self, mro_slots, recwarn): - inst = StringRegexHandler('pfft', self.callback_basic) + def test_slot_behaviour(self, mro_slots): + inst = StringRegexHandler("pfft", self.callback_context) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, str) - self.test_flag = test_bot and test_update - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - - def callback_group(self, bot, update, groups=None, groupdict=None): - if groups is not None: - self.test_flag = groups == ('t', ' message') - if groupdict is not None: - self.test_flag = groupdict == {'begin': 't', 'end': ' message'} - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -112,85 +92,41 @@ def callback_context(self, update, context): def callback_context_pattern(self, update, context): if context.matches[0].groups(): - self.test_flag = context.matches[0].groups() == ('t', ' message') + self.test_flag = context.matches[0].groups() == ("t", " message") if context.matches[0].groupdict(): - self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' message'} + self.test_flag = context.matches[0].groupdict() == {"begin": "t", "end": " message"} def test_basic(self, dp): - handler = StringRegexHandler('(?P.*)est(?P.*)', self.callback_basic) + handler = StringRegexHandler("(?P.*)est(?P.*)", self.callback_context) dp.add_handler(handler) - assert handler.check_update('test message') - dp.process_update('test message') + assert handler.check_update("test message") + dp.process_update("test message") assert self.test_flag - assert not handler.check_update('does not match') + assert not handler.check_update("does not match") - def test_with_passing_group_dict(self, dp): - handler = StringRegexHandler( - '(?P.*)est(?P.*)', self.callback_group, pass_groups=True - ) - dp.add_handler(handler) - - dp.process_update('test message') - assert self.test_flag + def test_other_update_types(self, false_update): + handler = StringRegexHandler("test", self.callback_context) + assert not handler.check_update(false_update) - dp.remove_handler(handler) - handler = StringRegexHandler( - '(?P.*)est(?P.*)', self.callback_group, pass_groupdict=True - ) + def test_context(self, dp): + handler = StringRegexHandler(r"(t)est(.*)", self.callback_context) dp.add_handler(handler) - self.test_flag = False - dp.process_update('test message') + dp.process_update("test message") assert self.test_flag - def test_pass_job_or_update_queue(self, dp): - handler = StringRegexHandler('test', self.callback_queue_1, pass_job_queue=True) + def test_context_pattern(self, dp): + handler = StringRegexHandler(r"(t)est(.*)", self.callback_context_pattern) dp.add_handler(handler) - dp.process_update('test') + dp.process_update("test message") assert self.test_flag dp.remove_handler(handler) - handler = StringRegexHandler('test', self.callback_queue_1, pass_update_queue=True) + handler = StringRegexHandler(r"(t)est(.*)", self.callback_context_pattern) dp.add_handler(handler) - self.test_flag = False - dp.process_update('test') - assert self.test_flag - - dp.remove_handler(handler) - handler = StringRegexHandler( - 'test', self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update('test') - assert self.test_flag - - def test_other_update_types(self, false_update): - handler = StringRegexHandler('test', self.callback_basic) - assert not handler.check_update(false_update) - - def test_context(self, cdp): - handler = StringRegexHandler(r'(t)est(.*)', self.callback_context) - cdp.add_handler(handler) - - cdp.process_update('test message') - assert self.test_flag - - def test_context_pattern(self, cdp): - handler = StringRegexHandler(r'(t)est(.*)', self.callback_context_pattern) - cdp.add_handler(handler) - - cdp.process_update('test message') - assert self.test_flag - - cdp.remove_handler(handler) - handler = StringRegexHandler(r'(t)est(.*)', self.callback_context_pattern) - cdp.add_handler(handler) - - cdp.process_update('test message') + dp.process_update("test message") assert self.test_flag diff --git a/tests/test_successfulpayment.py b/tests/test_successfulpayment.py index 471f695587b..778c165b2b7 100644 --- a/tests/test_successfulpayment.py +++ b/tests/test_successfulpayment.py @@ -21,7 +21,7 @@ from telegram import OrderInfo, SuccessfulPayment -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def successful_payment(): return SuccessfulPayment( TestSuccessfulPayment.currency, @@ -35,32 +35,29 @@ def successful_payment(): class TestSuccessfulPayment: - invoice_payload = 'invoice_payload' - shipping_option_id = 'shipping_option_id' - currency = 'EUR' + invoice_payload = "invoice_payload" + shipping_option_id = "shipping_option_id" + currency = "EUR" total_amount = 100 order_info = OrderInfo() - telegram_payment_charge_id = 'telegram_payment_charge_id' - provider_payment_charge_id = 'provider_payment_charge_id' + telegram_payment_charge_id = "telegram_payment_charge_id" + provider_payment_charge_id = "provider_payment_charge_id" - def test_slot_behaviour(self, successful_payment, recwarn, mro_slots): + def test_slot_behaviour(self, successful_payment, mro_slots): inst = successful_payment for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.currency = 'should give warning', self.currency - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'invoice_payload': self.invoice_payload, - 'shipping_option_id': self.shipping_option_id, - 'currency': self.currency, - 'total_amount': self.total_amount, - 'order_info': self.order_info.to_dict(), - 'telegram_payment_charge_id': self.telegram_payment_charge_id, - 'provider_payment_charge_id': self.provider_payment_charge_id, + "invoice_payload": self.invoice_payload, + "shipping_option_id": self.shipping_option_id, + "currency": self.currency, + "total_amount": self.total_amount, + "order_info": self.order_info.to_dict(), + "telegram_payment_charge_id": self.telegram_payment_charge_id, + "provider_payment_charge_id": self.provider_payment_charge_id, } successful_payment = SuccessfulPayment.de_json(json_dict, bot) @@ -75,18 +72,18 @@ def test_to_dict(self, successful_payment): successful_payment_dict = successful_payment.to_dict() assert isinstance(successful_payment_dict, dict) - assert successful_payment_dict['invoice_payload'] == successful_payment.invoice_payload + assert successful_payment_dict["invoice_payload"] == successful_payment.invoice_payload assert ( - successful_payment_dict['shipping_option_id'] == successful_payment.shipping_option_id + successful_payment_dict["shipping_option_id"] == successful_payment.shipping_option_id ) - assert successful_payment_dict['currency'] == successful_payment.currency - assert successful_payment_dict['order_info'] == successful_payment.order_info.to_dict() + assert successful_payment_dict["currency"] == successful_payment.currency + assert successful_payment_dict["order_info"] == successful_payment.order_info.to_dict() assert ( - successful_payment_dict['telegram_payment_charge_id'] + successful_payment_dict["telegram_payment_charge_id"] == successful_payment.telegram_payment_charge_id ) assert ( - successful_payment_dict['provider_payment_charge_id'] + successful_payment_dict["provider_payment_charge_id"] == successful_payment.provider_payment_charge_id ) @@ -106,14 +103,14 @@ def test_equality(self): self.provider_payment_charge_id, ) c = SuccessfulPayment( - '', 0, '', self.telegram_payment_charge_id, self.provider_payment_charge_id + "", 0, "", self.telegram_payment_charge_id, self.provider_payment_charge_id ) d = SuccessfulPayment( self.currency, self.total_amount, self.invoice_payload, self.telegram_payment_charge_id, - '', + "", ) assert a == b diff --git a/tests/test_telegramobject.py b/tests/test_telegramobject.py index 96ae1bd3edc..a504348eaa8 100644 --- a/tests/test_telegramobject.py +++ b/tests/test_telegramobject.py @@ -32,13 +32,13 @@ class TestTelegramObject: def test_to_json_native(self, monkeypatch): if ujson: - monkeypatch.setattr('ujson.dumps', json_lib.dumps) + monkeypatch.setattr("ujson.dumps", json_lib.dumps) # to_json simply takes whatever comes from to_dict, therefore we only need to test it once telegram_object = TelegramObject() # Test that it works with a dict with str keys as well as dicts as lists as values - d = {'str': 'str', 'str2': ['str', 'str'], 'str3': {'str': 'str'}} - monkeypatch.setattr('telegram.TelegramObject.to_dict', lambda _: d) + d = {"str": "str", "str2": ["str", "str"], "str3": {"str": "str"}} + monkeypatch.setattr("telegram.TelegramObject.to_dict", lambda _: d) json = telegram_object.to_json() # Order isn't guarantied assert '"str": "str"' in json @@ -47,20 +47,20 @@ def test_to_json_native(self, monkeypatch): # Now make sure that it doesn't work with not json stuff and that it fails loudly # Tuples aren't allowed as keys in json - d = {('str', 'str'): 'str'} + d = {("str", "str"): "str"} - monkeypatch.setattr('telegram.TelegramObject.to_dict', lambda _: d) + monkeypatch.setattr("telegram.TelegramObject.to_dict", lambda _: d) with pytest.raises(TypeError): telegram_object.to_json() - @pytest.mark.skipif(not ujson, reason='ujson not installed') + @pytest.mark.skipif(not ujson, reason="ujson not installed") def test_to_json_ujson(self, monkeypatch): # to_json simply takes whatever comes from to_dict, therefore we only need to test it once telegram_object = TelegramObject() # Test that it works with a dict with str keys as well as dicts as lists as values - d = {'str': 'str', 'str2': ['str', 'str'], 'str3': {'str': 'str'}} - monkeypatch.setattr('telegram.TelegramObject.to_dict', lambda _: d) + d = {"str": "str", "str2": ["str", "str"], "str3": {"str": "str"}} + monkeypatch.setattr("telegram.TelegramObject.to_dict", lambda _: d) json = telegram_object.to_json() # Order isn't guarantied and ujon discards whitespace assert '"str":"str"' in json @@ -70,30 +70,27 @@ def test_to_json_ujson(self, monkeypatch): # Test that ujson allows tuples # NOTE: This could be seen as a bug (since it's differnt from the normal "json", # but we test it anyways - d = {('str', 'str'): 'str'} + d = {("str", "str"): "str"} - monkeypatch.setattr('telegram.TelegramObject.to_dict', lambda _: d) + monkeypatch.setattr("telegram.TelegramObject.to_dict", lambda _: d) telegram_object.to_json() def test_to_dict_private_attribute(self): class TelegramObjectSubclass(TelegramObject): - __slots__ = ('a', '_b') # Added slots so that the attrs are converted to dict + __slots__ = ("a", "_b") # Added slots so that the attrs are converted to dict def __init__(self): self.a = 1 self._b = 2 subclass_instance = TelegramObjectSubclass() - assert subclass_instance.to_dict() == {'a': 1} + assert subclass_instance.to_dict() == {"a": 1} - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): inst = TelegramObject() for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_meaningless_comparison(self, recwarn): expected_warning = "Objects of type TGO can not be meaningfully tested for equivalence." @@ -110,7 +107,8 @@ class TGO(TelegramObject): def test_meaningful_comparison(self, recwarn): class TGO(TelegramObject): - _id_attrs = (1,) + def __init__(self): + self._id_attrs = (1,) a = TGO() b = TGO() diff --git a/tests/test_typehandler.py b/tests/test_typehandler.py index c550dee9fce..474dcc7ca0d 100644 --- a/tests/test_typehandler.py +++ b/tests/test_typehandler.py @@ -28,30 +28,16 @@ class TestTypeHandler: test_flag = False - def test_slot_behaviour(self, mro_slots, recwarn): - inst = TypeHandler(dict, self.callback_basic) + def test_slot_behaviour(self, mro_slots): + inst = TypeHandler(dict, self.callback_context) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.callback = 'should give warning', self.callback_basic - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list @pytest.fixture(autouse=True) def reset(self): self.test_flag = False - def callback_basic(self, bot, update): - test_bot = isinstance(bot, Bot) - test_update = isinstance(update, dict) - self.test_flag = test_bot and test_update - - def callback_queue_1(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) or (update_queue is not None) - - def callback_queue_2(self, bot, update, job_queue=None, update_queue=None): - self.test_flag = (job_queue is not None) and (update_queue is not None) - def callback_context(self, update, context): self.test_flag = ( isinstance(context, CallbackContext) @@ -65,48 +51,23 @@ def callback_context(self, update, context): ) def test_basic(self, dp): - handler = TypeHandler(dict, self.callback_basic) + handler = TypeHandler(dict, self.callback_context) dp.add_handler(handler) - assert handler.check_update({'a': 1, 'b': 2}) - assert not handler.check_update('not a dict') - dp.process_update({'a': 1, 'b': 2}) + assert handler.check_update({"a": 1, "b": 2}) + assert not handler.check_update("not a dict") + dp.process_update({"a": 1, "b": 2}) assert self.test_flag def test_strict(self): - handler = TypeHandler(dict, self.callback_basic, strict=True) - o = OrderedDict({'a': 1, 'b': 2}) - assert handler.check_update({'a': 1, 'b': 2}) + handler = TypeHandler(dict, self.callback_context, strict=True) + o = OrderedDict({"a": 1, "b": 2}) + assert handler.check_update({"a": 1, "b": 2}) assert not handler.check_update(o) - def test_pass_job_or_update_queue(self, dp): - handler = TypeHandler(dict, self.callback_queue_1, pass_job_queue=True) - dp.add_handler(handler) - - dp.process_update({'a': 1, 'b': 2}) - assert self.test_flag - - dp.remove_handler(handler) - handler = TypeHandler(dict, self.callback_queue_1, pass_update_queue=True) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update({'a': 1, 'b': 2}) - assert self.test_flag - - dp.remove_handler(handler) - handler = TypeHandler( - dict, self.callback_queue_2, pass_job_queue=True, pass_update_queue=True - ) - dp.add_handler(handler) - - self.test_flag = False - dp.process_update({'a': 1, 'b': 2}) - assert self.test_flag - - def test_context(self, cdp): + def test_context(self, dp): handler = TypeHandler(dict, self.callback_context) - cdp.add_handler(handler) + dp.add_handler(handler) - cdp.process_update({'a': 1, 'b': 2}) + dp.process_update({"a": 1, "b": 2}) assert self.test_flag diff --git a/tests/test_update.py b/tests/test_update.py index 2777ff00893..5e9a8c9bbe8 100644 --- a/tests/test_update.py +++ b/tests/test_update.py @@ -33,54 +33,54 @@ Poll, PollOption, ChatMemberUpdated, - ChatMember, + ChatMemberOwner, ) from telegram.poll import PollAnswer from telegram.utils.helpers import from_timestamp -message = Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Text') +message = Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Text") chat_member_updated = ChatMemberUpdated( - Chat(1, 'chat'), - User(1, '', False), + Chat(1, "chat"), + User(1, "", False), from_timestamp(int(time.time())), - ChatMember(User(1, '', False), ChatMember.CREATOR), - ChatMember(User(1, '', False), ChatMember.CREATOR), + ChatMemberOwner(User(1, "", False), True), + ChatMemberOwner(User(1, "", False), True), ) params = [ - {'message': message}, - {'edited_message': message}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat', message=message)}, - {'channel_post': message}, - {'edited_channel_post': message}, - {'inline_query': InlineQuery(1, User(1, '', False), '', '')}, - {'chosen_inline_result': ChosenInlineResult('id', User(1, '', False), '')}, - {'shipping_query': ShippingQuery('id', User(1, '', False), '', None)}, - {'pre_checkout_query': PreCheckoutQuery('id', User(1, '', False), '', 0, '')}, - {'callback_query': CallbackQuery(1, User(1, '', False), 'chat')}, - {'poll': Poll('id', '?', [PollOption('.', 1)], False, False, False, Poll.REGULAR, True)}, - {'poll_answer': PollAnswer("id", User(1, '', False), [1])}, - {'my_chat_member': chat_member_updated}, - {'chat_member': chat_member_updated}, + {"message": message}, + {"edited_message": message}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat", message=message)}, + {"channel_post": message}, + {"edited_channel_post": message}, + {"inline_query": InlineQuery(1, User(1, "", False), "", "")}, + {"chosen_inline_result": ChosenInlineResult("id", User(1, "", False), "")}, + {"shipping_query": ShippingQuery("id", User(1, "", False), "", None)}, + {"pre_checkout_query": PreCheckoutQuery("id", User(1, "", False), "", 0, "")}, + {"callback_query": CallbackQuery(1, User(1, "", False), "chat")}, + {"poll": Poll("id", "?", [PollOption(".", 1)], False, False, False, Poll.REGULAR, True)}, + {"poll_answer": PollAnswer("id", User(1, "", False), [1])}, + {"my_chat_member": chat_member_updated}, + {"chat_member": chat_member_updated}, ] all_types = ( - 'message', - 'edited_message', - 'callback_query', - 'channel_post', - 'edited_channel_post', - 'inline_query', - 'chosen_inline_result', - 'shipping_query', - 'pre_checkout_query', - 'poll', - 'poll_answer', - 'my_chat_member', - 'chat_member', + "message", + "edited_message", + "callback_query", + "channel_post", + "edited_channel_post", + "inline_query", + "chosen_inline_result", + "shipping_query", + "pre_checkout_query", + "poll", + "poll_answer", + "my_chat_member", + "chat_member", ) -ids = all_types + ('callback_query_without_message',) +ids = all_types + ("callback_query_without_message",) @pytest.fixture(params=params, ids=ids) @@ -91,17 +91,14 @@ def update(request): class TestUpdate: update_id = 868573637 - def test_slot_behaviour(self, update, recwarn, mro_slots): + def test_slot_behaviour(self, update, mro_slots): for attr in update.__slots__: - assert getattr(update, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not update.__dict__, f"got missing slot(s): {update.__dict__}" + assert getattr(update, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(update)) == len(set(mro_slots(update))), "duplicate slot" - update.custom, update.update_id = 'should give warning', self.update_id - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - @pytest.mark.parametrize('paramdict', argvalues=params, ids=ids) + @pytest.mark.parametrize("paramdict", argvalues=params, ids=ids) def test_de_json(self, bot, paramdict): - json_dict = {'update_id': TestUpdate.update_id} + json_dict = {"update_id": TestUpdate.update_id} # Convert the single update 'item' to a dict of that item and apply it to the json_dict json_dict.update({k: v.to_dict() for k, v in paramdict.items()}) update = Update.de_json(json_dict, bot) @@ -125,7 +122,7 @@ def test_to_dict(self, update): update_dict = update.to_dict() assert isinstance(update_dict, dict) - assert update_dict['update_id'] == update.update_id + 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() @@ -181,7 +178,7 @@ def test_equality(self): b = Update(self.update_id, message=message) c = Update(self.update_id) d = Update(0, message=message) - e = User(self.update_id, '', False) + e = User(self.update_id, "", False) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_updater.py b/tests/test_updater.py index b574319f0f8..123b0d64dd4 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -35,6 +35,7 @@ from urllib.error import HTTPError import pytest +from .conftest import DictBot from telegram import ( TelegramError, @@ -59,7 +60,7 @@ from telegram.ext.utils.webhookhandler import WebhookServer signalskip = pytest.mark.skipif( - sys.platform == 'win32', + sys.platform == "win32", reason="Can't send signals without stopping whole process on windows", ) @@ -90,24 +91,11 @@ class TestUpdater: offset = 0 test_flag = False - def test_slot_behaviour(self, updater, mro_slots, recwarn): + def test_slot_behaviour(self, updater, mro_slots): for at in updater.__slots__: - at = f"_Updater{at}" if at.startswith('__') and not at.endswith('__') else at - assert getattr(updater, at, 'err') != 'err', f"got extra slot '{at}'" - assert not updater.__dict__, f"got missing slot(s): {updater.__dict__}" + at = f"_Updater{at}" if at.startswith("__") and not at.endswith("__") else at + assert getattr(updater, at, "err") != "err", f"got extra slot '{at}'" assert len(mro_slots(updater)) == len(set(mro_slots(updater))), "duplicate slot" - updater.custom, updater.running = 'should give warning', updater.running - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list - - class CustomUpdater(Updater): - pass # Tests that setting custom attributes of Updater subclass doesn't raise warning - - a = CustomUpdater(updater.bot.token) - a.my_custom = 'no error!' - assert len(recwarn) == 1 - - updater.__setattr__('__test', 'mangled success') - assert getattr(updater, '_Updater__test', 'e') == 'mangled success', "mangling failed" @pytest.fixture(autouse=True) def reset(self): @@ -118,30 +106,30 @@ def reset(self): self.cb_handler_called.clear() self.test_flag = False - def error_handler(self, bot, update, error): - self.received = error.message + def error_handler(self, update, context): + self.received = context.error.message self.err_handler_called.set() - def callback(self, bot, update): + def callback(self, update, context): self.received = update.message.text self.cb_handler_called.set() def test_warn_arbitrary_callback_data(self, bot, recwarn): Updater(bot=bot, arbitrary_callback_data=True) assert len(recwarn) == 1 - assert 'Passing arbitrary_callback_data to an Updater' in str(recwarn[0].message) + assert "Passing arbitrary_callback_data to an Updater" in str(recwarn[0].message) @pytest.mark.parametrize( - ('error',), - argvalues=[(TelegramError('Test Error 2'),), (Unauthorized('Test Unauthorized'),)], - ids=('TelegramError', 'Unauthorized'), + ("error",), + argvalues=[(TelegramError("Test Error 2"),), (Unauthorized("Test Unauthorized"),)], + ids=("TelegramError", "Unauthorized"), ) def test_get_updates_normal_err(self, monkeypatch, updater, error): def test(*args, **kwargs): raise error - monkeypatch.setattr(updater.bot, 'get_updates', test) - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "get_updates", test) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) @@ -153,7 +141,7 @@ def test(*args, **kwargs): self.err_handler_called.clear() self.err_handler_called.wait() - @pytest.mark.filterwarnings('ignore:.*:pytest.PytestUnhandledThreadExceptionWarning') + @pytest.mark.filterwarnings("ignore:.*:pytest.PytestUnhandledThreadExceptionWarning") def test_get_updates_bailout_err(self, monkeypatch, updater, caplog): error = InvalidToken() @@ -161,8 +149,8 @@ def test(*args, **kwargs): raise error with caplog.at_level(logging.DEBUG): - monkeypatch.setattr(updater.bot, 'get_updates', test) - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "get_updates", test) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) assert self.err_handler_called.wait(1) is not True @@ -175,14 +163,14 @@ def test(*args, **kwargs): # TODO: We should have a way to poll Updater status and decide if it's running or not. import pprint - pprint.pprint([rec.getMessage() for rec in caplog.get_records('call')]) + pprint.pprint([rec.getMessage() for rec in caplog.get_records("call")]) assert any( - f'unhandled exception in Bot:{updater.bot.id}:updater' in rec.getMessage() - for rec in caplog.get_records('call') + f"unhandled exception in Bot:{updater.bot.id}:updater" in rec.getMessage() + for rec in caplog.get_records("call") ) @pytest.mark.parametrize( - ('error',), argvalues=[(RetryAfter(0.01),), (TimedOut(),)], ids=('RetryAfter', 'TimedOut') + ("error",), argvalues=[(RetryAfter(0.01),), (TimedOut(),)], ids=("RetryAfter", "TimedOut") ) def test_get_updates_retries(self, monkeypatch, updater, error): event = Event() @@ -191,8 +179,8 @@ def test(*args, **kwargs): event.set() raise error - monkeypatch.setattr(updater.bot, 'get_updates', test) - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "get_updates", test) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) updater.dispatcher.add_error_handler(self.error_handler) updater.start_polling(0.01) @@ -206,44 +194,44 @@ def test(*args, **kwargs): event.wait() assert self.err_handler_called.wait(0.5) is not True - @pytest.mark.parametrize('ext_bot', [True, False]) + @pytest.mark.parametrize("ext_bot", [True, False]) def test_webhook(self, monkeypatch, updater, ext_bot): # Testing with both ExtBot and Bot to make sure any logic in WebhookHandler # that depends on this distinction works if ext_bot and not isinstance(updater.bot, ExtBot): updater.bot = ExtBot(updater.bot.token) if not ext_bot and not type(updater.bot) is Bot: - updater.bot = Bot(updater.bot.token) + updater.bot = DictBot(updater.bot.token) q = Queue() - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr("telegram.ext.Dispatcher.process_update", lambda _, u: q.put(u)) - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port - updater.start_webhook(ip, port, url_path='TOKEN') + updater.start_webhook(ip, port, url_path="TOKEN") sleep(0.2) try: # Now, we send an update to the server via urlopen update = Update( 1, message=Message( - 1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook' + 1, None, Chat(1, ""), from_user=User(1, "", False), text="Webhook" ), ) - self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') + self._send_webhook_msg(ip, port, update.to_json(), "TOKEN") sleep(0.2) assert q.get(False) == update # Returns 404 if path is incorrect with pytest.raises(HTTPError) as excinfo: - self._send_webhook_msg(ip, port, None, 'webookhandler.py') + self._send_webhook_msg(ip, port, None, "webookhandler.py") assert excinfo.value.code == 404 with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg( - ip, port, None, 'webookhandler.py', get_method=lambda: 'HEAD' + ip, port, None, "webookhandler.py", get_method=lambda: "HEAD" ) assert excinfo.value.code == 404 @@ -255,25 +243,25 @@ def test_webhook(self, monkeypatch, updater, ext_bot): assert not updater.httpd.is_running updater.stop() - @pytest.mark.parametrize('invalid_data', [True, False]) + @pytest.mark.parametrize("invalid_data", [True, False]) def test_webhook_arbitrary_callback_data(self, monkeypatch, updater, invalid_data): """Here we only test one simple setup. telegram.ext.ExtBot.insert_callback_data is tested extensively in test_bot.py in conjunction with get_updates.""" updater.bot.arbitrary_callback_data = True try: q = Queue() - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr("telegram.ext.Dispatcher.process_update", lambda _, u: q.put(u)) - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port - updater.start_webhook(ip, port, url_path='TOKEN') + updater.start_webhook(ip, port, url_path="TOKEN") sleep(0.2) try: # Now, we send an update to the server via urlopen reply_markup = InlineKeyboardMarkup.from_button( - InlineKeyboardButton(text='text', callback_data='callback_data') + InlineKeyboardButton(text="text", callback_data="callback_data") ) if not invalid_data: reply_markup = updater.bot.callback_data_cache.process_keyboard(reply_markup) @@ -285,7 +273,7 @@ def test_webhook_arbitrary_callback_data(self, monkeypatch, updater, invalid_dat reply_markup=reply_markup, ) update = Update(1, message=message) - self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN') + self._send_webhook_msg(ip, port, update.to_json(), "TOKEN") sleep(0.2) received_update = q.get(False) assert received_update == update @@ -294,7 +282,7 @@ def test_webhook_arbitrary_callback_data(self, monkeypatch, updater, invalid_dat if invalid_data: assert isinstance(button.callback_data, InvalidCallbackData) else: - assert button.callback_data == 'callback_data' + assert button.callback_data == "callback_data" # Test multiple shutdown() calls updater.httpd.shutdown() @@ -309,13 +297,12 @@ def test_webhook_arbitrary_callback_data(self, monkeypatch, updater, invalid_dat updater.bot.callback_data_cache.clear_callback_queries() def test_start_webhook_no_warning_or_error_logs(self, caplog, updater, monkeypatch): - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) # prevent api calls from @info decorator when updater.bot.id is used in thread names - monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) - monkeypatch.setattr(updater.bot, '_commands', []) + monkeypatch.setattr(updater.bot, "_bot", User(id=123, first_name="bot", is_bot=True)) - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port with caplog.at_level(logging.WARNING): updater.start_webhook(ip, port) @@ -323,18 +310,18 @@ def test_start_webhook_no_warning_or_error_logs(self, caplog, updater, monkeypat assert not caplog.records def test_webhook_ssl(self, monkeypatch, updater): - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - ip = '127.0.0.1' + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port tg_err = False try: updater._start_webhook( ip, port, - url_path='TOKEN', - cert='./tests/test_updater.py', - key='./tests/test_updater.py', + url_path="TOKEN", + cert="./tests/test_updater.py", + key="./tests/test_updater.py", bootstrap_retries=0, drop_pending_updates=False, webhook_url=None, @@ -346,11 +333,11 @@ def test_webhook_ssl(self, monkeypatch, updater): def test_webhook_no_ssl(self, monkeypatch, updater): q = Queue() - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) + monkeypatch.setattr(updater.bot, "set_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr("telegram.ext.Dispatcher.process_update", lambda _, u: q.put(u)) - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port updater.start_webhook(ip, port, webhook_url=None) sleep(0.2) @@ -358,7 +345,7 @@ def test_webhook_no_ssl(self, monkeypatch, updater): # Now, we send an update to the server via urlopen update = Update( 1, - message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'), + message=Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Webhook 2"), ) self._send_webhook_msg(ip, port, update.to_json()) sleep(0.2) @@ -369,7 +356,7 @@ def test_webhook_ssl_just_for_telegram(self, monkeypatch, updater): q = Queue() def set_webhook(**kwargs): - self.test_flag.append(bool(kwargs.get('certificate'))) + self.test_flag.append(bool(kwargs.get("certificate"))) return True orig_wh_server_init = WebhookServer.__init__ @@ -378,22 +365,22 @@ def webhook_server_init(*args): self.test_flag = [args[-1] is None] orig_wh_server_init(*args) - monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) + monkeypatch.setattr(updater.bot, "set_webhook", set_webhook) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr("telegram.ext.Dispatcher.process_update", lambda _, u: q.put(u)) monkeypatch.setattr( - 'telegram.ext.utils.webhookhandler.WebhookServer.__init__', webhook_server_init + "telegram.ext.utils.webhookhandler.WebhookServer.__init__", webhook_server_init ) - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port - updater.start_webhook(ip, port, webhook_url=None, cert='./tests/test_updater.py') + updater.start_webhook(ip, port, webhook_url=None, cert="./tests/test_updater.py") sleep(0.2) # Now, we send an update to the server via urlopen update = Update( 1, - message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'), + message=Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Webhook 2"), ) self._send_webhook_msg(ip, port, update.to_json()) sleep(0.2) @@ -401,23 +388,23 @@ def webhook_server_init(*args): updater.stop() assert self.test_flag == [True, True] - @pytest.mark.parametrize('pass_max_connections', [True, False]) + @pytest.mark.parametrize("pass_max_connections", [True, False]) def test_webhook_max_connections(self, monkeypatch, updater, pass_max_connections): q = Queue() max_connections = 42 def set_webhook(**kwargs): print(kwargs) - self.test_flag = kwargs.get('max_connections') == ( + self.test_flag = kwargs.get("max_connections") == ( max_connections if pass_max_connections else 40 ) return True - monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u)) + monkeypatch.setattr(updater.bot, "set_webhook", set_webhook) + monkeypatch.setattr(updater.bot, "delete_webhook", lambda *args, **kwargs: True) + monkeypatch.setattr("telegram.ext.Dispatcher.process_update", lambda _, u: q.put(u)) - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # Select random port if pass_max_connections: updater.start_webhook(ip, port, webhook_url=None, max_connections=max_connections) @@ -429,7 +416,7 @@ def set_webhook(**kwargs): # Now, we send an update to the server via urlopen update = Update( 1, - message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'), + message=Message(1, None, Chat(1, ""), from_user=User(1, "", False), text="Webhook 2"), ) self._send_webhook_msg(ip, port, update.to_json()) sleep(0.2) @@ -437,7 +424,7 @@ def set_webhook(**kwargs): updater.stop() assert self.test_flag is True - @pytest.mark.parametrize(('error',), argvalues=[(TelegramError(''),)], ids=('TelegramError',)) + @pytest.mark.parametrize(("error",), argvalues=[(TelegramError(""),)], ids=("TelegramError",)) def test_bootstrap_retries_success(self, monkeypatch, updater, error): retries = 2 @@ -446,16 +433,16 @@ def attempt(*args, **kwargs): self.attempts += 1 raise error - monkeypatch.setattr(updater.bot, 'set_webhook', attempt) + monkeypatch.setattr(updater.bot, "set_webhook", attempt) updater.running = True - updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) + updater._bootstrap(retries, False, "path", None, bootstrap_interval=0) assert self.attempts == retries @pytest.mark.parametrize( - ('error', 'attempts'), - argvalues=[(TelegramError(''), 2), (Unauthorized(''), 1), (InvalidToken(), 1)], - ids=('TelegramError', 'Unauthorized', 'InvalidToken'), + ("error", "attempts"), + argvalues=[(TelegramError(""), 2), (Unauthorized(""), 1), (InvalidToken(), 1)], + ids=("TelegramError", "Unauthorized", "InvalidToken"), ) def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts): retries = 1 @@ -464,23 +451,23 @@ def attempt(*args, **kwargs): self.attempts += 1 raise error - monkeypatch.setattr(updater.bot, 'set_webhook', attempt) + monkeypatch.setattr(updater.bot, "set_webhook", attempt) updater.running = True with pytest.raises(type(error)): - updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0) + updater._bootstrap(retries, False, "path", None, bootstrap_interval=0) assert self.attempts == attempts - @pytest.mark.parametrize('drop_pending_updates', (True, False)) + @pytest.mark.parametrize("drop_pending_updates", (True, False)) def test_bootstrap_clean_updates(self, monkeypatch, updater, drop_pending_updates): # As dropping pending updates is done by passing `drop_pending_updates` to # set_webhook, we just check that we pass the correct value self.test_flag = False def delete_webhook(**kwargs): - self.test_flag = kwargs.get('drop_pending_updates') == drop_pending_updates + self.test_flag = kwargs.get("drop_pending_updates") == drop_pending_updates - monkeypatch.setattr(updater.bot, 'delete_webhook', delete_webhook) + monkeypatch.setattr(updater.bot, "delete_webhook", delete_webhook) updater.running = True updater._bootstrap( @@ -492,65 +479,12 @@ def delete_webhook(**kwargs): ) assert self.test_flag is True - def test_deprecation_warnings_start_webhook(self, recwarn, updater, monkeypatch): - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - # prevent api calls from @info decorator when updater.bot.id is used in thread names - monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) - monkeypatch.setattr(updater.bot, '_commands', []) - - ip = '127.0.0.1' - port = randrange(1024, 49152) # Select random port - updater.start_webhook(ip, port, clean=True, force_event_loop=False) - updater.stop() - - for warning in recwarn: - print(warning) - - try: # This is for flaky tests (there's an unclosed socket sometimes) - recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it - except AssertionError: - pass - - assert len(recwarn) == 3 - assert str(recwarn[0].message).startswith('Old Handler API') - assert str(recwarn[1].message).startswith('The argument `clean` of') - assert str(recwarn[2].message).startswith('The argument `force_event_loop` of') - - def test_clean_deprecation_warning_polling(self, recwarn, updater, monkeypatch): - monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True) - monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True) - # prevent api calls from @info decorator when updater.bot.id is used in thread names - monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True)) - monkeypatch.setattr(updater.bot, '_commands', []) - - updater.start_polling(clean=True) - updater.stop() - for msg in recwarn: - print(msg) - - try: # This is for flaky tests (there's an unclosed socket sometimes) - recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it - except AssertionError: - pass - - assert len(recwarn) == 2 - assert str(recwarn[0].message).startswith('Old Handler API') - assert str(recwarn[1].message).startswith('The argument `clean` of') - - def test_clean_drop_pending_mutually_exclusive(self, updater): - with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'): - updater.start_polling(clean=True, drop_pending_updates=False) - - with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'): - updater.start_webhook(clean=True, drop_pending_updates=False) - @flaky(3, 1) def test_webhook_invalid_posts(self, updater): - ip = '127.0.0.1' + ip = "127.0.0.1" port = randrange(1024, 49152) # select random port for travis thr = Thread( - target=updater._start_webhook, args=(ip, port, '', None, None, 0, False, None, None) + target=updater._start_webhook, args=(ip, port, "", None, None, 0, False, None, None) ) thr.start() @@ -559,12 +493,12 @@ def test_webhook_invalid_posts(self, updater): try: with pytest.raises(HTTPError) as excinfo: self._send_webhook_msg( - ip, port, 'data', content_type='application/xml' + ip, port, "data", content_type="application/xml" ) assert excinfo.value.code == 403 with pytest.raises(HTTPError) as excinfo: - self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2) + self._send_webhook_msg(ip, port, "dummy-payload", content_len=-2) assert excinfo.value.code == 500 # TODO: prevent urllib or the underlying from adding content-length @@ -573,7 +507,7 @@ def test_webhook_invalid_posts(self, updater): # assert excinfo.value.code == 411 with pytest.raises(HTTPError): - self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number') + self._send_webhook_msg(ip, port, "dummy-payload", content_len="not-a-number") assert excinfo.value.code == 500 finally: @@ -585,28 +519,28 @@ def _send_webhook_msg( ip, port, payload_str, - url_path='', + url_path="", content_len=-1, - content_type='application/json', + content_type="application/json", get_method=None, ): headers = { - 'content-type': content_type, + "content-type": content_type, } if not payload_str: content_len = None payload = None else: - payload = bytes(payload_str, encoding='utf-8') + payload = bytes(payload_str, encoding="utf-8") if content_len == -1: content_len = len(payload) if content_len is not None: - headers['content-length'] = str(content_len) + headers["content-length"] = str(content_len) - url = f'http://{ip}:{port}/{url_path}' + url = f"http://{ip}:{port}/{url_path}" req = Request(url, data=payload, headers=headers) @@ -636,21 +570,21 @@ def test_idle(self, updater, caplog): for idx, log in enumerate(records): print(log) msg = log.getMessage() - if msg.startswith('Error while getting Updates: Conflict'): + if msg.startswith("Error while getting Updates: Conflict"): caplog.records.pop(idx) # For stability - if msg.startswith('No error handlers are registered'): + if msg.startswith("No error handlers are registered"): caplog.records.pop(idx) assert len(caplog.records) == 2, caplog.records rec = caplog.records[-2] - assert rec.getMessage().startswith(f'Received signal {signal.SIGTERM}') - assert rec.levelname == 'INFO' + assert rec.getMessage().startswith(f"Received signal {signal.SIGTERM}") + assert rec.levelname == "INFO" rec = caplog.records[-1] - assert rec.getMessage().startswith('Scheduler has been shut down') - assert rec.levelname == 'INFO' + assert rec.getMessage().startswith("Scheduler has been shut down") + assert rec.levelname == "INFO" # If we get this far, idle() ran through sleep(0.5) @@ -658,10 +592,10 @@ def test_idle(self, updater, caplog): @signalskip def test_user_signal(self, updater): - temp_var = {'a': 0} + temp_var = {"a": 0} def user_signal_inc(signum, frame): - temp_var['a'] = 1 + temp_var["a"] = 1 updater.user_sig_handler = user_signal_inc updater.start_polling(0.01) @@ -670,29 +604,29 @@ def user_signal_inc(signum, frame): # If we get this far, idle() ran through sleep(0.5) assert updater.running is False - assert temp_var['a'] != 0 + assert temp_var["a"] != 0 def test_create_bot(self): - updater = Updater('123:abcd') + updater = Updater("123:abcd") assert updater.bot is not None def test_mutual_exclude_token_bot(self): - bot = Bot('123:zyxw') + bot = Bot("123:zyxw") with pytest.raises(ValueError): - Updater(token='123:abcd', bot=bot) + Updater(token="123:abcd", bot=bot) def test_no_token_or_bot_or_dispatcher(self): with pytest.raises(ValueError): Updater() def test_mutual_exclude_bot_private_key(self): - bot = Bot('123:zyxw') + bot = Bot("123:zyxw") with pytest.raises(ValueError): - Updater(bot=bot, private_key=b'key') + Updater(bot=bot, private_key=b"key") def test_mutual_exclude_bot_dispatcher(self, bot): dispatcher = Dispatcher(bot, None) - bot = Bot('123:zyxw') + bot = Bot("123:zyxw") with pytest.raises(ValueError): Updater(bot=bot, dispatcher=dispatcher) @@ -707,17 +641,11 @@ def test_mutual_exclude_workers_dispatcher(self, bot): with pytest.raises(ValueError): Updater(dispatcher=dispatcher, workers=8) - def test_mutual_exclude_use_context_dispatcher(self, bot): - dispatcher = Dispatcher(bot, None) - use_context = not dispatcher.use_context - with pytest.raises(ValueError): - Updater(dispatcher=dispatcher, use_context=use_context) - def test_mutual_exclude_custom_context_dispatcher(self): dispatcher = Dispatcher(None, None) with pytest.raises(ValueError): Updater(dispatcher=dispatcher, context_types=True) def test_defaults_warning(self, bot): - with pytest.warns(TelegramDeprecationWarning, match='no effect when a Bot is passed'): + with pytest.warns(TelegramDeprecationWarning, match="no effect when a Bot is passed"): Updater(bot=bot, defaults=Defaults()) diff --git a/tests/test_user.py b/tests/test_user.py index 85f75bb8b59..735decc0891 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -23,22 +23,22 @@ from tests.conftest import check_shortcut_signature, check_shortcut_call, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def json_dict(): return { - 'id': TestUser.id_, - 'is_bot': TestUser.is_bot, - 'first_name': TestUser.first_name, - 'last_name': TestUser.last_name, - 'username': TestUser.username, - 'language_code': TestUser.language_code, - 'can_join_groups': TestUser.can_join_groups, - 'can_read_all_group_messages': TestUser.can_read_all_group_messages, - 'supports_inline_queries': TestUser.supports_inline_queries, + "id": TestUser.id_, + "is_bot": TestUser.is_bot, + "first_name": TestUser.first_name, + "last_name": TestUser.last_name, + "username": TestUser.username, + "language_code": TestUser.language_code, + "can_join_groups": TestUser.can_join_groups, + "can_read_all_group_messages": TestUser.can_read_all_group_messages, + "supports_inline_queries": TestUser.supports_inline_queries, } -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def user(bot): return User( id=TestUser.id_, @@ -57,21 +57,18 @@ def user(bot): class TestUser: id_ = 1 is_bot = True - first_name = 'first\u2022name' - last_name = 'last\u2022name' - username = 'username' - language_code = 'en_us' + first_name = "first\u2022name" + last_name = "last\u2022name" + username = "username" + language_code = "en_us" can_join_groups = True can_read_all_group_messages = True supports_inline_queries = False - def test_slot_behaviour(self, user, mro_slots, recwarn): + def test_slot_behaviour(self, user, mro_slots): for attr in user.__slots__: - assert getattr(user, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not user.__dict__, f"got missing slot(s): {user.__dict__}" + assert getattr(user, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(user)) == len(set(mro_slots(user))), "duplicate slot" - user.custom, user.id = 'should give warning', self.id_ - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, json_dict, bot): user = User.de_json(json_dict, bot) @@ -87,7 +84,7 @@ def test_de_json(self, json_dict, bot): assert user.supports_inline_queries == self.supports_inline_queries def test_de_json_without_username(self, json_dict, bot): - del json_dict['username'] + del json_dict["username"] user = User.de_json(json_dict, bot) @@ -102,8 +99,8 @@ def test_de_json_without_username(self, json_dict, bot): assert user.supports_inline_queries == self.supports_inline_queries def test_de_json_without_username_and_last_name(self, json_dict, bot): - del json_dict['username'] - del json_dict['last_name'] + del json_dict["username"] + del json_dict["last_name"] user = User.de_json(json_dict, bot) @@ -118,347 +115,347 @@ def test_de_json_without_username_and_last_name(self, json_dict, bot): assert user.supports_inline_queries == self.supports_inline_queries def test_name(self, user): - assert user.name == '@username' + assert user.name == "@username" user.username = None - assert user.name == 'first\u2022name last\u2022name' + assert user.name == "first\u2022name last\u2022name" user.last_name = None - assert user.name == 'first\u2022name' + assert user.name == "first\u2022name" user.username = self.username - assert user.name == '@username' + assert user.name == "@username" def test_full_name(self, user): - assert user.full_name == 'first\u2022name last\u2022name' + assert user.full_name == "first\u2022name last\u2022name" user.last_name = None - assert user.full_name == 'first\u2022name' + assert user.full_name == "first\u2022name" def test_link(self, user): - assert user.link == f'https://t.me/{user.username}' + assert user.link == f"https://t.me/{user.username}" user.username = None assert user.link is None def test_instance_method_get_profile_photos(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['user_id'] == user.id + return kwargs["user_id"] == user.id assert check_shortcut_signature( - User.get_profile_photos, Bot.get_user_profile_photos, ['user_id'], [] + User.get_profile_photos, Bot.get_user_profile_photos, ["user_id"], [] ) - assert check_shortcut_call(user.get_profile_photos, user.bot, 'get_user_profile_photos') + assert check_shortcut_call(user.get_profile_photos, user.bot, "get_user_profile_photos") assert check_defaults_handling(user.get_profile_photos, user.bot) - monkeypatch.setattr(user.bot, 'get_user_profile_photos', make_assertion) + monkeypatch.setattr(user.bot, "get_user_profile_photos", make_assertion) assert user.get_profile_photos() def test_instance_method_pin_message(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id + return kwargs["chat_id"] == user.id - assert check_shortcut_signature(User.pin_message, Bot.pin_chat_message, ['chat_id'], []) - assert check_shortcut_call(user.pin_message, user.bot, 'pin_chat_message') + assert check_shortcut_signature(User.pin_message, Bot.pin_chat_message, ["chat_id"], []) + assert check_shortcut_call(user.pin_message, user.bot, "pin_chat_message") assert check_defaults_handling(user.pin_message, user.bot) - monkeypatch.setattr(user.bot, 'pin_chat_message', make_assertion) + monkeypatch.setattr(user.bot, "pin_chat_message", make_assertion) assert user.pin_message(1) def test_instance_method_unpin_message(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id + return kwargs["chat_id"] == user.id assert check_shortcut_signature( - User.unpin_message, Bot.unpin_chat_message, ['chat_id'], [] + User.unpin_message, Bot.unpin_chat_message, ["chat_id"], [] ) - assert check_shortcut_call(user.unpin_message, user.bot, 'unpin_chat_message') + assert check_shortcut_call(user.unpin_message, user.bot, "unpin_chat_message") assert check_defaults_handling(user.unpin_message, user.bot) - monkeypatch.setattr(user.bot, 'unpin_chat_message', make_assertion) + monkeypatch.setattr(user.bot, "unpin_chat_message", make_assertion) assert user.unpin_message() def test_instance_method_unpin_all_messages(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id + return kwargs["chat_id"] == user.id assert check_shortcut_signature( - User.unpin_all_messages, Bot.unpin_all_chat_messages, ['chat_id'], [] + User.unpin_all_messages, Bot.unpin_all_chat_messages, ["chat_id"], [] ) - assert check_shortcut_call(user.unpin_all_messages, user.bot, 'unpin_all_chat_messages') + assert check_shortcut_call(user.unpin_all_messages, user.bot, "unpin_all_chat_messages") assert check_defaults_handling(user.unpin_all_messages, user.bot) - monkeypatch.setattr(user.bot, 'unpin_all_chat_messages', make_assertion) + monkeypatch.setattr(user.bot, "unpin_all_chat_messages", make_assertion) assert user.unpin_all_messages() def test_instance_method_send_message(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['text'] == 'test' + return kwargs["chat_id"] == user.id and kwargs["text"] == "test" - assert check_shortcut_signature(User.send_message, Bot.send_message, ['chat_id'], []) - assert check_shortcut_call(user.send_message, user.bot, 'send_message') + assert check_shortcut_signature(User.send_message, Bot.send_message, ["chat_id"], []) + assert check_shortcut_call(user.send_message, user.bot, "send_message") assert check_defaults_handling(user.send_message, user.bot) - monkeypatch.setattr(user.bot, 'send_message', make_assertion) - assert user.send_message('test') + monkeypatch.setattr(user.bot, "send_message", make_assertion) + assert user.send_message("test") def test_instance_method_send_photo(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['photo'] == 'test_photo' + return kwargs["chat_id"] == user.id and kwargs["photo"] == "test_photo" - assert check_shortcut_signature(User.send_photo, Bot.send_photo, ['chat_id'], []) - assert check_shortcut_call(user.send_photo, user.bot, 'send_photo') + assert check_shortcut_signature(User.send_photo, Bot.send_photo, ["chat_id"], []) + assert check_shortcut_call(user.send_photo, user.bot, "send_photo") assert check_defaults_handling(user.send_photo, user.bot) - monkeypatch.setattr(user.bot, 'send_photo', make_assertion) - assert user.send_photo('test_photo') + monkeypatch.setattr(user.bot, "send_photo", make_assertion) + assert user.send_photo("test_photo") def test_instance_method_send_media_group(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['media'] == 'test_media_group' + return kwargs["chat_id"] == user.id and kwargs["media"] == "test_media_group" assert check_shortcut_signature( - User.send_media_group, Bot.send_media_group, ['chat_id'], [] + User.send_media_group, Bot.send_media_group, ["chat_id"], [] ) - assert check_shortcut_call(user.send_media_group, user.bot, 'send_media_group') + assert check_shortcut_call(user.send_media_group, user.bot, "send_media_group") assert check_defaults_handling(user.send_media_group, user.bot) - monkeypatch.setattr(user.bot, 'send_media_group', make_assertion) - assert user.send_media_group('test_media_group') + monkeypatch.setattr(user.bot, "send_media_group", make_assertion) + assert user.send_media_group("test_media_group") def test_instance_method_send_audio(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['audio'] == 'test_audio' + return kwargs["chat_id"] == user.id and kwargs["audio"] == "test_audio" - assert check_shortcut_signature(User.send_audio, Bot.send_audio, ['chat_id'], []) - assert check_shortcut_call(user.send_audio, user.bot, 'send_audio') + assert check_shortcut_signature(User.send_audio, Bot.send_audio, ["chat_id"], []) + assert check_shortcut_call(user.send_audio, user.bot, "send_audio") assert check_defaults_handling(user.send_audio, user.bot) - monkeypatch.setattr(user.bot, 'send_audio', make_assertion) - assert user.send_audio('test_audio') + monkeypatch.setattr(user.bot, "send_audio", make_assertion) + assert user.send_audio("test_audio") def test_instance_method_send_chat_action(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['action'] == 'test_chat_action' + return kwargs["chat_id"] == user.id and kwargs["action"] == "test_chat_action" assert check_shortcut_signature( - User.send_chat_action, Bot.send_chat_action, ['chat_id'], [] + User.send_chat_action, Bot.send_chat_action, ["chat_id"], [] ) - assert check_shortcut_call(user.send_chat_action, user.bot, 'send_chat_action') + assert check_shortcut_call(user.send_chat_action, user.bot, "send_chat_action") assert check_defaults_handling(user.send_chat_action, user.bot) - monkeypatch.setattr(user.bot, 'send_chat_action', make_assertion) - assert user.send_chat_action('test_chat_action') + monkeypatch.setattr(user.bot, "send_chat_action", make_assertion) + assert user.send_chat_action("test_chat_action") def test_instance_method_send_contact(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['phone_number'] == 'test_contact' + return kwargs["chat_id"] == user.id and kwargs["phone_number"] == "test_contact" - assert check_shortcut_signature(User.send_contact, Bot.send_contact, ['chat_id'], []) - assert check_shortcut_call(user.send_contact, user.bot, 'send_contact') + assert check_shortcut_signature(User.send_contact, Bot.send_contact, ["chat_id"], []) + assert check_shortcut_call(user.send_contact, user.bot, "send_contact") assert check_defaults_handling(user.send_contact, user.bot) - monkeypatch.setattr(user.bot, 'send_contact', make_assertion) - assert user.send_contact(phone_number='test_contact') + monkeypatch.setattr(user.bot, "send_contact", make_assertion) + assert user.send_contact(phone_number="test_contact") def test_instance_method_send_dice(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['emoji'] == 'test_dice' + return kwargs["chat_id"] == user.id and kwargs["emoji"] == "test_dice" - assert check_shortcut_signature(User.send_dice, Bot.send_dice, ['chat_id'], []) - assert check_shortcut_call(user.send_dice, user.bot, 'send_dice') + assert check_shortcut_signature(User.send_dice, Bot.send_dice, ["chat_id"], []) + assert check_shortcut_call(user.send_dice, user.bot, "send_dice") assert check_defaults_handling(user.send_dice, user.bot) - monkeypatch.setattr(user.bot, 'send_dice', make_assertion) - assert user.send_dice(emoji='test_dice') + monkeypatch.setattr(user.bot, "send_dice", make_assertion) + assert user.send_dice(emoji="test_dice") def test_instance_method_send_document(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['document'] == 'test_document' + return kwargs["chat_id"] == user.id and kwargs["document"] == "test_document" - assert check_shortcut_signature(User.send_document, Bot.send_document, ['chat_id'], []) - assert check_shortcut_call(user.send_document, user.bot, 'send_document') + assert check_shortcut_signature(User.send_document, Bot.send_document, ["chat_id"], []) + assert check_shortcut_call(user.send_document, user.bot, "send_document") assert check_defaults_handling(user.send_document, user.bot) - monkeypatch.setattr(user.bot, 'send_document', make_assertion) - assert user.send_document('test_document') + monkeypatch.setattr(user.bot, "send_document", make_assertion) + assert user.send_document("test_document") def test_instance_method_send_game(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['game_short_name'] == 'test_game' + return kwargs["chat_id"] == user.id and kwargs["game_short_name"] == "test_game" - assert check_shortcut_signature(User.send_game, Bot.send_game, ['chat_id'], []) - assert check_shortcut_call(user.send_game, user.bot, 'send_game') + assert check_shortcut_signature(User.send_game, Bot.send_game, ["chat_id"], []) + assert check_shortcut_call(user.send_game, user.bot, "send_game") assert check_defaults_handling(user.send_game, user.bot) - monkeypatch.setattr(user.bot, 'send_game', make_assertion) - assert user.send_game(game_short_name='test_game') + monkeypatch.setattr(user.bot, "send_game", make_assertion) + assert user.send_game(game_short_name="test_game") def test_instance_method_send_invoice(self, monkeypatch, user): def make_assertion(*_, **kwargs): - title = kwargs['title'] == 'title' - description = kwargs['description'] == 'description' - payload = kwargs['payload'] == 'payload' - provider_token = kwargs['provider_token'] == 'provider_token' - currency = kwargs['currency'] == 'currency' - prices = kwargs['prices'] == 'prices' + title = kwargs["title"] == "title" + description = kwargs["description"] == "description" + payload = kwargs["payload"] == "payload" + provider_token = kwargs["provider_token"] == "provider_token" + currency = kwargs["currency"] == "currency" + prices = kwargs["prices"] == "prices" args = title and description and payload and provider_token and currency and prices - return kwargs['chat_id'] == user.id and args + return kwargs["chat_id"] == user.id and args - assert check_shortcut_signature(User.send_invoice, Bot.send_invoice, ['chat_id'], []) - assert check_shortcut_call(user.send_invoice, user.bot, 'send_invoice') + assert check_shortcut_signature(User.send_invoice, Bot.send_invoice, ["chat_id"], []) + assert check_shortcut_call(user.send_invoice, user.bot, "send_invoice") assert check_defaults_handling(user.send_invoice, user.bot) - monkeypatch.setattr(user.bot, 'send_invoice', make_assertion) + monkeypatch.setattr(user.bot, "send_invoice", make_assertion) assert user.send_invoice( - 'title', - 'description', - 'payload', - 'provider_token', - 'currency', - 'prices', + "title", + "description", + "payload", + "provider_token", + "currency", + "prices", ) def test_instance_method_send_location(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['latitude'] == 'test_location' + return kwargs["chat_id"] == user.id and kwargs["latitude"] == "test_location" - assert check_shortcut_signature(User.send_location, Bot.send_location, ['chat_id'], []) - assert check_shortcut_call(user.send_location, user.bot, 'send_location') + assert check_shortcut_signature(User.send_location, Bot.send_location, ["chat_id"], []) + assert check_shortcut_call(user.send_location, user.bot, "send_location") assert check_defaults_handling(user.send_location, user.bot) - monkeypatch.setattr(user.bot, 'send_location', make_assertion) - assert user.send_location('test_location') + monkeypatch.setattr(user.bot, "send_location", make_assertion) + assert user.send_location("test_location") def test_instance_method_send_sticker(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['sticker'] == 'test_sticker' + return kwargs["chat_id"] == user.id and kwargs["sticker"] == "test_sticker" - assert check_shortcut_signature(User.send_sticker, Bot.send_sticker, ['chat_id'], []) - assert check_shortcut_call(user.send_sticker, user.bot, 'send_sticker') + assert check_shortcut_signature(User.send_sticker, Bot.send_sticker, ["chat_id"], []) + assert check_shortcut_call(user.send_sticker, user.bot, "send_sticker") assert check_defaults_handling(user.send_sticker, user.bot) - monkeypatch.setattr(user.bot, 'send_sticker', make_assertion) - assert user.send_sticker('test_sticker') + monkeypatch.setattr(user.bot, "send_sticker", make_assertion) + assert user.send_sticker("test_sticker") def test_instance_method_send_video(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['video'] == 'test_video' + return kwargs["chat_id"] == user.id and kwargs["video"] == "test_video" - assert check_shortcut_signature(User.send_video, Bot.send_video, ['chat_id'], []) - assert check_shortcut_call(user.send_video, user.bot, 'send_video') + assert check_shortcut_signature(User.send_video, Bot.send_video, ["chat_id"], []) + assert check_shortcut_call(user.send_video, user.bot, "send_video") assert check_defaults_handling(user.send_video, user.bot) - monkeypatch.setattr(user.bot, 'send_video', make_assertion) - assert user.send_video('test_video') + monkeypatch.setattr(user.bot, "send_video", make_assertion) + assert user.send_video("test_video") def test_instance_method_send_venue(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['title'] == 'test_venue' + return kwargs["chat_id"] == user.id and kwargs["title"] == "test_venue" - assert check_shortcut_signature(User.send_venue, Bot.send_venue, ['chat_id'], []) - assert check_shortcut_call(user.send_venue, user.bot, 'send_venue') + assert check_shortcut_signature(User.send_venue, Bot.send_venue, ["chat_id"], []) + assert check_shortcut_call(user.send_venue, user.bot, "send_venue") assert check_defaults_handling(user.send_venue, user.bot) - monkeypatch.setattr(user.bot, 'send_venue', make_assertion) - assert user.send_venue(title='test_venue') + monkeypatch.setattr(user.bot, "send_venue", make_assertion) + assert user.send_venue(title="test_venue") def test_instance_method_send_video_note(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['video_note'] == 'test_video_note' + return kwargs["chat_id"] == user.id and kwargs["video_note"] == "test_video_note" - assert check_shortcut_signature(User.send_video_note, Bot.send_video_note, ['chat_id'], []) - assert check_shortcut_call(user.send_video_note, user.bot, 'send_video_note') + assert check_shortcut_signature(User.send_video_note, Bot.send_video_note, ["chat_id"], []) + assert check_shortcut_call(user.send_video_note, user.bot, "send_video_note") assert check_defaults_handling(user.send_video_note, user.bot) - monkeypatch.setattr(user.bot, 'send_video_note', make_assertion) - assert user.send_video_note('test_video_note') + monkeypatch.setattr(user.bot, "send_video_note", make_assertion) + assert user.send_video_note("test_video_note") def test_instance_method_send_voice(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['voice'] == 'test_voice' + return kwargs["chat_id"] == user.id and kwargs["voice"] == "test_voice" - assert check_shortcut_signature(User.send_voice, Bot.send_voice, ['chat_id'], []) - assert check_shortcut_call(user.send_voice, user.bot, 'send_voice') + assert check_shortcut_signature(User.send_voice, Bot.send_voice, ["chat_id"], []) + assert check_shortcut_call(user.send_voice, user.bot, "send_voice") assert check_defaults_handling(user.send_voice, user.bot) - monkeypatch.setattr(user.bot, 'send_voice', make_assertion) - assert user.send_voice('test_voice') + monkeypatch.setattr(user.bot, "send_voice", make_assertion) + assert user.send_voice("test_voice") def test_instance_method_send_animation(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['animation'] == 'test_animation' + return kwargs["chat_id"] == user.id and kwargs["animation"] == "test_animation" - assert check_shortcut_signature(User.send_animation, Bot.send_animation, ['chat_id'], []) - assert check_shortcut_call(user.send_animation, user.bot, 'send_animation') + assert check_shortcut_signature(User.send_animation, Bot.send_animation, ["chat_id"], []) + assert check_shortcut_call(user.send_animation, user.bot, "send_animation") assert check_defaults_handling(user.send_animation, user.bot) - monkeypatch.setattr(user.bot, 'send_animation', make_assertion) - assert user.send_animation('test_animation') + monkeypatch.setattr(user.bot, "send_animation", make_assertion) + assert user.send_animation("test_animation") def test_instance_method_send_poll(self, monkeypatch, user): def make_assertion(*_, **kwargs): - return kwargs['chat_id'] == user.id and kwargs['question'] == 'test_poll' + return kwargs["chat_id"] == user.id and kwargs["question"] == "test_poll" - assert check_shortcut_signature(User.send_poll, Bot.send_poll, ['chat_id'], []) - assert check_shortcut_call(user.send_poll, user.bot, 'send_poll') + assert check_shortcut_signature(User.send_poll, Bot.send_poll, ["chat_id"], []) + assert check_shortcut_call(user.send_poll, user.bot, "send_poll") assert check_defaults_handling(user.send_poll, user.bot) - monkeypatch.setattr(user.bot, 'send_poll', make_assertion) - assert user.send_poll(question='test_poll', options=[1, 2]) + monkeypatch.setattr(user.bot, "send_poll", make_assertion) + assert user.send_poll(question="test_poll", options=[1, 2]) def test_instance_method_send_copy(self, monkeypatch, user): def make_assertion(*_, **kwargs): - user_id = kwargs['chat_id'] == user.id - message_id = kwargs['message_id'] == 'message_id' - from_chat_id = kwargs['from_chat_id'] == 'from_chat_id' + user_id = kwargs["chat_id"] == user.id + message_id = kwargs["message_id"] == "message_id" + from_chat_id = kwargs["from_chat_id"] == "from_chat_id" return from_chat_id and message_id and user_id - assert check_shortcut_signature(User.send_copy, Bot.copy_message, ['chat_id'], []) - assert check_shortcut_call(user.copy_message, user.bot, 'copy_message') + assert check_shortcut_signature(User.send_copy, Bot.copy_message, ["chat_id"], []) + assert check_shortcut_call(user.copy_message, user.bot, "copy_message") assert check_defaults_handling(user.copy_message, user.bot) - monkeypatch.setattr(user.bot, 'copy_message', make_assertion) - assert user.send_copy(from_chat_id='from_chat_id', message_id='message_id') + monkeypatch.setattr(user.bot, "copy_message", make_assertion) + assert user.send_copy(from_chat_id="from_chat_id", message_id="message_id") def test_instance_method_copy_message(self, monkeypatch, user): def make_assertion(*_, **kwargs): - chat_id = kwargs['chat_id'] == 'chat_id' - message_id = kwargs['message_id'] == 'message_id' - user_id = kwargs['from_chat_id'] == user.id + chat_id = kwargs["chat_id"] == "chat_id" + message_id = kwargs["message_id"] == "message_id" + user_id = kwargs["from_chat_id"] == user.id return chat_id and message_id and user_id - assert check_shortcut_signature(User.copy_message, Bot.copy_message, ['from_chat_id'], []) - assert check_shortcut_call(user.copy_message, user.bot, 'copy_message') + assert check_shortcut_signature(User.copy_message, Bot.copy_message, ["from_chat_id"], []) + assert check_shortcut_call(user.copy_message, user.bot, "copy_message") assert check_defaults_handling(user.copy_message, user.bot) - monkeypatch.setattr(user.bot, 'copy_message', make_assertion) - assert user.copy_message(chat_id='chat_id', message_id='message_id') + monkeypatch.setattr(user.bot, "copy_message", make_assertion) + assert user.copy_message(chat_id="chat_id", message_id="message_id") def test_mention_html(self, user): expected = '{}' assert user.mention_html() == expected.format(user.id, user.full_name) - assert user.mention_html('thename\u2022') == expected.format( - user.id, 'the<b>name\u2022' + assert user.mention_html("thename\u2022") == expected.format( + user.id, "the<b>name\u2022" ) assert user.mention_html(user.username) == expected.format(user.id, user.username) def test_mention_markdown(self, user): - expected = '[{}](tg://user?id={})' + expected = "[{}](tg://user?id={})" assert user.mention_markdown() == expected.format(user.full_name, user.id) - assert user.mention_markdown('the_name*\u2022') == expected.format( - 'the\\_name\\*\u2022', user.id + assert user.mention_markdown("the_name*\u2022") == expected.format( + "the\\_name\\*\u2022", user.id ) assert user.mention_markdown(user.username) == expected.format(user.username, user.id) def test_mention_markdown_v2(self, user): - user.first_name = 'first{name' - user.last_name = 'last_name' + user.first_name = "first{name" + user.last_name = "last_name" - expected = '[{}](tg://user?id={})' + expected = "[{}](tg://user?id={})" assert user.mention_markdown_v2() == expected.format( escape_markdown(user.full_name, version=2), user.id ) - assert user.mention_markdown_v2('the{name>\u2022') == expected.format( - 'the\\{name\\>\u2022', user.id + assert user.mention_markdown_v2("the{name>\u2022") == expected.format( + "the\\{name\\>\u2022", user.id ) assert user.mention_markdown_v2(user.username) == expected.format(user.username, user.id) diff --git a/tests/test_userprofilephotos.py b/tests/test_userprofilephotos.py index 84a428da18c..2bcfbd3ba5b 100644 --- a/tests/test_userprofilephotos.py +++ b/tests/test_userprofilephotos.py @@ -23,26 +23,23 @@ class TestUserProfilePhotos: total_count = 2 photos = [ [ - PhotoSize('file_id1', 'file_un_id1', 512, 512), - PhotoSize('file_id2', 'file_un_id1', 512, 512), + PhotoSize("file_id1", "file_un_id1", 512, 512), + PhotoSize("file_id2", "file_un_id1", 512, 512), ], [ - PhotoSize('file_id3', 'file_un_id3', 512, 512), - PhotoSize('file_id4', 'file_un_id4', 512, 512), + PhotoSize("file_id3", "file_un_id3", 512, 512), + PhotoSize("file_id4", "file_un_id4", 512, 512), ], ] - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): inst = UserProfilePhotos(self.total_count, self.photos) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.total_count = 'should give warning', self.total_count - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): - json_dict = {'total_count': 2, 'photos': [[y.to_dict() for y in x] for x in self.photos]} + json_dict = {"total_count": 2, "photos": [[y.to_dict() for y in x] for x in self.photos]} user_profile_photos = UserProfilePhotos.de_json(json_dict, bot) assert user_profile_photos.total_count == self.total_count assert user_profile_photos.photos == self.photos @@ -50,8 +47,8 @@ def test_de_json(self, bot): def test_to_dict(self): user_profile_photos = UserProfilePhotos(self.total_count, self.photos) user_profile_photos_dict = user_profile_photos.to_dict() - assert user_profile_photos_dict['total_count'] == user_profile_photos.total_count - for ix, x in enumerate(user_profile_photos_dict['photos']): + assert user_profile_photos_dict["total_count"] == user_profile_photos.total_count + for ix, x in enumerate(user_profile_photos_dict["photos"]): for iy, y in enumerate(x): assert y == user_profile_photos.photos[ix][iy].to_dict() @@ -59,7 +56,7 @@ def test_equality(self): a = UserProfilePhotos(2, self.photos) b = UserProfilePhotos(2, self.photos) c = UserProfilePhotos(1, [self.photos[0]]) - d = PhotoSize('file_id1', 'unique_id', 512, 512) + d = PhotoSize("file_id1", "unique_id", 512, 512) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_utils.py b/tests/test_utils.py deleted file mode 100644 index c8a92d9b223..00000000000 --- a/tests/test_utils.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# -# A library that provides a Python interface to the Telegram Bot API -# Copyright (C) 2015-2021 -# Leandro Toledo de Souza -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Lesser Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser Public License for more details. -# -# You should have received a copy of the GNU Lesser Public License -# along with this program. If not, see [http://www.gnu.org/licenses/]. - - -class TestUtils: - def test_promise_deprecation(self, recwarn): - import telegram.utils.promise # noqa: F401 - - assert len(recwarn) == 1 - assert str(recwarn[0].message) == ( - 'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.' - ) - - def test_webhookhandler_deprecation(self, recwarn): - import telegram.utils.webhookhandler # noqa: F401 - - assert len(recwarn) == 1 - assert str(recwarn[0].message) == ( - 'telegram.utils.webhookhandler is deprecated. Please use ' - 'telegram.ext.utils.webhookhandler instead.' - ) diff --git a/tests/test_venue.py b/tests/test_venue.py index 185318211ff..8a176fe1f48 100644 --- a/tests/test_venue.py +++ b/tests/test_venue.py @@ -23,7 +23,7 @@ from telegram.error import BadRequest -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def venue(): return Venue( TestVenue.location, @@ -38,30 +38,27 @@ def venue(): class TestVenue: location = Location(longitude=-46.788279, latitude=-23.691288) - title = 'title' - address = 'address' - foursquare_id = 'foursquare id' - foursquare_type = 'foursquare type' - google_place_id = 'google place id' - google_place_type = 'google place type' - - def test_slot_behaviour(self, venue, mro_slots, recwarn): + title = "title" + address = "address" + foursquare_id = "foursquare id" + foursquare_type = "foursquare type" + google_place_id = "google place id" + google_place_type = "google place type" + + def test_slot_behaviour(self, venue, mro_slots): for attr in venue.__slots__: - assert getattr(venue, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not venue.__dict__, f"got missing slot(s): {venue.__dict__}" + assert getattr(venue, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(venue)) == len(set(mro_slots(venue))), "duplicate slot" - venue.custom, venue.title = 'should give warning', self.title - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, bot): json_dict = { - 'location': TestVenue.location.to_dict(), - 'title': TestVenue.title, - 'address': TestVenue.address, - 'foursquare_id': TestVenue.foursquare_id, - 'foursquare_type': TestVenue.foursquare_type, - 'google_place_id': TestVenue.google_place_id, - 'google_place_type': TestVenue.google_place_type, + "location": TestVenue.location.to_dict(), + "title": TestVenue.title, + "address": TestVenue.address, + "foursquare_id": TestVenue.foursquare_id, + "foursquare_type": TestVenue.foursquare_type, + "google_place_id": TestVenue.google_place_id, + "google_place_type": TestVenue.google_place_type, } venue = Venue.de_json(json_dict, bot) @@ -76,34 +73,34 @@ def test_de_json(self, bot): def test_send_with_venue(self, monkeypatch, bot, chat_id, venue): def test(url, data, **kwargs): return ( - data['longitude'] == self.location.longitude - and data['latitude'] == self.location.latitude - and data['title'] == self.title - and data['address'] == self.address - and data['foursquare_id'] == self.foursquare_id - and data['foursquare_type'] == self.foursquare_type - and data['google_place_id'] == self.google_place_id - and data['google_place_type'] == self.google_place_type + data["longitude"] == self.location.longitude + and data["latitude"] == self.location.latitude + and data["title"] == self.title + and data["address"] == self.address + and data["foursquare_id"] == self.foursquare_id + and data["foursquare_type"] == self.foursquare_type + and data["google_place_id"] == self.google_place_id + and data["google_place_type"] == self.google_place_type ) - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_venue(chat_id, venue=venue) assert message @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_venue_default_allow_sending_without_reply( self, default_bot, chat_id, venue, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_venue( @@ -119,33 +116,33 @@ def test_send_venue_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_venue( chat_id, venue=venue, reply_to_message_id=reply_to_message.message_id ) def test_send_venue_without_required(self, bot, chat_id): - with pytest.raises(ValueError, match='Either venue or latitude, longitude, address and'): + with pytest.raises(ValueError, match="Either venue or latitude, longitude, address and"): bot.send_venue(chat_id=chat_id) def test_to_dict(self, venue): venue_dict = venue.to_dict() assert isinstance(venue_dict, dict) - assert venue_dict['location'] == venue.location.to_dict() - assert venue_dict['title'] == venue.title - assert venue_dict['address'] == venue.address - assert venue_dict['foursquare_id'] == venue.foursquare_id - assert venue_dict['foursquare_type'] == venue.foursquare_type - assert venue_dict['google_place_id'] == venue.google_place_id - assert venue_dict['google_place_type'] == venue.google_place_type + assert venue_dict["location"] == venue.location.to_dict() + assert venue_dict["title"] == venue.title + assert venue_dict["address"] == venue.address + assert venue_dict["foursquare_id"] == venue.foursquare_id + assert venue_dict["foursquare_type"] == venue.foursquare_type + assert venue_dict["google_place_id"] == venue.google_place_id + assert venue_dict["google_place_type"] == venue.google_place_type def test_equality(self): a = Venue(Location(0, 0), self.title, self.address) b = Venue(Location(0, 0), self.title, self.address) - c = Venue(Location(0, 0), self.title, '') + c = Venue(Location(0, 0), self.title, "") d = Venue(Location(0, 1), self.title, self.address) - d2 = Venue(Location(0, 0), '', self.address) + d2 = Venue(Location(0, 0), "", self.address) assert a == b assert hash(a) == hash(b) diff --git a/tests/test_video.py b/tests/test_video.py index 0eca16798ea..10a8b131db4 100644 --- a/tests/test_video.py +++ b/tests/test_video.py @@ -28,16 +28,16 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def video_file(): - f = open('tests/data/telegram.mp4', 'rb') + f = open("tests/data/telegram.mp4", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def video(bot, chat_id): - with open('tests/data/telegram.mp4', 'rb') as f: + with open("tests/data/telegram.mp4", "rb") as f: return bot.send_video(chat_id, video=f, timeout=50).video @@ -46,41 +46,38 @@ class TestVideo: height = 640 duration = 5 file_size = 326534 - mime_type = 'video/mp4' + mime_type = "video/mp4" supports_streaming = True - file_name = 'telegram.mp4' + file_name = "telegram.mp4" thumb_width = 180 thumb_height = 320 thumb_file_size = 1767 - caption = 'VideoTest - *Caption*' - video_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.mp4' + caption = "VideoTest - *Caption*" + video_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.mp4" - video_file_id = '5a3128a4d2a04750b5b58397f3b5e812' - video_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + video_file_id = "5a3128a4d2a04750b5b58397f3b5e812" + video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" - def test_slot_behaviour(self, video, mro_slots, recwarn): + def test_slot_behaviour(self, video, mro_slots): for attr in video.__slots__: - assert getattr(video, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not video.__dict__, f"got missing slot(s): {video.__dict__}" + assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(video)) == len(set(mro_slots(video))), "duplicate slot" - video.custom, video.width = 'should give warning', self.width - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, video): # Make sure file has been uploaded. assert isinstance(video, Video) assert isinstance(video.file_id, str) assert isinstance(video.file_unique_id, str) - assert video.file_id != '' - assert video.file_unique_id != '' + assert video.file_id != "" + assert video.file_unique_id != "" assert isinstance(video.thumb, PhotoSize) assert isinstance(video.thumb.file_id, str) assert isinstance(video.thumb.file_unique_id, str) - assert video.thumb.file_id != '' - assert video.thumb.file_unique_id != '' + assert video.thumb.file_id != "" + assert video.thumb.file_unique_id != "" def test_expected_values(self, video): assert video.width == self.width @@ -100,21 +97,21 @@ def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): disable_notification=False, width=video.width, height=video.height, - parse_mode='Markdown', + parse_mode="Markdown", thumb=thumb_file, ) assert isinstance(message.video, Video) assert isinstance(message.video.file_id, str) assert isinstance(message.video.file_unique_id, str) - assert message.video.file_id != '' - assert message.video.file_unique_id != '' + assert message.video.file_id != "" + assert message.video.file_unique_id != "" assert message.video.width == video.width assert message.video.height == video.height assert message.video.duration == video.duration assert message.video.file_size == video.file_size - assert message.caption == self.caption.replace('*', '') + assert message.caption == self.caption.replace("*", "") assert message.video.thumb.file_size == self.thumb_file_size assert message.video.thumb.width == self.thumb_width @@ -125,11 +122,11 @@ def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file): @flaky(3, 1) def test_send_video_custom_filename(self, bot, chat_id, video_file, monkeypatch): def make_assertion(url, data, **kwargs): - return data['video'].filename == 'custom_filename' + return data["video"].filename == "custom_filename" - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) - assert bot.send_video(chat_id, video_file, filename='custom_filename') + assert bot.send_video(chat_id, video_file, filename="custom_filename") @flaky(3, 1) def test_get_and_download(self, bot, video): @@ -138,11 +135,11 @@ def test_get_and_download(self, bot, video): assert new_file.file_size == self.file_size assert new_file.file_id == video.file_id assert new_file.file_unique_id == video.file_unique_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.mp4') + new_file.download("telegram.mp4") - assert os.path.isfile('telegram.mp4') + assert os.path.isfile("telegram.mp4") @flaky(3, 1) def test_send_mp4_file_url(self, bot, chat_id, video): @@ -151,8 +148,8 @@ def test_send_mp4_file_url(self, bot, chat_id, video): assert isinstance(message.video, Video) assert isinstance(message.video.file_id, str) assert isinstance(message.video.file_unique_id, str) - assert message.video.file_id != '' - assert message.video.file_unique_id != '' + assert message.video.file_id != "" + assert message.video.file_unique_id != "" assert message.video.width == video.width assert message.video.height == video.height assert message.video.duration == video.duration @@ -161,8 +158,8 @@ def test_send_mp4_file_url(self, bot, chat_id, video): assert isinstance(message.video.thumb, PhotoSize) assert isinstance(message.video.thumb.file_id, str) assert isinstance(message.video.thumb.file_unique_id, str) - assert message.video.thumb.file_id != '' - assert message.video.thumb.file_unique_id != '' + assert message.video.thumb.file_id != "" + assert message.video.thumb.file_unique_id != "" assert message.video.thumb.width == 51 # This seems odd that it's not self.thumb_width assert message.video.thumb.height == 90 # Ditto assert message.video.thumb.file_size == 645 # same @@ -171,7 +168,7 @@ def test_send_mp4_file_url(self, bot, chat_id, video): @flaky(3, 1) def test_send_video_caption_entities(self, bot, chat_id, video): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -190,26 +187,26 @@ def test_resend(self, bot, chat_id, video): def test_send_with_video(self, monkeypatch, bot, chat_id, video): def test(url, data, **kwargs): - return data['video'] == video.file_id + return data["video"] == video.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_video(chat_id, video=video) assert message @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_video_default_parse_mode_1(self, default_bot, chat_id, video): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_video(chat_id, video, caption=test_markdown_string) assert message.caption_markdown == test_markdown_string assert message.caption == test_string @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_video_default_parse_mode_2(self, default_bot, chat_id, video): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_video( chat_id, video, caption=test_markdown_string, parse_mode=None @@ -218,12 +215,12 @@ def test_send_video_default_parse_mode_2(self, default_bot, chat_id, video): assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_video_default_parse_mode_3(self, default_bot, chat_id, video): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_video( - chat_id, video, caption=test_markdown_string, parse_mode='HTML' + chat_id, video, caption=test_markdown_string, parse_mode="HTML" ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @@ -231,32 +228,32 @@ def test_send_video_default_parse_mode_3(self, default_bot, chat_id, video): def test_send_video_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('video') == expected and data.get('thumb') == expected + test_flag = data.get("video") == expected and data.get("thumb") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_video(chat_id, file, thumb=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_video_default_allow_sending_without_reply( self, default_bot, chat_id, video, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_video( @@ -272,21 +269,21 @@ def test_send_video_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_video( chat_id, video, reply_to_message_id=reply_to_message.message_id ) def test_de_json(self, bot): json_dict = { - 'file_id': self.video_file_id, - 'file_unique_id': self.video_file_unique_id, - 'width': self.width, - 'height': self.height, - 'duration': self.duration, - 'mime_type': self.mime_type, - 'file_size': self.file_size, - 'file_name': self.file_name, + "file_id": self.video_file_id, + "file_unique_id": self.video_file_unique_id, + "width": self.width, + "height": self.height, + "duration": self.duration, + "mime_type": self.mime_type, + "file_size": self.file_size, + "file_name": self.file_name, } json_video = Video.de_json(json_dict, bot) @@ -303,24 +300,24 @@ def test_to_dict(self, video): video_dict = video.to_dict() assert isinstance(video_dict, dict) - assert video_dict['file_id'] == video.file_id - assert video_dict['file_unique_id'] == video.file_unique_id - assert video_dict['width'] == video.width - assert video_dict['height'] == video.height - assert video_dict['duration'] == video.duration - assert video_dict['mime_type'] == video.mime_type - assert video_dict['file_size'] == video.file_size - assert video_dict['file_name'] == video.file_name + assert video_dict["file_id"] == video.file_id + assert video_dict["file_unique_id"] == video.file_unique_id + assert video_dict["width"] == video.width + assert video_dict["height"] == video.height + assert video_dict["duration"] == video.duration + assert video_dict["mime_type"] == video.mime_type + assert video_dict["file_size"] == video.file_size + assert video_dict["file_name"] == video.file_name @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_video(chat_id, open(os.devnull, 'rb')) + bot.send_video(chat_id, open(os.devnull, "rb")) @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_video(chat_id, '') + bot.send_video(chat_id, "") def test_error_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -328,20 +325,20 @@ def test_error_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, video): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == video.file_id + return kwargs["file_id"] == video.file_id - assert check_shortcut_signature(Video.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(video.get_file, video.bot, 'get_file') + assert check_shortcut_signature(Video.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(video.get_file, video.bot, "get_file") assert check_defaults_handling(video.get_file, video.bot) - monkeypatch.setattr(video.bot, 'get_file', make_assertion) + monkeypatch.setattr(video.bot, "get_file", make_assertion) assert video.get_file() def test_equality(self, video): a = Video(video.file_id, video.file_unique_id, self.width, self.height, self.duration) - b = Video('', video.file_unique_id, self.width, self.height, self.duration) + b = Video("", video.file_unique_id, self.width, self.height, self.duration) c = Video(video.file_id, video.file_unique_id, 0, 0, 0) - d = Video('', '', self.width, self.height, self.duration) + d = Video("", "", self.width, self.height, self.duration) e = Voice(video.file_id, video.file_unique_id, self.duration) assert a == b diff --git a/tests/test_videonote.py b/tests/test_videonote.py index 7f8c39773fb..17400d161bb 100644 --- a/tests/test_videonote.py +++ b/tests/test_videonote.py @@ -27,16 +27,16 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def video_note_file(): - f = open('tests/data/telegram2.mp4', 'rb') + f = open("tests/data/telegram2.mp4", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def video_note(bot, chat_id): - with open('tests/data/telegram2.mp4', 'rb') as f: + with open("tests/data/telegram2.mp4", "rb") as f: return bot.send_video_note(chat_id, video_note=f, timeout=50).video_note @@ -49,31 +49,28 @@ class TestVideoNote: thumb_height = 240 thumb_file_size = 11547 - caption = 'VideoNoteTest - Caption' - videonote_file_id = '5a3128a4d2a04750b5b58397f3b5e812' - videonote_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + caption = "VideoNoteTest - Caption" + videonote_file_id = "5a3128a4d2a04750b5b58397f3b5e812" + videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" - def test_slot_behaviour(self, video_note, recwarn, mro_slots): + def test_slot_behaviour(self, video_note, mro_slots): for attr in video_note.__slots__: - assert getattr(video_note, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not video_note.__dict__, f"got missing slot(s): {video_note.__dict__}" + assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(video_note)) == len(set(mro_slots(video_note))), "duplicate slot" - video_note.custom, video_note.length = 'should give warning', self.length - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, video_note): # Make sure file has been uploaded. assert isinstance(video_note, VideoNote) assert isinstance(video_note.file_id, str) assert isinstance(video_note.file_unique_id, str) - assert video_note.file_id != '' - assert video_note.file_unique_id != '' + assert video_note.file_id != "" + assert video_note.file_unique_id != "" assert isinstance(video_note.thumb, PhotoSize) assert isinstance(video_note.thumb.file_id, str) assert isinstance(video_note.thumb.file_unique_id, str) - assert video_note.thumb.file_id != '' - assert video_note.thumb.file_unique_id != '' + assert video_note.thumb.file_id != "" + assert video_note.thumb.file_unique_id != "" def test_expected_values(self, video_note): assert video_note.length == self.length @@ -94,8 +91,8 @@ def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_fi assert isinstance(message.video_note, VideoNote) assert isinstance(message.video_note.file_id, str) assert isinstance(message.video_note.file_unique_id, str) - assert message.video_note.file_id != '' - assert message.video_note.file_unique_id != '' + assert message.video_note.file_id != "" + assert message.video_note.file_unique_id != "" assert message.video_note.length == video_note.length assert message.video_note.duration == video_note.duration assert message.video_note.file_size == video_note.file_size @@ -107,11 +104,11 @@ def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_fi @flaky(3, 1) def test_send_video_note_custom_filename(self, bot, chat_id, video_note_file, monkeypatch): def make_assertion(url, data, **kwargs): - return data['video_note'].filename == 'custom_filename' + return data["video_note"].filename == "custom_filename" - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) - assert bot.send_video_note(chat_id, video_note_file, filename='custom_filename') + assert bot.send_video_note(chat_id, video_note_file, filename="custom_filename") @flaky(3, 1) def test_get_and_download(self, bot, video_note): @@ -120,11 +117,11 @@ def test_get_and_download(self, bot, video_note): assert new_file.file_size == self.file_size assert new_file.file_id == video_note.file_id assert new_file.file_unique_id == video_note.file_unique_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram2.mp4') + new_file.download("telegram2.mp4") - assert os.path.isfile('telegram2.mp4') + assert os.path.isfile("telegram2.mp4") @flaky(3, 1) def test_resend(self, bot, chat_id, video_note): @@ -134,19 +131,19 @@ def test_resend(self, bot, chat_id, video_note): def test_send_with_video_note(self, monkeypatch, bot, chat_id, video_note): def test(url, data, **kwargs): - return data['video_note'] == video_note.file_id + return data["video_note"] == video_note.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_video_note(chat_id, video_note=video_note) assert message def test_de_json(self, bot): json_dict = { - 'file_id': self.videonote_file_id, - 'file_unique_id': self.videonote_file_unique_id, - 'length': self.length, - 'duration': self.duration, - 'file_size': self.file_size, + "file_id": self.videonote_file_id, + "file_unique_id": self.videonote_file_unique_id, + "length": self.length, + "duration": self.duration, + "file_size": self.file_size, } json_video_note = VideoNote.de_json(json_dict, bot) @@ -160,41 +157,41 @@ def test_to_dict(self, video_note): video_note_dict = video_note.to_dict() assert isinstance(video_note_dict, dict) - assert video_note_dict['file_id'] == video_note.file_id - assert video_note_dict['file_unique_id'] == video_note.file_unique_id - assert video_note_dict['length'] == video_note.length - assert video_note_dict['duration'] == video_note.duration - assert video_note_dict['file_size'] == video_note.file_size + assert video_note_dict["file_id"] == video_note.file_id + assert video_note_dict["file_unique_id"] == video_note.file_unique_id + assert video_note_dict["length"] == video_note.length + assert video_note_dict["duration"] == video_note.duration + assert video_note_dict["file_size"] == video_note.file_size def test_send_video_note_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('video_note') == expected and data.get('thumb') == expected + test_flag = data.get("video_note") == expected and data.get("thumb") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_video_note(chat_id, file, thumb=file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_video_note_default_allow_sending_without_reply( self, default_bot, chat_id, video_note, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_video_note( @@ -210,7 +207,7 @@ def test_send_video_note_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_video_note( chat_id, video_note, reply_to_message_id=reply_to_message.message_id ) @@ -218,12 +215,12 @@ def test_send_video_note_default_allow_sending_without_reply( @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_video_note(chat_id, open(os.devnull, 'rb')) + bot.send_video_note(chat_id, open(os.devnull, "rb")) @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.send_video_note(chat_id, '') + bot.send_video_note(chat_id, "") def test_error_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -231,20 +228,20 @@ def test_error_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, video_note): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == video_note.file_id + return kwargs["file_id"] == video_note.file_id - assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(video_note.get_file, video_note.bot, 'get_file') + assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(video_note.get_file, video_note.bot, "get_file") assert check_defaults_handling(video_note.get_file, video_note.bot) - monkeypatch.setattr(video_note.bot, 'get_file', make_assertion) + monkeypatch.setattr(video_note.bot, "get_file", make_assertion) assert video_note.get_file() def test_equality(self, video_note): a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration) - b = VideoNote('', video_note.file_unique_id, self.length, self.duration) + b = VideoNote("", video_note.file_unique_id, self.length, self.duration) c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0) - d = VideoNote('', '', self.length, self.duration) + d = VideoNote("", "", self.length, self.duration) e = Voice(video_note.file_id, video_note.file_unique_id, self.duration) assert a == b diff --git a/tests/test_voice.py b/tests/test_voice.py index df45da699fd..f179fd26caa 100644 --- a/tests/test_voice.py +++ b/tests/test_voice.py @@ -28,45 +28,42 @@ from tests.conftest import check_shortcut_call, check_shortcut_signature, check_defaults_handling -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def voice_file(): - f = open('tests/data/telegram.ogg', 'rb') + f = open("tests/data/telegram.ogg", "rb") yield f f.close() -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def voice(bot, chat_id): - with open('tests/data/telegram.ogg', 'rb') as f: + with open("tests/data/telegram.ogg", "rb") as f: return bot.send_voice(chat_id, voice=f, timeout=50).voice class TestVoice: duration = 3 - mime_type = 'audio/ogg' + mime_type = "audio/ogg" file_size = 9199 - caption = 'Test *voice*' - voice_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.ogg' + caption = "Test *voice*" + voice_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.ogg" - voice_file_id = '5a3128a4d2a04750b5b58397f3b5e812' - voice_file_unique_id = 'adc3145fd2e84d95b64d68eaa22aa33e' + voice_file_id = "5a3128a4d2a04750b5b58397f3b5e812" + voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e" - def test_slot_behaviour(self, voice, recwarn, mro_slots): + def test_slot_behaviour(self, voice, mro_slots): for attr in voice.__slots__: - assert getattr(voice, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not voice.__dict__, f"got missing slot(s): {voice.__dict__}" + assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(voice)) == len(set(mro_slots(voice))), "duplicate slot" - voice.custom, voice.duration = 'should give warning', self.duration - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_creation(self, voice): # Make sure file has been uploaded. assert isinstance(voice, Voice) assert isinstance(voice.file_id, str) assert isinstance(voice.file_unique_id, str) - assert voice.file_id != '' - assert voice.file_unique_id != '' + assert voice.file_id != "" + assert voice.file_unique_id != "" def test_expected_values(self, voice): assert voice.duration == self.duration @@ -81,27 +78,27 @@ def test_send_all_args(self, bot, chat_id, voice_file, voice): duration=self.duration, caption=self.caption, disable_notification=False, - parse_mode='Markdown', + parse_mode="Markdown", ) assert isinstance(message.voice, Voice) assert isinstance(message.voice.file_id, str) assert isinstance(message.voice.file_unique_id, str) - assert message.voice.file_id != '' - assert message.voice.file_unique_id != '' + assert message.voice.file_id != "" + assert message.voice.file_unique_id != "" assert message.voice.duration == voice.duration assert message.voice.mime_type == voice.mime_type assert message.voice.file_size == voice.file_size - assert message.caption == self.caption.replace('*', '') + assert message.caption == self.caption.replace("*", "") @flaky(3, 1) def test_send_voice_custom_filename(self, bot, chat_id, voice_file, monkeypatch): def make_assertion(url, data, **kwargs): - return data['voice'].filename == 'custom_filename' + return data["voice"].filename == "custom_filename" - monkeypatch.setattr(bot.request, 'post', make_assertion) + monkeypatch.setattr(bot.request, "post", make_assertion) - assert bot.send_voice(chat_id, voice_file, filename='custom_filename') + assert bot.send_voice(chat_id, voice_file, filename="custom_filename") @flaky(3, 1) def test_get_and_download(self, bot, voice): @@ -110,11 +107,11 @@ def test_get_and_download(self, bot, voice): assert new_file.file_size == voice.file_size assert new_file.file_id == voice.file_id assert new_file.file_unique_id == voice.file_unique_id - assert new_file.file_path.startswith('https://') + assert new_file.file_path.startswith("https://") - new_file.download('telegram.ogg') + new_file.download("telegram.ogg") - assert os.path.isfile('telegram.ogg') + assert os.path.isfile("telegram.ogg") @flaky(3, 1) def test_send_ogg_url_file(self, bot, chat_id, voice): @@ -123,8 +120,8 @@ def test_send_ogg_url_file(self, bot, chat_id, voice): assert isinstance(message.voice, Voice) assert isinstance(message.voice.file_id, str) assert isinstance(message.voice.file_unique_id, str) - assert message.voice.file_id != '' - assert message.voice.file_unique_id != '' + assert message.voice.file_id != "" + assert message.voice.file_unique_id != "" assert message.voice.duration == voice.duration assert message.voice.mime_type == voice.mime_type assert message.voice.file_size == voice.file_size @@ -137,15 +134,15 @@ def test_resend(self, bot, chat_id, voice): def test_send_with_voice(self, monkeypatch, bot, chat_id, voice): def test(url, data, **kwargs): - return data['voice'] == voice.file_id + return data["voice"] == voice.file_id - monkeypatch.setattr(bot.request, 'post', test) + monkeypatch.setattr(bot.request, "post", test) message = bot.send_voice(chat_id, voice=voice) assert message @flaky(3, 1) def test_send_voice_caption_entities(self, bot, chat_id, voice_file): - test_string = 'Italic Bold Code' + test_string = "Italic Bold Code" entities = [ MessageEntity(MessageEntity.ITALIC, 0, 6), MessageEntity(MessageEntity.ITALIC, 7, 4), @@ -159,19 +156,19 @@ def test_send_voice_caption_entities(self, bot, chat_id, voice_file): assert message.caption_entities == entities @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_voice_default_parse_mode_1(self, default_bot, chat_id, voice): - test_string = 'Italic Bold Code' - test_markdown_string = '_Italic_ *Bold* `Code`' + test_string = "Italic Bold Code" + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_voice(chat_id, voice, caption=test_markdown_string) assert message.caption_markdown == test_markdown_string assert message.caption == test_string @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_voice_default_parse_mode_2(self, default_bot, chat_id, voice): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_voice( chat_id, voice, caption=test_markdown_string, parse_mode=None @@ -180,12 +177,12 @@ def test_send_voice_default_parse_mode_2(self, default_bot, chat_id, voice): assert message.caption_markdown == escape_markdown(test_markdown_string) @flaky(3, 1) - @pytest.mark.parametrize('default_bot', [{'parse_mode': 'Markdown'}], indirect=True) + @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True) def test_send_voice_default_parse_mode_3(self, default_bot, chat_id, voice): - test_markdown_string = '_Italic_ *Bold* `Code`' + test_markdown_string = "_Italic_ *Bold* `Code`" message = default_bot.send_voice( - chat_id, voice, caption=test_markdown_string, parse_mode='HTML' + chat_id, voice, caption=test_markdown_string, parse_mode="HTML" ) assert message.caption == test_markdown_string assert message.caption_markdown == escape_markdown(test_markdown_string) @@ -193,32 +190,32 @@ def test_send_voice_default_parse_mode_3(self, default_bot, chat_id, voice): def test_send_voice_local_files(self, monkeypatch, bot, chat_id): # For just test that the correct paths are passed as we have no local bot API set up test_flag = False - expected = (Path.cwd() / 'tests/data/telegram.jpg/').as_uri() - file = 'tests/data/telegram.jpg' + expected = (Path.cwd() / "tests/data/telegram.jpg/").as_uri() + file = "tests/data/telegram.jpg" def make_assertion(_, data, *args, **kwargs): nonlocal test_flag - test_flag = data.get('voice') == expected + test_flag = data.get("voice") == expected - monkeypatch.setattr(bot, '_post', make_assertion) + monkeypatch.setattr(bot, "_post", make_assertion) bot.send_voice(chat_id, file) assert test_flag - monkeypatch.delattr(bot, '_post') + monkeypatch.delattr(bot, "_post") @flaky(3, 1) @pytest.mark.parametrize( - 'default_bot,custom', + "default_bot,custom", [ - ({'allow_sending_without_reply': True}, None), - ({'allow_sending_without_reply': False}, None), - ({'allow_sending_without_reply': False}, True), + ({"allow_sending_without_reply": True}, None), + ({"allow_sending_without_reply": False}, None), + ({"allow_sending_without_reply": False}, True), ], - indirect=['default_bot'], + indirect=["default_bot"], ) def test_send_voice_default_allow_sending_without_reply( self, default_bot, chat_id, voice, custom ): - reply_to_message = default_bot.send_message(chat_id, 'test') + reply_to_message = default_bot.send_message(chat_id, "test") reply_to_message.delete() if custom is not None: message = default_bot.send_voice( @@ -234,19 +231,19 @@ def test_send_voice_default_allow_sending_without_reply( ) assert message.reply_to_message is None else: - with pytest.raises(BadRequest, match='message not found'): + with pytest.raises(BadRequest, match="message not found"): default_bot.send_voice( chat_id, voice, reply_to_message_id=reply_to_message.message_id ) def test_de_json(self, bot): json_dict = { - 'file_id': self.voice_file_id, - 'file_unique_id': self.voice_file_unique_id, - 'duration': self.duration, - 'caption': self.caption, - 'mime_type': self.mime_type, - 'file_size': self.file_size, + "file_id": self.voice_file_id, + "file_unique_id": self.voice_file_unique_id, + "duration": self.duration, + "caption": self.caption, + "mime_type": self.mime_type, + "file_size": self.file_size, } json_voice = Voice.de_json(json_dict, bot) @@ -260,21 +257,21 @@ def test_to_dict(self, voice): voice_dict = voice.to_dict() assert isinstance(voice_dict, dict) - assert voice_dict['file_id'] == voice.file_id - assert voice_dict['file_unique_id'] == voice.file_unique_id - assert voice_dict['duration'] == voice.duration - assert voice_dict['mime_type'] == voice.mime_type - assert voice_dict['file_size'] == voice.file_size + assert voice_dict["file_id"] == voice.file_id + assert voice_dict["file_unique_id"] == voice.file_unique_id + assert voice_dict["duration"] == voice.duration + assert voice_dict["mime_type"] == voice.mime_type + assert voice_dict["file_size"] == voice.file_size @flaky(3, 1) def test_error_send_empty_file(self, bot, chat_id): with pytest.raises(TelegramError): - bot.sendVoice(chat_id, open(os.devnull, 'rb')) + bot.sendVoice(chat_id, open(os.devnull, "rb")) @flaky(3, 1) def test_error_send_empty_file_id(self, bot, chat_id): with pytest.raises(TelegramError): - bot.sendVoice(chat_id, '') + bot.sendVoice(chat_id, "") def test_error_without_required_args(self, bot, chat_id): with pytest.raises(TypeError): @@ -282,20 +279,20 @@ def test_error_without_required_args(self, bot, chat_id): def test_get_file_instance_method(self, monkeypatch, voice): def make_assertion(*_, **kwargs): - return kwargs['file_id'] == voice.file_id + return kwargs["file_id"] == voice.file_id - assert check_shortcut_signature(Voice.get_file, Bot.get_file, ['file_id'], []) - assert check_shortcut_call(voice.get_file, voice.bot, 'get_file') + assert check_shortcut_signature(Voice.get_file, Bot.get_file, ["file_id"], []) + assert check_shortcut_call(voice.get_file, voice.bot, "get_file") assert check_defaults_handling(voice.get_file, voice.bot) - monkeypatch.setattr(voice.bot, 'get_file', make_assertion) + monkeypatch.setattr(voice.bot, "get_file", make_assertion) assert voice.get_file() def test_equality(self, voice): a = Voice(voice.file_id, voice.file_unique_id, self.duration) - b = Voice('', voice.file_unique_id, self.duration) + b = Voice("", voice.file_unique_id, self.duration) c = Voice(voice.file_id, voice.file_unique_id, 0) - d = Voice('', '', self.duration) + d = Voice("", "", self.duration) e = Audio(voice.file_id, voice.file_unique_id, self.duration) assert a == b diff --git a/tests/test_voicechat.py b/tests/test_voicechat.py index 8969a2e01b2..97ef66738ba 100644 --- a/tests/test_voicechat.py +++ b/tests/test_voicechat.py @@ -29,25 +29,22 @@ from telegram.utils.helpers import to_timestamp -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def user1(): - return User(first_name='Misses Test', id=123, is_bot=False) + return User(first_name="Misses Test", id=123, is_bot=False) -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def user2(): - return User(first_name='Mister Test', id=124, is_bot=False) + return User(first_name="Mister Test", id=124, is_bot=False) class TestVoiceChatStarted: - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): action = VoiceChatStarted() for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self): voice_chat_started = VoiceChatStarted.de_json({}, None) @@ -62,17 +59,14 @@ def test_to_dict(self): class TestVoiceChatEnded: duration = 100 - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): action = VoiceChatEnded(8) for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self): - json_dict = {'duration': self.duration} + json_dict = {"duration": self.duration} voice_chat_ended = VoiceChatEnded.de_json(json_dict, None) assert voice_chat_ended.duration == self.duration @@ -101,14 +95,11 @@ def test_equality(self): class TestVoiceChatParticipantsInvited: - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots, user1): action = VoiceChatParticipantsInvited([user1]) for attr in action.__slots__: - assert getattr(action, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not action.__dict__, f"got missing slot(s): {action.__dict__}" + assert getattr(action, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(action)) == len(set(mro_slots(action))), "duplicate slot" - action.custom = 'should give warning' - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_de_json(self, user1, user2, bot): json_data = {"users": [user1.to_dict(), user2.to_dict()]} @@ -133,7 +124,7 @@ def test_equality(self, user1, user2): a = VoiceChatParticipantsInvited([user1]) b = VoiceChatParticipantsInvited([user1]) c = VoiceChatParticipantsInvited([user1, user2]) - d = VoiceChatParticipantsInvited([user2]) + d = VoiceChatParticipantsInvited(None) e = VoiceChatStarted() assert a == b @@ -152,14 +143,11 @@ def test_equality(self, user1, user2): class TestVoiceChatScheduled: start_date = dtm.datetime.utcnow() - def test_slot_behaviour(self, recwarn, mro_slots): + def test_slot_behaviour(self, mro_slots): inst = VoiceChatScheduled(self.start_date) for attr in inst.__slots__: - assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not inst.__dict__, f"got missing slot(s): {inst.__dict__}" + assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - inst.custom, inst.start_date = 'should give warning', self.start_date - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_expected_values(self): assert pytest.approx(VoiceChatScheduled(start_date=self.start_date) == self.start_date) @@ -167,7 +155,7 @@ def test_expected_values(self): def test_de_json(self, bot): assert VoiceChatScheduled.de_json({}, bot=bot) is None - json_dict = {'start_date': to_timestamp(self.start_date)} + json_dict = {"start_date": to_timestamp(self.start_date)} voice_chat_scheduled = VoiceChatScheduled.de_json(json_dict, bot) assert pytest.approx(voice_chat_scheduled.start_date == self.start_date) diff --git a/tests/test_webhookinfo.py b/tests/test_webhookinfo.py index 9b07932f508..6875b901981 100644 --- a/tests/test_webhookinfo.py +++ b/tests/test_webhookinfo.py @@ -22,7 +22,7 @@ from telegram import WebhookInfo, LoginUrl -@pytest.fixture(scope='class') +@pytest.fixture(scope="class") def webhook_info(): return WebhookInfo( url=TestWebhookInfo.url, @@ -39,29 +39,26 @@ class TestWebhookInfo: url = "http://www.google.com" has_custom_certificate = False pending_update_count = 5 - ip_address = '127.0.0.1' + ip_address = "127.0.0.1" last_error_date = time.time() max_connections = 42 - allowed_updates = ['type1', 'type2'] + allowed_updates = ["type1", "type2"] - def test_slot_behaviour(self, webhook_info, mro_slots, recwarn): + def test_slot_behaviour(self, webhook_info, mro_slots): for attr in webhook_info.__slots__: - assert getattr(webhook_info, attr, 'err') != 'err', f"got extra slot '{attr}'" - assert not webhook_info.__dict__, f"got missing slot(s): {webhook_info.__dict__}" + assert getattr(webhook_info, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(webhook_info)) == len(set(mro_slots(webhook_info))), "duplicate slot" - webhook_info.custom, webhook_info.url = 'should give warning', self.url - assert len(recwarn) == 1 and 'custom' in str(recwarn[0].message), recwarn.list def test_to_dict(self, webhook_info): webhook_info_dict = webhook_info.to_dict() assert isinstance(webhook_info_dict, dict) - assert webhook_info_dict['url'] == self.url - assert webhook_info_dict['pending_update_count'] == self.pending_update_count - assert webhook_info_dict['last_error_date'] == self.last_error_date - assert webhook_info_dict['max_connections'] == self.max_connections - assert webhook_info_dict['allowed_updates'] == self.allowed_updates - assert webhook_info_dict['ip_address'] == self.ip_address + assert webhook_info_dict["url"] == self.url + assert webhook_info_dict["pending_update_count"] == self.pending_update_count + assert webhook_info_dict["last_error_date"] == self.last_error_date + assert webhook_info_dict["max_connections"] == self.max_connections + assert webhook_info_dict["allowed_updates"] == self.allowed_updates + assert webhook_info_dict["ip_address"] == self.ip_address def test_equality(self): a = WebhookInfo(