From 92b9370c234fe37307f6c0940b7d78361386b680 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi Date: Sat, 31 Oct 2020 16:33:34 +0100 Subject: [PATCH] Improve Code Quality (#2131) * Make pre-commit more strict * Get pylint to read setup.cfg * Make pylint & mypy happy aka ignore all the things * use LogRecord.getMessage() in tests * Make noam happy * Update both pylint & mypy while we're at it * Bring reqs-dev and makefile up to speed * try making pre-commit happy * fix jobqueue tests on the fly --- .pre-commit-config.yaml | 18 +-- Makefile | 25 ++-- examples/conversationbot.py | 35 ++++-- examples/conversationbot2.py | 32 +++-- examples/deeplinking.py | 27 +++-- examples/echobot.py | 19 +-- examples/errorhandlerbot.py | 20 ++-- examples/inlinebot.py | 22 ++-- examples/inlinekeyboard.py | 12 +- examples/inlinekeyboard2.py | 32 +++-- examples/nestedconversationbot.py | 68 +++++------ examples/passportbot.py | 11 +- examples/paymentbot.py | 47 ++++---- examples/persistentconversationbot.py | 20 ++-- examples/pollbot.py | 40 ++++--- examples/rawapibot.py | 29 +++-- examples/timerbot.py | 21 ++-- requirements-dev.txt | 19 +-- setup.cfg | 14 ++- telegram/__main__.py | 6 +- telegram/base.py | 7 +- telegram/bot.py | 109 ++++++++--------- telegram/botcommand.py | 5 +- telegram/callbackquery.py | 112 +++++++++--------- telegram/chat.py | 15 +-- telegram/chatmember.py | 9 +- telegram/chatpermissions.py | 5 +- telegram/choseninlineresult.py | 9 +- telegram/dice.py | 5 +- telegram/error.py | 4 +- telegram/ext/basepersistence.py | 15 ++- telegram/ext/callbackcontext.py | 7 +- telegram/ext/callbackqueryhandler.py | 22 ++-- telegram/ext/choseninlineresulthandler.py | 7 +- telegram/ext/commandhandler.py | 32 +++-- telegram/ext/conversationhandler.py | 61 +++++----- telegram/ext/defaults.py | 4 +- telegram/ext/dictpersistence.py | 42 ++++--- telegram/ext/dispatcher.py | 60 +++++----- telegram/ext/filters.py | 35 +++--- telegram/ext/handler.py | 28 ++--- telegram/ext/inlinequeryhandler.py | 21 ++-- telegram/ext/jobqueue.py | 36 +++--- telegram/ext/messagehandler.py | 11 +- telegram/ext/messagequeue.py | 14 +-- telegram/ext/picklepersistence.py | 41 +++---- telegram/ext/pollanswerhandler.py | 5 +- telegram/ext/pollhandler.py | 5 +- telegram/ext/precheckoutqueryhandler.py | 4 +- telegram/ext/regexhandler.py | 8 +- telegram/ext/shippingqueryhandler.py | 4 +- telegram/ext/stringcommandhandler.py | 5 +- telegram/ext/stringregexhandler.py | 6 +- telegram/ext/typehandler.py | 10 +- telegram/ext/updater.py | 40 +++---- telegram/files/animation.py | 7 +- telegram/files/audio.py | 6 +- telegram/files/chatphoto.py | 6 +- telegram/files/contact.py | 5 +- telegram/files/document.py | 6 +- telegram/files/file.py | 40 +++---- telegram/files/inputfile.py | 3 +- telegram/files/inputmedia.py | 9 +- telegram/files/location.py | 5 +- telegram/files/photosize.py | 5 +- telegram/files/sticker.py | 13 +- telegram/files/venue.py | 7 +- telegram/files/video.py | 5 +- telegram/files/videonote.py | 5 +- telegram/files/voice.py | 5 +- telegram/forcereply.py | 7 +- telegram/games/game.py | 11 +- telegram/games/gamehighscore.py | 3 +- telegram/inline/inlinekeyboardbutton.py | 5 +- telegram/inline/inlinekeyboardmarkup.py | 11 +- telegram/inline/inlinequery.py | 13 +- telegram/inline/inlinequeryresult.py | 8 +- telegram/inline/inlinequeryresultarticle.py | 7 +- telegram/inline/inlinequeryresultaudio.py | 7 +- .../inline/inlinequeryresultcachedaudio.py | 7 +- .../inline/inlinequeryresultcacheddocument.py | 8 +- telegram/inline/inlinequeryresultcachedgif.py | 7 +- .../inline/inlinequeryresultcachedmpeg4gif.py | 7 +- .../inline/inlinequeryresultcachedphoto.py | 8 +- .../inline/inlinequeryresultcachedsticker.py | 9 +- .../inline/inlinequeryresultcachedvideo.py | 7 +- .../inline/inlinequeryresultcachedvoice.py | 7 +- telegram/inline/inlinequeryresultcontact.py | 9 +- telegram/inline/inlinequeryresultdocument.py | 7 +- telegram/inline/inlinequeryresultgame.py | 11 +- telegram/inline/inlinequeryresultgif.py | 8 +- telegram/inline/inlinequeryresultlocation.py | 9 +- telegram/inline/inlinequeryresultmpeg4gif.py | 7 +- telegram/inline/inlinequeryresultphoto.py | 7 +- telegram/inline/inlinequeryresultvenue.py | 9 +- telegram/inline/inlinequeryresultvideo.py | 7 +- telegram/inline/inlinequeryresultvoice.py | 7 +- telegram/inline/inputcontactmessagecontent.py | 5 +- .../inline/inputlocationmessagecontent.py | 7 +- telegram/inline/inputtextmessagecontent.py | 5 +- telegram/inline/inputvenuemessagecontent.py | 5 +- telegram/keyboardbutton.py | 5 +- telegram/keyboardbuttonpolltype.py | 5 +- telegram/loginurl.py | 5 +- telegram/message.py | 65 +++++----- telegram/messageentity.py | 9 +- telegram/passport/credentials.py | 38 +++--- telegram/passport/data.py | 12 +- telegram/passport/encryptedpassportelement.py | 17 +-- telegram/passport/passportdata.py | 6 +- telegram/passport/passportelementerrors.py | 52 +++++--- telegram/passport/passportfile.py | 5 +- telegram/payment/invoice.py | 5 +- telegram/payment/labeledprice.py | 5 +- telegram/payment/orderinfo.py | 7 +- telegram/payment/precheckoutquery.py | 11 +- telegram/payment/shippingaddress.py | 5 +- telegram/payment/shippingoption.py | 13 +- telegram/payment/shippingquery.py | 11 +- telegram/payment/successfulpayment.py | 7 +- telegram/poll.py | 27 +++-- telegram/replykeyboardmarkup.py | 31 ++--- telegram/replykeyboardremove.py | 5 +- telegram/replymarkup.py | 2 - telegram/update.py | 19 +-- telegram/user.py | 8 +- telegram/userprofilephotos.py | 7 +- telegram/utils/deprecate.py | 6 +- telegram/utils/helpers.py | 70 +++++------ telegram/utils/promise.py | 5 +- telegram/utils/request.py | 68 +++++------ telegram/utils/types.py | 2 +- telegram/utils/webhookhandler.py | 53 +++++---- telegram/version.py | 1 + telegram/webhookinfo.py | 5 +- tests/test_conversationhandler.py | 26 ++-- tests/test_dispatcher.py | 20 ++-- tests/test_jobqueue.py | 26 ++-- tests/test_persistence.py | 10 +- tests/test_updater.py | 4 +- 140 files changed, 1279 insertions(+), 1081 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 702c4cad3..01bfd7fb5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +# Make sure that +# * the revs specified here match requirements-dev.txt +# * the makefile checks the same files as pre-commit repos: - repo: https://github.com/psf/black rev: 20.8b1 @@ -7,19 +10,18 @@ repos: - --diff - --check - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.1 + rev: 3.8.4 hooks: - id: flake8 -- repo: git://github.com/pre-commit/mirrors-pylint - rev: v2.5.3 +- repo: https://github.com/PyCQA/pylint + rev: pylint-2.6.0 hooks: - id: pylint - files: ^telegram/.*\.py$ + files: ^(telegram|examples)/.*\.py$ args: - - --errors-only - - --disable=import-error + - --rcfile=setup.cfg - repo: https://github.com/pre-commit/mirrors-mypy - rev: 'v0.770' + rev: v0.790 hooks: - id: mypy - files: ^telegram/.*\.py$ + files: ^(telegram|examples)/.*\.py$ diff --git a/Makefile b/Makefile index 3060dbc80..81f0dbf1c 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,10 @@ .DEFAULT_GOAL := help -.PHONY: clean pep257 pep8 yapf lint test install +.PHONY: clean pep8 black lint test install PYLINT := pylint PYTEST := pytest -PEP257 := pep257 PEP8 := flake8 -YAPF := yapf +BLACK := black MYPY := mypy PIP := pip @@ -15,22 +14,20 @@ clean: find . -name '*.pyc' -exec rm -f {} \; find . -name '*.pyo' -exec rm -f {} \; find . -name '*~' -exec rm -f {} \; - find . -regex "./telegram.\(mp3\|mp4\|ogg\|png\|webp\)" -exec rm {} \; - -pep257: - $(PEP257) telegram + find . -regex "./telegram[0-9]*.\(jpg\|mp3\|mp4\|ogg\|png\|webp\)" -exec rm {} \; pep8: - $(PEP8) telegram + $(PEP8) telegram tests examples -yapf: - $(YAPF) -r telegram +black: + $(BLACK) . lint: - $(PYLINT) -E telegram --disable=no-name-in-module,import-error + $(PYLINT) --rcfile=setup.cfg telegram examples mypy: $(MYPY) -p telegram + $(MYPY) examples test: $(PYTEST) -v @@ -41,18 +38,16 @@ install: help: @echo "Available targets:" @echo "- clean Clean up the source directory" - @echo "- pep257 Check docstring style with pep257" @echo "- pep8 Check style with flake8" @echo "- lint Check style with pylint" - @echo "- yapf Check style with yapf" + @echo "- black Check style with black" @echo "- mypy Check type hinting with mypy" @echo "- test Run tests using pytest" @echo @echo "Available variables:" @echo "- PYLINT default: $(PYLINT)" @echo "- PYTEST default: $(PYTEST)" - @echo "- PEP257 default: $(PEP257)" @echo "- PEP8 default: $(PEP8)" - @echo "- YAPF default: $(YAPF)" + @echo "- BLACK default: $(BLACK)" @echo "- MYPY default: $(MYPY)" @echo "- PIP default: $(PIP)" diff --git a/examples/conversationbot.py b/examples/conversationbot.py index c72d02804..0837b9475 100644 --- a/examples/conversationbot.py +++ b/examples/conversationbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -16,8 +18,15 @@ bot. import logging -from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove -from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler +from telegram import ReplyKeyboardMarkup, ReplyKeyboardRemove, Update +from telegram.ext import ( + Updater, + CommandHandler, + MessageHandler, + Filters, + ConversationHandler, + CallbackContext, +) # Enable logging logging.basicConfig( @@ -29,7 +38,7 @@ logger = logging.getLogger(__name__) GENDER, PHOTO, LOCATION, BIO = range(4) -def start(update, context): +def start(update: Update, context: CallbackContext) -> int: reply_keyboard = [['Boy', 'Girl', 'Other']] update.message.reply_text( @@ -42,7 +51,7 @@ def start(update, context): return GENDER -def gender(update, context): +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( @@ -54,7 +63,7 @@ def gender(update, context): return PHOTO -def photo(update, context): +def photo(update: Update, context: CallbackContext) -> int: user = update.message.from_user photo_file = update.message.photo[-1].get_file() photo_file.download('user_photo.jpg') @@ -66,7 +75,7 @@ def photo(update, context): return LOCATION -def skip_photo(update, context): +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( @@ -76,7 +85,7 @@ def skip_photo(update, context): return LOCATION -def location(update, context): +def location(update: Update, context: CallbackContext) -> int: user = update.message.from_user user_location = update.message.location logger.info( @@ -89,7 +98,7 @@ def location(update, context): return BIO -def skip_location(update, context): +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( @@ -99,7 +108,7 @@ def skip_location(update, context): return BIO -def bio(update, context): +def bio(update: Update, context: CallbackContext) -> int: 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.') @@ -107,7 +116,7 @@ def bio(update, context): return ConversationHandler.END -def cancel(update, context): +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( @@ -117,14 +126,14 @@ def cancel(update, context): return ConversationHandler.END -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. # Make sure to set use_context=True to use the new context based callbacks # Post version 12 this will no longer be necessary updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # Add conversation handler with the states GENDER, PHOTO, LOCATION and BIO conv_handler = ConversationHandler( @@ -141,7 +150,7 @@ def main(): fallbacks=[CommandHandler('cancel', cancel)], ) - dp.add_handler(conv_handler) + dispatcher.add_handler(conv_handler) # Start the Bot updater.start_polling() diff --git a/examples/conversationbot2.py b/examples/conversationbot2.py index 4f08ef130..ce6d9ca70 100644 --- a/examples/conversationbot2.py +++ b/examples/conversationbot2.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -15,9 +17,17 @@ bot. """ import logging +from typing import Dict -from telegram import ReplyKeyboardMarkup -from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ConversationHandler +from telegram import ReplyKeyboardMarkup, Update +from telegram.ext import ( + Updater, + CommandHandler, + MessageHandler, + Filters, + ConversationHandler, + CallbackContext, +) # Enable logging logging.basicConfig( @@ -36,7 +46,7 @@ reply_keyboard = [ markup = ReplyKeyboardMarkup(reply_keyboard, one_time_keyboard=True) -def facts_to_str(user_data): +def facts_to_str(user_data: Dict[str, str]) -> str: facts = list() for key, value in user_data.items(): @@ -45,7 +55,7 @@ def facts_to_str(user_data): return "\n".join(facts).join(['\n', '\n']) -def start(update, context): +def start(update: Update, context: CallbackContext) -> int: update.message.reply_text( "Hi! My name is Doctor Botter. I will hold a more complex conversation with you. " "Why don't you tell me something about yourself?", @@ -55,7 +65,7 @@ def start(update, context): return CHOOSING -def regular_choice(update, context): +def regular_choice(update: Update, context: CallbackContext) -> int: text = update.message.text context.user_data['choice'] = text update.message.reply_text( @@ -65,7 +75,7 @@ def regular_choice(update, context): return TYPING_REPLY -def custom_choice(update, context): +def custom_choice(update: Update, context: CallbackContext) -> int: update.message.reply_text( 'Alright, please send me the category first, ' 'for example "Most impressive skill"' ) @@ -73,7 +83,7 @@ def custom_choice(update, context): return TYPING_CHOICE -def received_information(update, context): +def received_information(update: Update, context: CallbackContext) -> int: user_data = context.user_data text = update.message.text category = user_data['choice'] @@ -90,7 +100,7 @@ def received_information(update, context): return CHOOSING -def done(update, context): +def done(update: Update, context: CallbackContext) -> int: user_data = context.user_data if 'choice' in user_data: del user_data['choice'] @@ -103,14 +113,14 @@ def done(update, context): return ConversationHandler.END -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. # Make sure to set use_context=True to use the new context based callbacks # Post version 12 this will no longer be necessary updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # Add conversation handler with the states CHOOSING, TYPING_CHOICE and TYPING_REPLY conv_handler = ConversationHandler( @@ -137,7 +147,7 @@ def main(): fallbacks=[MessageHandler(Filters.regex('^Done$'), done)], ) - dp.add_handler(conv_handler) + dispatcher.add_handler(conv_handler) # Start the Bot updater.start_polling() diff --git a/examples/deeplinking.py b/examples/deeplinking.py index 1a71e959d..23e30275e 100644 --- a/examples/deeplinking.py +++ b/examples/deeplinking.py @@ -1,5 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] +# This program is dedicated to the public domain under the CC0 license. """Bot that explains Telegram's "Deep Linking Parameters" functionality. @@ -19,8 +22,8 @@ bot. import logging -from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton -from telegram.ext import Updater, CommandHandler, Filters +from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton, Update +from telegram.ext import Updater, CommandHandler, Filters, CallbackContext # Enable logging from telegram.utils import helpers @@ -37,7 +40,7 @@ USING_ENTITIES = 'using-entities-here' SO_COOL = 'so-cool' -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: """Send a deep-linked URL when the command /start is issued.""" bot = context.bot url = helpers.create_deep_linked_url(bot.get_me().username, CHECK_THIS_OUT, group=True) @@ -45,7 +48,7 @@ def start(update, context): update.message.reply_text(text) -def deep_linked_level_1(update, context): +def deep_linked_level_1(update: Update, context: CallbackContext) -> None: """Reached through the CHECK_THIS_OUT payload""" bot = context.bot url = helpers.create_deep_linked_url(bot.get_me().username, SO_COOL) @@ -59,7 +62,7 @@ def deep_linked_level_1(update, context): update.message.reply_text(text, reply_markup=keyboard) -def deep_linked_level_2(update, context): +def deep_linked_level_2(update: Update, context: CallbackContext) -> None: """Reached through the SO_COOL payload""" bot = context.bot url = helpers.create_deep_linked_url(bot.get_me().username, USING_ENTITIES) @@ -67,7 +70,7 @@ def deep_linked_level_2(update, context): update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) -def deep_linked_level_3(update, context): +def deep_linked_level_3(update: Update, context: CallbackContext) -> None: """Reached through the USING_ENTITIES payload""" payload = context.args update.message.reply_text( @@ -81,24 +84,26 @@ def main(): updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # More info on what deep linking actually is (read this first if it's unclear to you): # https://core.telegram.org/bots#deep-linking # Register a deep-linking handler - dp.add_handler(CommandHandler("start", deep_linked_level_1, Filters.regex(CHECK_THIS_OUT))) + dispatcher.add_handler( + CommandHandler("start", deep_linked_level_1, Filters.regex(CHECK_THIS_OUT)) + ) # This one works with a textual link instead of an URL - dp.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL))) + dispatcher.add_handler(CommandHandler("start", deep_linked_level_2, Filters.regex(SO_COOL))) # We can also pass on the deep-linking payload - dp.add_handler( + dispatcher.add_handler( CommandHandler("start", deep_linked_level_3, Filters.regex(USING_ENTITIES), pass_args=True) ) # Make sure the deep-linking handlers occur *before* the normal /start handler. - dp.add_handler(CommandHandler("start", start)) + dispatcher.add_handler(CommandHandler("start", start)) # Start the Bot updater.start_polling() diff --git a/examples/echobot.py b/examples/echobot.py index 8dfb1aa03..c8a264584 100644 --- a/examples/echobot.py +++ b/examples/echobot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -17,7 +19,8 @@ bot. import logging -from telegram.ext import Updater, CommandHandler, MessageHandler, Filters +from telegram import Update +from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext # Enable logging logging.basicConfig( @@ -29,17 +32,17 @@ logger = logging.getLogger(__name__) # Define a few command handlers. These usually take the two arguments update and # context. Error handlers also receive the raised TelegramError object in error. -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: """Send a message when the command /start is issued.""" update.message.reply_text('Hi!') -def help_command(update, context): +def help_command(update: Update, context: CallbackContext) -> None: """Send a message when the command /help is issued.""" update.message.reply_text('Help!') -def echo(update, context): +def echo(update: Update, context: CallbackContext) -> None: """Echo the user message.""" update.message.reply_text(update.message.text) @@ -52,14 +55,14 @@ def main(): updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # on different commands - answer in Telegram - dp.add_handler(CommandHandler("start", start)) - dp.add_handler(CommandHandler("help", help_command)) + dispatcher.add_handler(CommandHandler("start", start)) + dispatcher.add_handler(CommandHandler("help", help_command)) # on noncommand i.e message - echo the message on Telegram - dp.add_handler(MessageHandler(Filters.text & ~Filters.command, echo)) + dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, echo)) # Start the Bot updater.start_polling() diff --git a/examples/errorhandlerbot.py b/examples/errorhandlerbot.py index 2f2133e15..4836be668 100644 --- a/examples/errorhandlerbot.py +++ b/examples/errorhandlerbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -27,7 +29,7 @@ BOT_TOKEN = "TOKEN" DEVELOPER_CHAT_ID = 123456789 -def error_handler(update: Update, context: CallbackContext): +def error_handler(update: Update, context: CallbackContext) -> None: """Log the error and send a telegram message to notify the developer.""" # Log the error before we do anything else, so we can see it even if something breaks. logger.error(msg="Exception while handling an update:", exc_info=context.error) @@ -35,7 +37,7 @@ def error_handler(update: Update, context: CallbackContext): # 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 = ''.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. @@ -49,19 +51,19 @@ def error_handler(update: Update, context: CallbackContext): html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)), html.escape(str(context.chat_data)), html.escape(str(context.user_data)), - html.escape(tb), + html.escape(tb_string), ) # Finally, send the message context.bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML) -def bad_command(update: Update, context: CallbackContext): +def bad_command(update: Update, context: CallbackContext) -> None: """Raise an error to trigger the error handler.""" context.bot.wrong_method_name() -def start(update: Update, context: CallbackContext): +def start(update: Update, context: CallbackContext) -> None: update.effective_message.reply_html( 'Use /bad_command to cause an error.\n' 'Your chat id is {}.'.format(update.effective_chat.id) @@ -75,14 +77,14 @@ def main(): updater = Updater(BOT_TOKEN, use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # Register the commands... - dp.add_handler(CommandHandler('start', start)) - dp.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 - dp.add_error_handler(error_handler) + dispatcher.add_error_handler(error_handler) # Start the Bot updater.start_polling() diff --git a/examples/inlinebot.py b/examples/inlinebot.py index dc5b63eca..f9f804cd4 100644 --- a/examples/inlinebot.py +++ b/examples/inlinebot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -15,8 +17,8 @@ bot. import logging from uuid import uuid4 -from telegram import InlineQueryResultArticle, ParseMode, InputTextMessageContent -from telegram.ext import Updater, InlineQueryHandler, CommandHandler +from telegram import InlineQueryResultArticle, ParseMode, InputTextMessageContent, Update +from telegram.ext import Updater, InlineQueryHandler, CommandHandler, CallbackContext from telegram.utils.helpers import escape_markdown # Enable logging @@ -29,17 +31,17 @@ logger = logging.getLogger(__name__) # Define a few command handlers. These usually take the two arguments update and # context. Error handlers also receive the raised TelegramError object in error. -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: """Send a message when the command /start is issued.""" update.message.reply_text('Hi!') -def help_command(update, context): +def help_command(update: Update, context: CallbackContext) -> None: """Send a message when the command /help is issued.""" update.message.reply_text('Help!') -def inlinequery(update, context): +def inlinequery(update: Update, context: CallbackContext) -> None: """Handle the inline query.""" query = update.inline_query.query results = [ @@ -65,21 +67,21 @@ def inlinequery(update, context): update.inline_query.answer(results) -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. # Make sure to set use_context=True to use the new context based callbacks # Post version 12 this will no longer be necessary updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # on different commands - answer in Telegram - dp.add_handler(CommandHandler("start", start)) - dp.add_handler(CommandHandler("help", help_command)) + dispatcher.add_handler(CommandHandler("start", start)) + dispatcher.add_handler(CommandHandler("help", help_command)) # on noncommand i.e message - echo the message on Telegram - dp.add_handler(InlineQueryHandler(inlinequery)) + dispatcher.add_handler(InlineQueryHandler(inlinequery)) # Start the Bot updater.start_polling() diff --git a/examples/inlinekeyboard.py b/examples/inlinekeyboard.py index 5eb452b64..70461a004 100644 --- a/examples/inlinekeyboard.py +++ b/examples/inlinekeyboard.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -7,8 +9,8 @@ Basic example for a bot that uses inline keyboards. """ import logging -from telegram import InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Updater, CommandHandler, CallbackQueryHandler +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update +from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, CallbackContext logging.basicConfig( format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO @@ -16,7 +18,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: keyboard = [ [ InlineKeyboardButton("Option 1", callback_data='1'), @@ -30,7 +32,7 @@ def start(update, context): update.message.reply_text('Please choose:', reply_markup=reply_markup) -def button(update, context): +def button(update: Update, context: CallbackContext) -> None: query = update.callback_query # CallbackQueries need to be answered, even if no notification to the user is needed @@ -40,7 +42,7 @@ def button(update, context): query.edit_message_text(text="Selected option: {}".format(query.data)) -def help_command(update, context): +def help_command(update: Update, context: CallbackContext) -> None: update.message.reply_text("Use /start to test this bot.") diff --git a/examples/inlinekeyboard2.py b/examples/inlinekeyboard2.py index aaca1bd89..4d6092bec 100644 --- a/examples/inlinekeyboard2.py +++ b/examples/inlinekeyboard2.py @@ -1,5 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] +# This program is dedicated to the public domain under the CC0 license. + """Simple inline keyboard bot with multiple CallbackQueryHandlers. This Bot uses the Updater class to handle the bot. @@ -12,9 +16,15 @@ ConversationHandler. Send /start to initiate the conversation. Press Ctrl-C on the command line to stop the bot. """ -from telegram import InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, ConversationHandler import logging +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update +from telegram.ext import ( + Updater, + CommandHandler, + CallbackQueryHandler, + ConversationHandler, + CallbackContext, +) # Enable logging logging.basicConfig( @@ -29,7 +39,7 @@ FIRST, SECOND = range(2) ONE, TWO, THREE, FOUR = range(4) -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: """Send message on `/start`.""" # Get user that sent /start and log his name user = update.message.from_user @@ -51,7 +61,7 @@ def start(update, context): return FIRST -def start_over(update, context): +def start_over(update: Update, context: CallbackContext) -> None: """Prompt same text & keyboard as `start` does but not as new message""" # Get CallbackQuery from Update query = update.callback_query @@ -72,7 +82,7 @@ def start_over(update, context): return FIRST -def one(update, context): +def one(update: Update, context: CallbackContext) -> None: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -89,7 +99,7 @@ def one(update, context): return FIRST -def two(update, context): +def two(update: Update, context: CallbackContext) -> None: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -106,7 +116,7 @@ def two(update, context): return FIRST -def three(update, context): +def three(update: Update, context: CallbackContext) -> None: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -124,7 +134,7 @@ def three(update, context): return SECOND -def four(update, context): +def four(update: Update, context: CallbackContext) -> None: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -141,7 +151,7 @@ def four(update, context): return FIRST -def end(update, context): +def end(update: Update, context: CallbackContext) -> None: """Returns `ConversationHandler.END`, which tells the ConversationHandler that the conversation is over""" query = update.callback_query @@ -155,7 +165,7 @@ def main(): updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # Setup conversation handler with the states FIRST and SECOND # Use the pattern parameter to pass CallbackQueries with specific @@ -182,7 +192,7 @@ def main(): # Add ConversationHandler to dispatcher that will be used for handling # updates - dp.add_handler(conv_handler) + dispatcher.add_handler(conv_handler) # Start the Bot updater.start_polling() diff --git a/examples/nestedconversationbot.py b/examples/nestedconversationbot.py index a50c8344a..ec9bb8410 100644 --- a/examples/nestedconversationbot.py +++ b/examples/nestedconversationbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -16,7 +18,7 @@ bot. import logging -from telegram import InlineKeyboardMarkup, InlineKeyboardButton +from telegram import InlineKeyboardMarkup, InlineKeyboardButton, Update from telegram.ext import ( Updater, CommandHandler, @@ -24,6 +26,7 @@ from telegram.ext import ( Filters, ConversationHandler, CallbackQueryHandler, + CallbackContext, ) # Enable logging @@ -64,13 +67,12 @@ END = ConversationHandler.END # Helper def _name_switcher(level): if level == PARENTS: - return ('Father', 'Mother') - elif level == CHILDREN: - return ('Brother', 'Sister') + return 'Father', 'Mother' + return 'Brother', 'Sister' # Top level conversation callbacks -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: """Select an action: Adding parent/child or show data.""" text = ( 'You may add a familiy member, yourself show the gathered data or end the ' @@ -102,7 +104,7 @@ def start(update, context): return SELECTING_ACTION -def adding_self(update, context): +def adding_self(update: Update, context: CallbackContext) -> None: """Add information about youself.""" context.user_data[CURRENT_LEVEL] = SELF text = 'Okay, please tell me about yourself.' @@ -115,7 +117,7 @@ def adding_self(update, context): return DESCRIBING_SELF -def show_data(update, context): +def show_data(update: Update, context: CallbackContext) -> None: """Pretty print gathered data.""" def prettyprint(user_data, level): @@ -137,29 +139,29 @@ def show_data(update, context): ) return text - ud = context.user_data - text = 'Yourself:' + prettyprint(ud, SELF) - text += '\n\nParents:' + prettyprint(ud, PARENTS) - text += '\n\nChildren:' + prettyprint(ud, CHILDREN) + user_data = context.user_data + text = 'Yourself:' + prettyprint(user_data, SELF) + text += '\n\nParents:' + prettyprint(user_data, PARENTS) + text += '\n\nChildren:' + prettyprint(user_data, CHILDREN) buttons = [[InlineKeyboardButton(text='Back', callback_data=str(END))]] keyboard = InlineKeyboardMarkup(buttons) update.callback_query.answer() update.callback_query.edit_message_text(text=text, reply_markup=keyboard) - ud[START_OVER] = True + user_data[START_OVER] = True return SHOWING -def stop(update, context): +def stop(update: Update, context: CallbackContext) -> None: """End Conversation by command.""" update.message.reply_text('Okay, bye.') return END -def end(update, context): +def end(update: Update, context: CallbackContext) -> None: """End conversation from InlineKeyboardButton.""" update.callback_query.answer() @@ -170,7 +172,7 @@ def end(update, context): # Second level conversation callbacks -def select_level(update, context): +def select_level(update: Update, context: CallbackContext) -> None: """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.' buttons = [ @@ -191,7 +193,7 @@ def select_level(update, context): return SELECTING_LEVEL -def select_gender(update, context): +def select_gender(update: Update, context: CallbackContext) -> None: """Choose to add mother or father.""" level = update.callback_query.data context.user_data[CURRENT_LEVEL] = level @@ -218,7 +220,7 @@ def select_gender(update, context): return SELECTING_GENDER -def end_second_level(update, context): +def end_second_level(update: Update, context: CallbackContext) -> None: """Return to top level conversation.""" context.user_data[START_OVER] = True start(update, context) @@ -227,7 +229,7 @@ def end_second_level(update, context): # Third level callbacks -def select_feature(update, context): +def select_feature(update: Update, context: CallbackContext) -> None: """Select a feature to update for the person.""" buttons = [ [ @@ -254,7 +256,7 @@ def select_feature(update, context): return SELECTING_FEATURE -def ask_for_input(update, context): +def ask_for_input(update: Update, context: CallbackContext) -> None: """Prompt user to input data for selected feature.""" context.user_data[CURRENT_FEATURE] = update.callback_query.data text = 'Okay, tell me.' @@ -265,27 +267,27 @@ def ask_for_input(update, context): return TYPING -def save_input(update, context): +def save_input(update: Update, context: CallbackContext) -> None: """Save input for feature and return to feature selection.""" - ud = context.user_data - ud[FEATURES][ud[CURRENT_FEATURE]] = update.message.text + user_data = context.user_data + user_data[FEATURES][user_data[CURRENT_FEATURE]] = update.message.text - ud[START_OVER] = True + user_data[START_OVER] = True return select_feature(update, context) -def end_describing(update, context): +def end_describing(update: Update, context: CallbackContext) -> None: """End gathering of features and return to parent conversation.""" - ud = context.user_data - level = ud[CURRENT_LEVEL] - if not ud.get(level): - ud[level] = [] - ud[level].append(ud[FEATURES]) + user_data = context.user_data + level = user_data[CURRENT_LEVEL] + if not user_data.get(level): + user_data[level] = [] + user_data[level].append(user_data[FEATURES]) # Print upper level menu if level == SELF: - ud[START_OVER] = True + user_data[START_OVER] = True start(update, context) else: select_level(update, context) @@ -293,7 +295,7 @@ def end_describing(update, context): return END -def stop_nested(update, context): +def stop_nested(update: Update, context: CallbackContext) -> None: """Completely end conversation from within nested conversation.""" update.message.reply_text('Okay, bye.') @@ -307,7 +309,7 @@ def main(): updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # Set up third level ConversationHandler (collecting features) description_conv = ConversationHandler( @@ -381,7 +383,7 @@ def main(): fallbacks=[CommandHandler('stop', stop)], ) - dp.add_handler(conv_handler) + dispatcher.add_handler(conv_handler) # Start the Bot updater.start_polling() diff --git a/examples/passportbot.py b/examples/passportbot.py index 22e512608..33372753e 100644 --- a/examples/passportbot.py +++ b/examples/passportbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -12,7 +14,8 @@ See https://git.io/fAvYd for how to use Telegram Passport properly with python-t """ import logging -from telegram.ext import Updater, MessageHandler, Filters +from telegram import Update +from telegram.ext import Updater, MessageHandler, Filters, CallbackContext # Enable logging logging.basicConfig( @@ -22,7 +25,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def msg(update, context): +def msg(update: Update, context: CallbackContext) -> None: # If we received any passport data passport_data = update.message.passport_data if passport_data: @@ -100,10 +103,10 @@ def main(): updater = Updater("TOKEN", private_key=open('private.key', 'rb').read()) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # On messages that include passport data call msg - dp.add_handler(MessageHandler(Filters.passport_data, msg)) + dispatcher.add_handler(MessageHandler(Filters.passport_data, msg)) # Start the Bot updater.start_polling() diff --git a/examples/paymentbot.py b/examples/paymentbot.py index b6879b381..7080e30a7 100644 --- a/examples/paymentbot.py +++ b/examples/paymentbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -8,7 +10,7 @@ Basic example for a bot that can receive payment from user. import logging -from telegram import LabeledPrice, ShippingOption +from telegram import LabeledPrice, ShippingOption, Update from telegram.ext import ( Updater, CommandHandler, @@ -16,6 +18,7 @@ from telegram.ext import ( Filters, PreCheckoutQueryHandler, ShippingQueryHandler, + CallbackContext, ) # Enable logging @@ -26,13 +29,13 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def start_callback(update, context): +def start_callback(update: Update, context: CallbackContext) -> None: msg = "Use /shipping to get an invoice for shipping-payment, " msg += "or /noshipping for an invoice without shipping." update.message.reply_text(msg) -def start_with_shipping_callback(update, context): +def start_with_shipping_callback(update: Update, context: CallbackContext) -> None: chat_id = update.message.chat_id title = "Payment Example" description = "Payment Example using python-telegram-bot" @@ -67,7 +70,7 @@ def start_with_shipping_callback(update, context): ) -def start_without_shipping_callback(update, context): +def start_without_shipping_callback(update: Update, context: CallbackContext) -> None: chat_id = update.message.chat_id title = "Payment Example" description = "Payment Example using python-telegram-bot" @@ -89,25 +92,25 @@ def start_without_shipping_callback(update, context): ) -def shipping_callback(update, context): +def shipping_callback(update: Update, context: CallbackContext) -> None: query = update.shipping_query # check the payload, is this from your bot? if query.invoice_payload != 'Custom-Payload': # answer False pre_checkout_query query.answer(ok=False, error_message="Something went wrong...") return - else: - options = list() - # a single LabeledPrice - options.append(ShippingOption('1', 'Shipping Option A', [LabeledPrice('A', 100)])) - # an array of LabeledPrice objects - price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)] - options.append(ShippingOption('2', 'Shipping Option B', price_list)) - query.answer(ok=True, shipping_options=options) + + options = list() + # a single LabeledPrice + options.append(ShippingOption('1', 'Shipping Option A', [LabeledPrice('A', 100)])) + # an array of LabeledPrice objects + price_list = [LabeledPrice('B1', 150), LabeledPrice('B2', 200)] + options.append(ShippingOption('2', 'Shipping Option B', price_list)) + query.answer(ok=True, shipping_options=options) # after (optional) shipping, it's the pre-checkout -def precheckout_callback(update, context): +def precheckout_callback(update: Update, context: CallbackContext) -> None: query = update.pre_checkout_query # check the payload, is this from your bot? if query.invoice_payload != 'Custom-Payload': @@ -118,7 +121,7 @@ def precheckout_callback(update, context): # finally, after contacting the payment provider... -def successful_payment_callback(update, context): +def successful_payment_callback(update: Update, context: CallbackContext) -> None: # do something after successfully receiving payment? update.message.reply_text("Thank you for your payment!") @@ -130,23 +133,23 @@ def main(): updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # simple start function - dp.add_handler(CommandHandler("start", start_callback)) + dispatcher.add_handler(CommandHandler("start", start_callback)) # Add command handler to start the payment invoice - dp.add_handler(CommandHandler("shipping", start_with_shipping_callback)) - dp.add_handler(CommandHandler("noshipping", start_without_shipping_callback)) + dispatcher.add_handler(CommandHandler("shipping", start_with_shipping_callback)) + dispatcher.add_handler(CommandHandler("noshipping", start_without_shipping_callback)) # Optional handler if your product requires shipping - dp.add_handler(ShippingQueryHandler(shipping_callback)) + dispatcher.add_handler(ShippingQueryHandler(shipping_callback)) # Pre-checkout handler to final check - dp.add_handler(PreCheckoutQueryHandler(precheckout_callback)) + dispatcher.add_handler(PreCheckoutQueryHandler(precheckout_callback)) # Success! Notify your user! - dp.add_handler(MessageHandler(Filters.successful_payment, successful_payment_callback)) + dispatcher.add_handler(MessageHandler(Filters.successful_payment, successful_payment_callback)) # Start the Bot updater.start_polling() diff --git a/examples/persistentconversationbot.py b/examples/persistentconversationbot.py index 4ea39b9da..0f2afb705 100644 --- a/examples/persistentconversationbot.py +++ b/examples/persistentconversationbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116, C0103 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -14,7 +16,9 @@ Press Ctrl-C on the command line or send a signal to the process to stop the bot. """ -from telegram import ReplyKeyboardMarkup +import logging + +from telegram import ReplyKeyboardMarkup, Update from telegram.ext import ( Updater, CommandHandler, @@ -22,9 +26,9 @@ from telegram.ext import ( Filters, ConversationHandler, PicklePersistence, + CallbackContext, ) -import logging # Enable logging logging.basicConfig( @@ -52,7 +56,7 @@ def facts_to_str(user_data): return "\n".join(facts).join(['\n', '\n']) -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: reply_text = "Hi! My name is Doctor Botter." if context.user_data: reply_text += ( @@ -70,7 +74,7 @@ def start(update, context): return CHOOSING -def regular_choice(update, context): +def regular_choice(update: Update, context: CallbackContext) -> None: text = update.message.text.lower() context.user_data['choice'] = text if context.user_data.get(text): @@ -84,7 +88,7 @@ def regular_choice(update, context): return TYPING_REPLY -def custom_choice(update, context): +def custom_choice(update: Update, context: CallbackContext) -> None: update.message.reply_text( 'Alright, please send me the category first, ' 'for example "Most impressive skill"' ) @@ -92,7 +96,7 @@ def custom_choice(update, context): return TYPING_CHOICE -def received_information(update, context): +def received_information(update: Update, context: CallbackContext) -> None: text = update.message.text category = context.user_data['choice'] context.user_data[category] = text.lower() @@ -109,13 +113,13 @@ def received_information(update, context): return CHOOSING -def show_data(update, context): +def show_data(update: Update, context: CallbackContext) -> None: update.message.reply_text( "This is what you already told me:" "{}".format(facts_to_str(context.user_data)) ) -def done(update, context): +def done(update: Update, context: CallbackContext) -> None: if 'choice' in context.user_data: del context.user_data['choice'] diff --git a/examples/pollbot.py b/examples/pollbot.py index b31fdbe73..b0540bf01 100644 --- a/examples/pollbot.py +++ b/examples/pollbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -16,6 +18,7 @@ from telegram import ( KeyboardButtonPollType, ReplyKeyboardMarkup, ReplyKeyboardRemove, + Update, ) from telegram.ext import ( Updater, @@ -24,6 +27,7 @@ from telegram.ext import ( PollHandler, MessageHandler, Filters, + CallbackContext, ) logging.basicConfig( @@ -32,7 +36,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def start(update, context): +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' @@ -40,7 +44,7 @@ def start(update, context): ) -def poll(update, context): +def poll(update: Update, context: CallbackContext) -> None: """Sends a predefined poll""" questions = ["Good", "Really good", "Fantastic", "Great"] message = context.bot.send_poll( @@ -62,7 +66,7 @@ def poll(update, context): context.bot_data.update(payload) -def receive_poll_answer(update, context): +def receive_poll_answer(update: Update, context: CallbackContext) -> None: """Summarize a users poll vote""" answer = update.poll_answer poll_id = answer.poll_id @@ -91,7 +95,7 @@ def receive_poll_answer(update, context): ) -def quiz(update, context): +def quiz(update: Update, context: CallbackContext) -> None: """Send a predefined poll""" questions = ["1", "2", "4", "20"] message = update.effective_message.reply_poll( @@ -104,7 +108,7 @@ def quiz(update, context): context.bot_data.update(payload) -def receive_quiz_answer(update, context): +def receive_quiz_answer(update: Update, context: CallbackContext) -> None: """Close quiz after three participants took it""" # the bot can receive closed poll updates we don't care about if update.poll.is_closed: @@ -118,7 +122,7 @@ def receive_quiz_answer(update, context): context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"]) -def preview(update, context): +def preview(update: Update, context: CallbackContext) -> None: """Ask user to create a poll and display a preview of it""" # using this without a type lets the user chooses what he wants (quiz or poll) button = [[KeyboardButton("Press me!", request_poll=KeyboardButtonPollType())]] @@ -129,7 +133,7 @@ def preview(update, context): ) -def receive_poll(update, context): +def receive_poll(update: Update, context: CallbackContext) -> None: """On receiving polls, reply to it by a closed poll copying the received poll""" actual_poll = update.effective_message.poll # Only need to set the question and options, since all other parameters don't matter for @@ -143,25 +147,25 @@ def receive_poll(update, context): ) -def help_handler(update, context): +def help_handler(update: Update, context: CallbackContext) -> None: """Display a help message""" update.message.reply_text("Use /quiz, /poll or /preview to test this " "bot.") -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. # Make sure to set use_context=True to use the new context based callbacks # Post version 12 this will no longer be necessary updater = Updater("TOKEN", use_context=True) - dp = updater.dispatcher - dp.add_handler(CommandHandler('start', start)) - dp.add_handler(CommandHandler('poll', poll)) - dp.add_handler(PollAnswerHandler(receive_poll_answer)) - dp.add_handler(CommandHandler('quiz', quiz)) - dp.add_handler(PollHandler(receive_quiz_answer)) - dp.add_handler(CommandHandler('preview', preview)) - dp.add_handler(MessageHandler(Filters.poll, receive_poll)) - dp.add_handler(CommandHandler('help', help_handler)) + dispatcher = updater.dispatcher + 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(PollHandler(receive_quiz_answer)) + dispatcher.add_handler(CommandHandler('preview', preview)) + dispatcher.add_handler(MessageHandler(Filters.poll, receive_poll)) + dispatcher.add_handler(CommandHandler('help', help_handler)) # Start the Bot updater.start_polling() diff --git a/examples/rawapibot.py b/examples/rawapibot.py index f2948ba1b..60129fe6e 100644 --- a/examples/rawapibot.py +++ b/examples/rawapibot.py @@ -1,5 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0603 """Simple Bot to reply to Telegram messages. This is built on the API wrapper, see rawapibot.py to see the same example built @@ -7,26 +8,28 @@ on the telegram.ext bot framework. This program is dedicated to the public domain under the CC0 license. """ import logging -import telegram -from telegram.error import NetworkError, Unauthorized +from typing import NoReturn from time import sleep - -update_id = None +import telegram +from telegram.error import NetworkError, Unauthorized -def main(): +UPDATE_ID = None + + +def main() -> NoReturn: """Run the bot.""" - global update_id + global UPDATE_ID # Telegram Bot Authorization 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. try: - update_id = bot.get_updates()[0].update_id + UPDATE_ID = bot.get_updates()[0].update_id except IndexError: - update_id = None + UPDATE_ID = None logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') @@ -37,15 +40,15 @@ def main(): sleep(1) except Unauthorized: # The user has removed or blocked the bot. - update_id += 1 + UPDATE_ID += 1 # type: ignore[operator] -def echo(bot): +def echo(bot: telegram.Bot) -> None: """Echo the message the user sent.""" - global update_id + global UPDATE_ID # Request updates after the last update_id - for update in bot.get_updates(offset=update_id, timeout=10): - update_id = update.update_id + 1 + for update in bot.get_updates(offset=UPDATE_ID, timeout=10): + UPDATE_ID = update.update_id + 1 if update.message: # your bot can receive updates without messages # Reply to the message diff --git a/examples/timerbot.py b/examples/timerbot.py index a87802d34..499677559 100644 --- a/examples/timerbot.py +++ b/examples/timerbot.py @@ -1,5 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# pylint: disable=W0613, C0116 +# type: ignore[union-attr] # This program is dedicated to the public domain under the CC0 license. """ @@ -20,7 +22,8 @@ bot. import logging -from telegram.ext import Updater, CommandHandler +from telegram import Update +from telegram.ext import Updater, CommandHandler, CallbackContext # Enable logging logging.basicConfig( @@ -32,7 +35,7 @@ logger = logging.getLogger(__name__) # Define a few command handlers. These usually take the two arguments update and # context. Error handlers also receive the raised TelegramError object in error. -def start(update, context): +def start(update: Update, context: CallbackContext) -> None: update.message.reply_text('Hi! Use /set to set a timer') @@ -52,7 +55,7 @@ def remove_job_if_exists(name, context): return True -def set_timer(update, context): +def set_timer(update: Update, context: CallbackContext) -> None: """Add a job to the queue.""" chat_id = update.message.chat_id try: @@ -74,7 +77,7 @@ def set_timer(update, context): update.message.reply_text('Usage: /set ') -def unset(update, context): +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) @@ -90,13 +93,13 @@ def main(): updater = Updater("TOKEN", use_context=True) # Get the dispatcher to register handlers - dp = updater.dispatcher + dispatcher = updater.dispatcher # on different commands - answer in Telegram - dp.add_handler(CommandHandler("start", start)) - dp.add_handler(CommandHandler("help", start)) - dp.add_handler(CommandHandler("set", set_timer)) - dp.add_handler(CommandHandler("unset", unset)) + dispatcher.add_handler(CommandHandler("start", start)) + dispatcher.add_handler(CommandHandler("help", start)) + dispatcher.add_handler(CommandHandler("set", set_timer)) + dispatcher.add_handler(CommandHandler("unset", unset)) # Start the Bot updater.start_polling() diff --git a/requirements-dev.txt b/requirements-dev.txt index be7c179c6..eec7eb8ef 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,12 +1,15 @@ -flake8 -pep257 -pylint -flaky -yapf -mypy==0.770 pre-commit -beautifulsoup4 +# Make sure that the versions specified here match the pre-commit settings +black==20.8b1 +flake8==3.8.4 +pylint==2.6.0 +mypy==0.790 + pytest==4.2.0 +# Need older attrs version for pytest 4.2.0 +attrs==19.1.0 + +flaky +beautifulsoup4 pytest-timeout wheel -attrs==19.1.0 diff --git a/setup.cfg b/setup.cfg index 080d9bdc4..1aebfe10b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,7 +13,13 @@ upload-dir = docs/build/html max-line-length = 99 ignore = W503, W605 extend-ignore = E203 -exclude = setup.py, docs/source/conf.py +exclude = setup.py, docs/source/conf.py, telegram/vendor + +[pylint] +ignore=vendor + +[pylint.message-control] +disable = C0330,R0801,R0913,R0904,R0903,R0902,W0511,C0116,C0115,W0703,R0914,R0914,C0302,R0912,R0915,R0401 [tool:pytest] testpaths = tests @@ -52,3 +58,9 @@ ignore_errors = True # We don't want to clutter the code with 'if self.bot is None: raise RuntimeError()' [mypy-telegram.callbackquery,telegram.chat,telegram.message,telegram.user,telegram.files.*,telegram.inline.inlinequery,telegram.payment.precheckoutquery,telegram.payment.shippingquery,telegram.passport.passportdata,telegram.passport.credentials,telegram.passport.passportfile,telegram.ext.filters] strict_optional = False + +[mypy-urllib3.*] +ignore_missing_imports = True + +[mypy-apscheduler.*] +ignore_missing_imports = True diff --git a/telegram/__main__.py b/telegram/__main__.py index 50af21c35..affefe628 100644 --- a/telegram/__main__.py +++ b/telegram/__main__.py @@ -16,13 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -import sys +# pylint: disable=E0401, C0114 import subprocess +import sys +from typing import Optional import certifi -from typing import Optional - from . import __version__ as telegram_ver diff --git a/telegram/base.py b/telegram/base.py index 884c06d3b..0c96f42bf 100644 --- a/telegram/base.py +++ b/telegram/base.py @@ -23,9 +23,9 @@ except ImportError: import json # type: ignore[no-redef] import warnings +from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Type, TypeVar from telegram.utils.types import JSONDict -from typing import Tuple, Any, Optional, Type, TypeVar, TYPE_CHECKING, List if TYPE_CHECKING: from telegram import Bot @@ -36,7 +36,7 @@ TO = TypeVar('TO', bound='TelegramObject', covariant=True) class TelegramObject: """Base class for most telegram objects.""" - # def __init__(self, *args: Any, **kwargs: Any): + # def __init__(self, *args: Any, **kwargs: Any): # pylint: disable=W0613 # pass _id_attrs: Tuple[Any, ...] = () @@ -62,8 +62,7 @@ class TelegramObject: if cls == TelegramObject: return cls() - else: - return cls(bot=bot, **data) # type: ignore[call-arg] + return cls(bot=bot, **data) # type: ignore[call-arg] @classmethod def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]: diff --git a/telegram/bot.py b/telegram/bot.py index 03e39889b..82baccda2 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -18,10 +18,27 @@ # # 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=E0401 """This module contains an object that represents a Telegram Bot.""" import functools import inspect +import logging +from datetime import datetime + +from typing import ( + IO, + TYPE_CHECKING, + Any, + Callable, + List, + Optional, + Tuple, + TypeVar, + Union, + cast, + no_type_check, +) from decorator import decorate @@ -29,67 +46,51 @@ try: import ujson as json except ImportError: import json # type: ignore[no-redef] # noqa: F723 -import logging -from datetime import datetime from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization from telegram import ( - User, - Message, - Update, + Animation, + Audio, + BotCommand, Chat, ChatMember, - UserProfilePhotos, - File, - ReplyMarkup, - TelegramObject, - WebhookInfo, - GameHighScore, - StickerSet, - PhotoSize, - Audio, - Document, - Sticker, - Video, - Animation, - Voice, - VideoNote, - Location, - Venue, - Contact, - InputFile, - Poll, - BotCommand, - InlineQueryResult, - InputMedia, - PassportElementError, - MaskPosition, ChatPermissions, - ShippingOption, - LabeledPrice, ChatPhoto, + Contact, + Document, + File, + GameHighScore, + InlineQueryResult, + InputFile, + InputMedia, + LabeledPrice, + Location, + MaskPosition, + Message, + PassportElementError, + PhotoSize, + Poll, + ReplyMarkup, + ShippingOption, + Sticker, + StickerSet, + TelegramObject, + Update, + User, + UserProfilePhotos, + Venue, + Video, + VideoNote, + Voice, + WebhookInfo, ) from telegram.constants import MAX_INLINE_QUERY_RESULTS from telegram.error import InvalidToken, TelegramError -from telegram.utils.helpers import to_timestamp, DEFAULT_NONE, DefaultValue +from telegram.utils.helpers import DEFAULT_NONE, DefaultValue, to_timestamp from telegram.utils.request import Request -from telegram.utils.types import JSONDict, FileLike - -from typing import ( - Any, - Callable, - Optional, - TypeVar, - Union, - TYPE_CHECKING, - List, - Tuple, - no_type_check, - IO, - cast, -) +from telegram.utils.types import FileLike, JSONDict if TYPE_CHECKING: from telegram.ext import Defaults @@ -98,6 +99,7 @@ RT = TypeVar('RT') def info(func: Callable[..., RT]) -> Callable[..., RT]: + # pylint: disable=W0212 @functools.wraps(func) def decorator(self: 'Bot', *args: Any, **kwargs: Any) -> RT: if not self.bot: @@ -264,7 +266,7 @@ class Bot(TelegramObject): result = self._post(endpoint, data, timeout=timeout, api_kwargs=api_kwargs) if result is True: - return result # type: ignore + return result return Message.de_json(result, self) # type: ignore[arg-type] @@ -1346,7 +1348,7 @@ class Bot(TelegramObject): "Either location or latitude and longitude must be passed as" "argument." ) - if not ((latitude is not None or longitude is not None) ^ bool(location)): + if not (latitude is not None or longitude is not None) ^ bool(location): raise ValueError( "Either location or latitude and longitude must be passed as" "argument. Not both." ) @@ -1417,7 +1419,7 @@ class Bot(TelegramObject): raise ValueError( "Either location or latitude and longitude must be passed as" "argument." ) - if not ((latitude is not None or longitude is not None) ^ bool(location)): + if not (latitude is not None or longitude is not None) ^ bool(location): raise ValueError( "Either location or latitude and longitude must be passed as" "argument. Not both." ) @@ -1831,6 +1833,7 @@ class Bot(TelegramObject): @no_type_check def _set_defaults(res): + # pylint: disable=W0212 if res._has_parse_mode and res.parse_mode == DEFAULT_NONE: if self.defaults: res.parse_mode = self.defaults.parse_mode @@ -3207,7 +3210,7 @@ class Bot(TelegramObject): """ ok = bool(ok) - if not (ok ^ (error_message is not None)): + 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 ' @@ -4079,7 +4082,7 @@ class Bot(TelegramObject): question: str, options: List[str], is_anonymous: bool = True, - type: str = Poll.REGULAR, + type: str = Poll.REGULAR, # pylint: disable=W0622 allows_multiple_answers: bool = False, correct_option_id: int = None, is_closed: bool = None, diff --git a/telegram/botcommand.py b/telegram/botcommand.py index e4ec30284..5a1ff808f 100644 --- a/telegram/botcommand.py +++ b/telegram/botcommand.py @@ -18,9 +18,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Bot Command.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class BotCommand(TelegramObject): """ @@ -39,7 +40,7 @@ class BotCommand(TelegramObject): description (:obj:`str`): Description of the command, 3-256 characters. """ - def __init__(self, command: str, description: str, **kwargs: Any): + def __init__(self, command: str, description: str, **kwargs: Any): # pylint: disable=W0613 self.command = command self.description = description diff --git a/telegram/callbackquery.py b/telegram/callbackquery.py index 6821abc15..a229d316b 100644 --- a/telegram/callbackquery.py +++ b/telegram/callbackquery.py @@ -16,14 +16,15 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=W0622 """This module contains an object that represents a Telegram CallbackQuery""" -from telegram import TelegramObject, Message, User +from typing import TYPE_CHECKING, Any, List, Optional, Union +from telegram import Message, TelegramObject, User from telegram.utils.types import JSONDict -from typing import Optional, Any, Union, TYPE_CHECKING, List if TYPE_CHECKING: - from telegram import Bot, InlineKeyboardMarkup, GameHighScore + from telegram import Bot, GameHighScore, InlineKeyboardMarkup class CallbackQuery(TelegramObject): @@ -79,8 +80,8 @@ class CallbackQuery(TelegramObject): """ def __init__( - self, - id: str, + self, # pylint: disable=W0613 + id: str, # pylint: disable=W0622 from_user: User, chat_instance: str, message: Message = None, @@ -91,7 +92,7 @@ class CallbackQuery(TelegramObject): **kwargs: Any, ): # Required - self.id = id + self.id = id # pylint: disable=C0103 self.from_user = from_user self.chat_instance = chat_instance # Optionals @@ -148,14 +149,13 @@ class CallbackQuery(TelegramObject): return self.bot.edit_message_text( text, inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.edit_message_text( - text, - chat_id=self.message.chat_id, - message_id=self.message.message_id, - *args, - **kwargs, - ) + return self.bot.edit_message_text( + text, + chat_id=self.message.chat_id, + message_id=self.message.message_id, + *args, + **kwargs, + ) def edit_message_caption( self, caption: str, *args: Any, **kwargs: Any @@ -182,30 +182,34 @@ class CallbackQuery(TelegramObject): return self.bot.edit_message_caption( caption=caption, inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.edit_message_caption( - caption=caption, - chat_id=self.message.chat_id, - message_id=self.message.message_id, - *args, - **kwargs, - ) + return self.bot.edit_message_caption( + caption=caption, + chat_id=self.message.chat_id, + message_id=self.message.message_id, + *args, + **kwargs, + ) def edit_message_reply_markup( self, reply_markup: 'InlineKeyboardMarkup', *args: Any, **kwargs: Any ) -> Union[Message, bool]: """Shortcut for either:: - bot.edit_message_reply_markup(chat_id=update.callback_query.message.chat_id, - message_id=update.callback_query.message.message_id, - reply_markup=reply_markup, - *args, **kwargs) + bot.edit_message_reply_markup( + chat_id=update.callback_query.message.chat_id, + message_id=update.callback_query.message.message_id, + reply_markup=reply_markup, + *args, **kwargs + ) or:: - bot.edit_message_reply_markup(inline_message_id=update.callback_query.inline_message_id, - reply_markup=reply_markup, - *args, **kwargs) + bot.edit_message_reply_markup + inline_message_id=update.callback_query.inline_message_id, + reply_markup=reply_markup, + *args, + **kwargs + ) Returns: :class:`telegram.Message`: On success, if edited message is sent by the bot, the @@ -219,14 +223,13 @@ class CallbackQuery(TelegramObject): *args, **kwargs, ) - else: - return self.bot.edit_message_reply_markup( - reply_markup=reply_markup, - chat_id=self.message.chat_id, - message_id=self.message.message_id, - *args, - **kwargs, - ) + return self.bot.edit_message_reply_markup( + reply_markup=reply_markup, + chat_id=self.message.chat_id, + message_id=self.message.message_id, + *args, + **kwargs, + ) def edit_message_media(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: """Shortcut for either:: @@ -251,10 +254,9 @@ class CallbackQuery(TelegramObject): return self.bot.edit_message_media( inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.edit_message_media( - chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs - ) + return self.bot.edit_message_media( + chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs + ) def edit_message_live_location(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: """Shortcut for either:: @@ -281,10 +283,9 @@ class CallbackQuery(TelegramObject): return self.bot.edit_message_live_location( inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.edit_message_live_location( - chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs - ) + return self.bot.edit_message_live_location( + chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs + ) def stop_message_live_location(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: """Shortcut for either:: @@ -311,10 +312,9 @@ class CallbackQuery(TelegramObject): return self.bot.stop_message_live_location( inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.stop_message_live_location( - chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs - ) + return self.bot.stop_message_live_location( + chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs + ) def set_game_score(self, *args: Any, **kwargs: Any) -> Union[Message, bool]: """Shortcut for either:: @@ -339,10 +339,9 @@ class CallbackQuery(TelegramObject): return self.bot.set_game_score( inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.set_game_score( - chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs - ) + return self.bot.set_game_score( + chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs + ) def get_game_high_scores(self, *args: Any, **kwargs: Any) -> List['GameHighScore']: """Shortcut for either:: @@ -366,7 +365,6 @@ class CallbackQuery(TelegramObject): return self.bot.get_game_high_scores( inline_message_id=self.inline_message_id, *args, **kwargs ) - else: - return self.bot.get_game_high_scores( - chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs - ) + return self.bot.get_game_high_scores( + chat_id=self.message.chat_id, message_id=self.message.message_id, *args, **kwargs + ) diff --git a/telegram/chat.py b/telegram/chat.py index 2113f176b..fcb9f0097 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -19,14 +19,15 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Chat.""" -from telegram import TelegramObject, ChatPhoto, constants +from typing import TYPE_CHECKING, Any, List, Optional, ClassVar + +from telegram import ChatPhoto, TelegramObject, constants +from telegram.utils.types import JSONDict + from .chatpermissions import ChatPermissions -from telegram.utils.types import JSONDict -from typing import Any, Optional, List, TYPE_CHECKING, ClassVar - if TYPE_CHECKING: - from telegram import Bot, Message, ChatMember + from telegram import Bot, ChatMember, Message class Chat(TelegramObject): @@ -102,7 +103,7 @@ class Chat(TelegramObject): """:const:`telegram.constants.CHAT_CHANNEL`""" def __init__( - self, + self, # pylint: disable=W0613 id: int, type: str, title: str = None, @@ -173,7 +174,7 @@ class Chat(TelegramObject): return None data['photo'] = ChatPhoto.de_json(data.get('photo'), bot) - from telegram import Message + 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) diff --git a/telegram/chatmember.py b/telegram/chatmember.py index 153dfa81a..6a06d5d8c 100644 --- a/telegram/chatmember.py +++ b/telegram/chatmember.py @@ -18,12 +18,11 @@ # 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 -from telegram import User, TelegramObject, constants -from telegram.utils.helpers import to_timestamp, from_timestamp - +from telegram import TelegramObject, User, constants +from telegram.utils.helpers import from_timestamp, to_timestamp from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING, ClassVar if TYPE_CHECKING: from telegram import Bot @@ -126,7 +125,7 @@ class ChatMember(TelegramObject): """:const:`telegram.constants.CHATMEMBER_RESTRICTED`""" def __init__( - self, + self, # pylint: disable=W0613 user: User, status: str, until_date: datetime.datetime = None, diff --git a/telegram/chatpermissions.py b/telegram/chatpermissions.py index c852aab75..0dd0353c8 100644 --- a/telegram/chatpermissions.py +++ b/telegram/chatpermissions.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatPermission.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class ChatPermissions(TelegramObject): """Describes actions that a non-administrator user is allowed to take in a chat. @@ -78,7 +79,7 @@ class ChatPermissions(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 can_send_messages: bool = None, can_send_media_messages: bool = None, can_send_polls: bool = None, diff --git a/telegram/choseninlineresult.py b/telegram/choseninlineresult.py index 74ea6d54f..0db1d74c2 100644 --- a/telegram/choseninlineresult.py +++ b/telegram/choseninlineresult.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# pylint: disable=R0902,R0912,R0913 +# pylint: disable=R0902,R0913 # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2020 @@ -19,9 +19,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChosenInlineResult.""" -from telegram import TelegramObject, User, Location +from typing import TYPE_CHECKING, Any, Optional + +from telegram import Location, TelegramObject, User from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -63,7 +64,7 @@ class ChosenInlineResult(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 result_id: str, from_user: User, query: str, diff --git a/telegram/dice.py b/telegram/dice.py index 7186d951c..9a2c54c1b 100644 --- a/telegram/dice.py +++ b/telegram/dice.py @@ -18,9 +18,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Dice.""" -from telegram import TelegramObject, constants from typing import Any, List, ClassVar +from telegram import TelegramObject, constants + class Dice(TelegramObject): """ @@ -49,7 +50,7 @@ class Dice(TelegramObject): emoji (:obj:`str`): Emoji on which the dice throw animation is based. """ - def __init__(self, value: int, emoji: str, **kwargs: Any): + def __init__(self, value: int, emoji: str, **kwargs: Any): # pylint: disable=W0613 self.value = value self.emoji = emoji diff --git a/telegram/error.py b/telegram/error.py index 3e634eb17..e5b9dbb8e 100644 --- a/telegram/error.py +++ b/telegram/error.py @@ -16,6 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=C0115 """This module contains an object that represents Telegram errors.""" from typing import Tuple @@ -123,8 +124,5 @@ class Conflict(TelegramError): """ - def __init__(self, msg: str): - super().__init__(msg) - def __reduce__(self) -> Tuple[type, Tuple[str]]: return self.__class__, (self.message,) diff --git a/telegram/ext/basepersistence.py b/telegram/ext/basepersistence.py index dea635d37..1624e4ebd 100644 --- a/telegram/ext/basepersistence.py +++ b/telegram/ext/basepersistence.py @@ -21,10 +21,10 @@ from abc import ABC, abstractmethod from collections import defaultdict from copy import copy +from typing import Any, DefaultDict, Dict, Optional, Tuple, cast, ClassVar from telegram import Bot -from typing import DefaultDict, Dict, Any, Tuple, Optional, cast, ClassVar from telegram.utils.types import ConversationDict @@ -73,7 +73,7 @@ class BasePersistence(ABC): persistence class. Default is :obj:`True` . """ - def __new__(cls, *args: Any, **kwargs: Any) -> 'BasePersistence': + def __new__(cls, *args: Any, **kwargs: Any) -> 'BasePersistence': # pylint: disable=W0613 instance = super().__new__(cls) get_user_data = instance.get_user_data get_chat_data = instance.get_chat_data @@ -150,8 +150,8 @@ class BasePersistence(ABC): if isinstance(obj, (dict, defaultdict)): new_obj = cast(dict, new_obj) new_obj.clear() - for k, v in obj.items(): - new_obj[cls.replace_bot(k)] = cls.replace_bot(v) + for k, val in obj.items(): + new_obj[cls.replace_bot(k)] = cls.replace_bot(val) return new_obj if hasattr(obj, '__dict__'): for attr_name, attr in new_obj.__dict__.items(): @@ -168,7 +168,7 @@ class BasePersistence(ABC): return obj - def insert_bot(self, obj: object) -> object: + def insert_bot(self, obj: object) -> object: # pylint: disable=R0911 """ Replaces all instances of :attr:`REPLACED_BOT` that occur within the passed object with :attr:`bot`. Currently, this handles objects of type ``list``, ``tuple``, ``set``, @@ -192,8 +192,8 @@ class BasePersistence(ABC): if isinstance(obj, (dict, defaultdict)): new_obj = cast(dict, new_obj) new_obj.clear() - for k, v in obj.items(): - new_obj[self.insert_bot(k)] = self.insert_bot(v) + for k, val in obj.items(): + new_obj[self.insert_bot(k)] = self.insert_bot(val) return new_obj if hasattr(obj, '__dict__'): for attr_name, attr in new_obj.__dict__.items(): @@ -300,7 +300,6 @@ class BasePersistence(ABC): persistence a chance to finish up saving or close a database connection gracefully. If this is not of any importance just pass will be sufficient. """ - pass 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 665e5826a..20ea6fb88 100644 --- a/telegram/ext/callbackcontext.py +++ b/telegram/ext/callbackcontext.py @@ -16,9 +16,10 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=R0201 """This module contains the CallbackContext class.""" from queue import Queue -from typing import Dict, Any, Tuple, TYPE_CHECKING, Optional, Match, List, NoReturn, Union +from typing import TYPE_CHECKING, Any, Dict, List, Match, NoReturn, Optional, Tuple, Union from telegram import Update @@ -165,9 +166,9 @@ class CallbackContext: user = update.effective_user if chat: - self._chat_data = dispatcher.chat_data[chat.id] + self._chat_data = dispatcher.chat_data[chat.id] # pylint: disable=W0212 if user: - self._user_data = dispatcher.user_data[user.id] + self._user_data = dispatcher.user_data[user.id] # pylint: disable=W0212 return self @classmethod diff --git a/telegram/ext/callbackqueryhandler.py b/telegram/ext/callbackqueryhandler.py index 2fa0bbc43..8903b6387 100644 --- a/telegram/ext/callbackqueryhandler.py +++ b/telegram/ext/callbackqueryhandler.py @@ -19,24 +19,24 @@ """This module contains the CallbackQueryHandler class.""" import re - -from telegram import Update -from .handler import Handler - -from telegram.utils.types import HandlerArg from typing import ( - Callable, TYPE_CHECKING, Any, - Optional, - Union, - TypeVar, - Pattern, - Match, + Callable, Dict, + Match, + Optional, + Pattern, + TypeVar, + Union, cast, ) +from telegram import Update +from telegram.utils.types import HandlerArg + +from .handler import Handler + if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher diff --git a/telegram/ext/choseninlineresulthandler.py b/telegram/ext/choseninlineresulthandler.py index ed66d2fdf..9055be923 100644 --- a/telegram/ext/choseninlineresulthandler.py +++ b/telegram/ext/choseninlineresulthandler.py @@ -18,11 +18,12 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the ChosenInlineResultHandler class.""" -from telegram import Update -from .handler import Handler +from typing import Optional, TypeVar, Union +from telegram import Update from telegram.utils.types import HandlerArg -from typing import Optional, Union, TypeVar + +from .handler import Handler RT = TypeVar('RT') diff --git a/telegram/ext/commandhandler.py b/telegram/ext/commandhandler.py index a91230555..2485a00b7 100644 --- a/telegram/ext/commandhandler.py +++ b/telegram/ext/commandhandler.py @@ -19,15 +19,14 @@ """This module contains the CommandHandler and PrefixHandler classes.""" import re import warnings +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, TypeVar, Union -from telegram.ext import Filters, BaseFilter +from telegram import MessageEntity, Update +from telegram.ext import BaseFilter, Filters from telegram.utils.deprecate import TelegramDeprecationWarning - -from telegram import Update, MessageEntity -from .handler import Handler - from telegram.utils.types import HandlerArg -from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, List, Tuple + +from .handler import Handler if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher @@ -212,8 +211,7 @@ class CommandHandler(Handler): filter_result = self.filters(update) if filter_result: return args, filter_result - else: - return False + return False return None def collect_optional_args( @@ -270,9 +268,6 @@ class PrefixHandler(CommandHandler): use ~``Filters.update.edited_message``. Attributes: - prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`. - command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler - should listen for. callback (:obj:`callable`): The callback function for this handler. filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these Filters. @@ -380,6 +375,12 @@ class PrefixHandler(CommandHandler): @property def prefix(self) -> List[str]: + """ + The prefixes that will precede :attr:`command`. + + Returns: + List[:obj:`str`] + """ return self._prefix @prefix.setter @@ -392,6 +393,12 @@ class PrefixHandler(CommandHandler): @property # type: ignore[override] def command(self) -> List[str]: # type: ignore[override] + """ + The list of commands this handler should listen for. + + Returns: + List[:obj:`str`] + """ return self._command @command.setter @@ -427,8 +434,7 @@ class PrefixHandler(CommandHandler): filter_result = self.filters(update) if filter_result: return text_list[1:], filter_result - else: - return False + return False return None def collect_additional_context( diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index b5964a426..e0c309f17 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -16,26 +16,26 @@ # # 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=R0201 """This module contains the ConversationHandler.""" import logging import warnings from threading import Lock +from typing import TYPE_CHECKING, Any, Dict, List, NoReturn, Optional, Tuple, cast, ClassVar from telegram import Update from telegram.ext import ( - Handler, - CallbackQueryHandler, - InlineQueryHandler, - ChosenInlineResultHandler, - CallbackContext, BasePersistence, + CallbackContext, + CallbackQueryHandler, + ChosenInlineResultHandler, DispatcherHandlerStop, + Handler, + InlineQueryHandler, ) from telegram.utils.promise import Promise - from telegram.utils.types import ConversationDict, HandlerArg -from typing import Dict, Any, List, Optional, Tuple, TYPE_CHECKING, cast, NoReturn, ClassVar if TYPE_CHECKING: from telegram.ext import Dispatcher, Job @@ -176,7 +176,7 @@ class ConversationHandler(Handler): WAITING: ClassVar[int] = -3 """:obj:`int`: Used as a constant to handle state when a conversation is still waiting on the previous ``@run_sync`` decorated running handler to finish.""" - + # pylint: disable=W0231 def __init__( self, entry_points: List[Handler], @@ -389,7 +389,7 @@ class ConversationHandler(Handler): return tuple(key) - def check_update(self, update: HandlerArg) -> CheckUpdateType: + def check_update(self, update: HandlerArg) -> CheckUpdateType: # pylint: disable=R0911 """ Determines whether an update should be handled by this conversationhandler, and if so in which state the conversation currently is. @@ -401,18 +401,16 @@ class ConversationHandler(Handler): :obj:`bool` """ + if not isinstance(update, Update): + return None # Ignore messages in channels - if ( - not isinstance(update, Update) - or update.channel_post - or self.per_chat - and not update.effective_chat - or self.per_message - and not update.callback_query - or update.callback_query - and self.per_chat - and not update.callback_query.message - ): + if update.channel_post: + return None + if self.per_chat and not update.effective_chat: + return None + if self.per_message and not update.callback_query: + return None + if update.callback_query and self.per_chat and not update.callback_query.message: return None key = self._get_key(update) @@ -430,7 +428,7 @@ class ConversationHandler(Handler): res = res if res is not None else old_state except Exception as exc: self.logger.exception("Promise function raised exception") - self.logger.exception("{}".format(exc)) + self.logger.exception("%s", exc) res = old_state finally: if res is None and old_state is None: @@ -446,7 +444,7 @@ class ConversationHandler(Handler): return key, hdlr, check return None - self.logger.debug('selecting conversation {} with state {}'.format(str(key), str(state))) + self.logger.debug('selecting conversation %s with state %s', str(key), str(state)) handler = None @@ -515,8 +513,8 @@ class ConversationHandler(Handler): timeout_job.schedule_removal() try: new_state = handler.handle_update(update, dispatcher, check_result, context) - except DispatcherHandlerStop as e: - new_state = e.state + except DispatcherHandlerStop as exception: + new_state = exception.state raise_dp_handler_stop = True with self._timeout_jobs_lock: if self.conversation_timeout and new_state != self.END and dispatcher.job_queue: @@ -533,14 +531,13 @@ class ConversationHandler(Handler): self.update_state(self.END, conversation_key) if raise_dp_handler_stop: raise DispatcherHandlerStop(self.map_to_parent.get(new_state)) - else: - return self.map_to_parent.get(new_state) - else: - self.update_state(new_state, conversation_key) - if raise_dp_handler_stop: - # Don't pass the new state here. If we're in a nested conversation, the parent is - # expecting None as return value. - raise DispatcherHandlerStop() + return self.map_to_parent.get(new_state) + + self.update_state(new_state, conversation_key) + if raise_dp_handler_stop: + # Don't pass the new state here. If we're in a nested conversation, the parent is + # expecting None as return value. + raise DispatcherHandlerStop() return None def update_state(self, new_state: object, key: Tuple[int, ...]) -> None: diff --git a/telegram/ext/defaults.py b/telegram/ext/defaults.py index 6b041db71..d8e3a15b5 100644 --- a/telegram/ext/defaults.py +++ b/telegram/ext/defaults.py @@ -16,9 +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/]. +# pylint: disable=R0201, E0401 """This module contains the class Defaults, which allows to pass default values to Updater.""" +from typing import Any, NoReturn, Optional, Union + import pytz -from typing import Union, Optional, Any, NoReturn from telegram.utils.helpers import DEFAULT_NONE, DefaultValue diff --git a/telegram/ext/dictpersistence.py b/telegram/ext/dictpersistence.py index 7623efe6b..51ec84136 100644 --- a/telegram/ext/dictpersistence.py +++ b/telegram/ext/dictpersistence.py @@ -19,21 +19,21 @@ """This module contains the DictPersistence class.""" from copy import deepcopy +from typing import Any, DefaultDict, Dict, Optional, Tuple +from collections import defaultdict + from telegram.utils.helpers import ( - decode_user_chat_data_from_json, decode_conversations_from_json, + decode_user_chat_data_from_json, encode_conversations_to_json, ) +from telegram.ext import BasePersistence +from telegram.utils.types import ConversationDict try: import ujson as json except ImportError: import json # type: ignore[no-redef] -from collections import defaultdict -from telegram.ext import BasePersistence - -from typing import DefaultDict, Dict, Any, Tuple, Optional -from telegram.utils.types import ConversationDict class DictPersistence(BasePersistence): @@ -106,20 +106,20 @@ class DictPersistence(BasePersistence): try: self._user_data = decode_user_chat_data_from_json(user_data_json) self._user_data_json = user_data_json - except (ValueError, AttributeError): - raise TypeError("Unable to deserialize user_data_json. Not valid JSON") + except (ValueError, AttributeError) as exc: + raise TypeError("Unable to deserialize user_data_json. Not valid JSON") from exc if chat_data_json: try: self._chat_data = decode_user_chat_data_from_json(chat_data_json) self._chat_data_json = chat_data_json - except (ValueError, AttributeError): - raise TypeError("Unable to deserialize chat_data_json. Not valid JSON") + except (ValueError, AttributeError) as exc: + raise TypeError("Unable to deserialize chat_data_json. Not valid JSON") from exc if bot_data_json: try: self._bot_data = json.loads(bot_data_json) self._bot_data_json = bot_data_json - except (ValueError, AttributeError): - raise TypeError("Unable to deserialize bot_data_json. Not valid JSON") + except (ValueError, AttributeError) as exc: + raise TypeError("Unable to deserialize bot_data_json. Not valid JSON") from exc if not isinstance(self._bot_data, dict): raise TypeError("bot_data_json must be serialized dict") @@ -127,8 +127,10 @@ class DictPersistence(BasePersistence): try: self._conversations = decode_conversations_from_json(conversations_json) self._conversations_json = conversations_json - except (ValueError, AttributeError): - raise TypeError("Unable to deserialize conversations_json. Not valid JSON") + except (ValueError, AttributeError) as exc: + raise TypeError( + "Unable to deserialize conversations_json. Not valid JSON" + ) from exc @property def user_data(self) -> Optional[DefaultDict[int, Dict]]: @@ -140,8 +142,7 @@ class DictPersistence(BasePersistence): """:obj:`str`: The user_data serialized as a JSON-string.""" if self._user_data_json: return self._user_data_json - else: - return json.dumps(self.user_data) + return json.dumps(self.user_data) @property def chat_data(self) -> Optional[DefaultDict[int, Dict]]: @@ -153,8 +154,7 @@ class DictPersistence(BasePersistence): """:obj:`str`: The chat_data serialized as a JSON-string.""" if self._chat_data_json: return self._chat_data_json - else: - return json.dumps(self.chat_data) + return json.dumps(self.chat_data) @property def bot_data(self) -> Optional[Dict]: @@ -166,8 +166,7 @@ class DictPersistence(BasePersistence): """:obj:`str`: The bot_data serialized as a JSON-string.""" if self._bot_data_json: return self._bot_data_json - else: - return json.dumps(self.bot_data) + return json.dumps(self.bot_data) @property def conversations(self) -> Optional[Dict[str, Dict[Tuple, Any]]]: @@ -179,8 +178,7 @@ class DictPersistence(BasePersistence): """:obj:`str`: The conversations serialized as a JSON-string.""" if self._conversations_json: return self._conversations_json - else: - return encode_conversations_to_json(self.conversations) # type: ignore[arg-type] + return encode_conversations_to_json(self.conversations) # type: ignore[arg-type] def get_user_data(self) -> DefaultDict[int, Dict[Any, Any]]: """Returns the user_data created from the ``user_data_json`` or an empty diff --git a/telegram/ext/dispatcher.py b/telegram/ext/dispatcher.py index c7658e411..a35e36f8d 100644 --- a/telegram/ext/dispatcher.py +++ b/telegram/ext/dispatcher.py @@ -21,23 +21,20 @@ import logging import warnings import weakref -from functools import wraps -from threading import Thread, Lock, Event, current_thread, BoundedSemaphore -from time import sleep -from uuid import uuid4 from collections import defaultdict - -from queue import Queue, Empty +from functools import wraps +from queue import Empty, Queue +from threading import BoundedSemaphore, Event, Lock, Thread, current_thread +from time import sleep +from typing import TYPE_CHECKING, Any, Callable, DefaultDict, Dict, List, Optional, Set, Union +from uuid import uuid4 from telegram import TelegramError, Update -from telegram.ext.handler import Handler +from telegram.ext import BasePersistence from telegram.ext.callbackcontext import CallbackContext +from telegram.ext.handler import Handler from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.promise import Promise -from telegram.ext import BasePersistence - -from typing import Any, Callable, TYPE_CHECKING, Optional, Union, DefaultDict, Dict, List, Set - from telegram.utils.types import HandlerArg if TYPE_CHECKING: @@ -74,7 +71,7 @@ def run_async( TelegramDeprecationWarning, stacklevel=2, ) - return Dispatcher.get_instance()._run_async( + return Dispatcher.get_instance()._run_async( # pylint: disable=W0212 func, *args, update=None, error_handling=False, **kwargs ) @@ -245,10 +242,7 @@ class Dispatcher: """ if cls.__singleton is not None: return cls.__singleton() # type: ignore[return-value] # pylint: disable=not-callable - else: - raise RuntimeError( - '{} not initialized or multiple instances exist'.format(cls.__name__) - ) + raise RuntimeError('{} not initialized or multiple instances exist'.format(cls.__name__)) def _pooled(self) -> None: thr_name = current_thread().getName() @@ -328,7 +322,7 @@ class Dispatcher: *args: Any, update: HandlerArg = None, error_handling: bool = True, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ) -> Promise: # TODO: Remove error_handling parameter once we drop the @run_async decorator promise = Promise(func, args, kwargs, update=update, error_handling=error_handling) @@ -371,12 +365,12 @@ class Dispatcher: if self.__stop_event.is_set(): self.logger.debug('orderly stopping') break - elif self.__exception_event.is_set(): + if self.__exception_event.is_set(): 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() @@ -401,10 +395,10 @@ class Dispatcher: self.__async_queue.put(None) for i, thr in enumerate(threads): - self.logger.debug('Waiting for async thread {}/{} to end'.format(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 {}/{} has ended'.format(i + 1, total)) + self.logger.debug('async thread %s/%s has ended', i + 1, total) @property def has_running_threads(self) -> bool: @@ -450,9 +444,9 @@ class Dispatcher: break # Dispatch any error. - except Exception as e: + except Exception as exc: try: - self.dispatch_error(update, e) + self.dispatch_error(update, exc) except DispatcherHandlerStop: self.logger.debug('Error handler stopped further handlers') break @@ -486,7 +480,7 @@ class Dispatcher: """ # Unfortunately due to circular imports this has to be here - from .conversationhandler import ConversationHandler + from .conversationhandler import ConversationHandler # pylint: disable=C0415 if not isinstance(handler, Handler): raise TypeError('handler is not an instance of {}'.format(Handler.__name__)) @@ -552,9 +546,9 @@ class Dispatcher: if self.persistence.store_bot_data: try: self.persistence.update_bot_data(self.bot_data) - except Exception as e: + except Exception as exc: try: - self.dispatch_error(update, e) + self.dispatch_error(update, exc) except Exception: message = ( 'Saving bot data raised an error and an ' @@ -566,9 +560,9 @@ class Dispatcher: for chat_id in chat_ids: try: self.persistence.update_chat_data(chat_id, self.chat_data[chat_id]) - except Exception as e: + except Exception as exc: try: - self.dispatch_error(update, e) + self.dispatch_error(update, exc) except Exception: message = ( 'Saving chat data raised an error and an ' @@ -580,9 +574,9 @@ class Dispatcher: for user_id in user_ids: try: self.persistence.update_user_data(user_id, self.user_data[user_id]) - except Exception as e: + except Exception as exc: try: - self.dispatch_error(update, e) + self.dispatch_error(update, exc) except Exception: message = ( 'Saving user data raised an error and an ' @@ -592,7 +586,9 @@ class Dispatcher: self.logger.exception(message) def add_error_handler( - self, callback: Callable[[Any, CallbackContext], None], run_async: bool = False + self, + callback: Callable[[Any, CallbackContext], None], + run_async: bool = False, # pylint: disable=W0621 ) -> None: """Registers an error handler in the Dispatcher. This handler will receive every error which happens in your bot. @@ -647,7 +643,7 @@ class Dispatcher: async_kwargs = None if not promise else promise.kwargs if self.error_handlers: - for callback, run_async in self.error_handlers.items(): + for callback, run_async in self.error_handlers.items(): # pylint: disable=W0621 if self.use_context: context = CallbackContext.from_error( update, error, self, async_args=async_args, async_kwargs=async_kwargs diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index 220c3ab45..1736b0457 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -16,6 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=C0112, C0103, W0221 """This module contains the Filters for use with the MessageHandler class.""" import re @@ -23,10 +24,9 @@ import warnings from abc import ABC, abstractmethod from threading import Lock +from typing import Dict, FrozenSet, List, Match, Optional, Pattern, Set, Union, cast -from telegram import Chat, Update, MessageEntity, Message - -from typing import Optional, Dict, Union, List, Pattern, Match, cast, Set, FrozenSet +from telegram import Chat, Message, MessageEntity, Update __all__ = [ 'Filters', @@ -223,7 +223,8 @@ class MergedFilter(UpdateFilter): if self.or_filter and not isinstance(self.and_filter, bool) and self.or_filter.data_filter: self.data_filter = True - def _merge(self, base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> Dict: + @staticmethod + def _merge(base_output: Union[bool, Dict], comp_output: Union[bool, Dict]) -> Dict: base = base_output if isinstance(base_output, dict) else {} comp = comp_output if isinstance(comp_output, dict) else {} for k in comp.keys(): @@ -239,7 +240,7 @@ class MergedFilter(UpdateFilter): base[k] = comp_value return base - def filter(self, update: Update) -> Union[bool, Dict]: + def filter(self, update: Update) -> Union[bool, Dict]: # pylint: disable=R0911 base_output = self.base_filter(update) # We need to check if the filters are data filters and if so return the merged data. # If it's not a data filter or an or_filter but no matches return bool @@ -259,12 +260,12 @@ class MergedFilter(UpdateFilter): if self.data_filter: return base_output return True - else: - comp_output = self.or_filter(update) - if comp_output: - if self.data_filter: - return comp_output - return True + + comp_output = self.or_filter(update) + if comp_output: + if self.data_filter: + return comp_output + return True return False def __repr__(self) -> str: @@ -296,8 +297,7 @@ class _DiceEmoji(MessageFilter): ) -> Union[bool, '_DiceValues']: if isinstance(update, Update): return self.filter(update.effective_message) - else: - return self._DiceValues(update, self.name, emoji=self.emoji) + return self._DiceValues(update, self.name, emoji=self.emoji) def filter(self, message: Message) -> bool: if bool(message.dice): @@ -344,8 +344,7 @@ class Filters: ) -> Union[bool, '_TextStrings']: if isinstance(update, Update): return self.filter(update.effective_message) - else: - return self._TextStrings(update) + return self._TextStrings(update) def filter(self, message: Message) -> bool: return bool(message.text) @@ -396,8 +395,7 @@ class Filters: ) -> Union[bool, '_CaptionStrings']: if isinstance(update, Update): return self.filter(update.effective_message) - else: - return self._CaptionStrings(update) + return self._CaptionStrings(update) def filter(self, message: Message) -> bool: return bool(message.caption) @@ -433,8 +431,7 @@ class Filters: ) -> Union[bool, '_CommandOnlyStart']: if isinstance(update, Update): return self.filter(update.effective_message) - else: - return self._CommandOnlyStart(update) + return self._CommandOnlyStart(update) def filter(self, message: Message) -> bool: return bool( diff --git a/telegram/ext/handler.py b/telegram/ext/handler.py index 25499ecfc..c7b3dc2c1 100644 --- a/telegram/ext/handler.py +++ b/telegram/ext/handler.py @@ -19,11 +19,11 @@ """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 +from telegram import Update from telegram.utils.promise import Promise from telegram.utils.types import HandlerArg -from telegram import Update -from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher @@ -147,16 +147,14 @@ class Handler(ABC): self.collect_additional_context(context, update, dispatcher, check_result) if self.run_async: return dispatcher.run_async(self.callback, update, context, update=update) - else: - return self.callback(update, context) - else: - optional_args = self.collect_optional_args(dispatcher, update, check_result) - if self.run_async: - return dispatcher.run_async( - self.callback, dispatcher.bot, update, update=update, **optional_args - ) - else: - return self.callback(dispatcher.bot, update, **optional_args) # type: ignore + return self.callback(update, context) + + optional_args = self.collect_optional_args(dispatcher, update, check_result) + if self.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 def collect_additional_context( self, @@ -174,10 +172,12 @@ class Handler(ABC): check_result: The result (return value) from :attr:`check_update`. """ - pass def collect_optional_args( - self, dispatcher: 'Dispatcher', update: HandlerArg = None, check_result: Any = None + self, + dispatcher: 'Dispatcher', + update: HandlerArg = None, + check_result: Any = None, # pylint: disable=W0613 ) -> Dict[str, Any]: """ Prepares the optional arguments. If the handler has additional optional args, diff --git a/telegram/ext/inlinequeryhandler.py b/telegram/ext/inlinequeryhandler.py index cd8be1ba8..7e4960e13 100644 --- a/telegram/ext/inlinequeryhandler.py +++ b/telegram/ext/inlinequeryhandler.py @@ -18,25 +18,24 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """ This module contains the InlineQueryHandler class """ import re - -from telegram import Update - -from .handler import Handler - -from telegram.utils.types import HandlerArg from typing import ( - Callable, TYPE_CHECKING, Any, - Optional, - Union, - TypeVar, + Callable, Dict, - Pattern, Match, + Optional, + Pattern, + TypeVar, + Union, cast, ) +from telegram import Update +from telegram.utils.types import HandlerArg + +from .handler import Handler + if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher diff --git a/telegram/ext/jobqueue.py b/telegram/ext/jobqueue.py index 224c286ed..61e889e7c 100644 --- a/telegram/ext/jobqueue.py +++ b/telegram/ext/jobqueue.py @@ -16,25 +16,25 @@ # # 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=E0401 """This module contains the classes JobQueue and Job.""" import datetime import logging -import pytz +from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Union, cast, overload +import pytz +from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_EXECUTED, JobEvent from apscheduler.schedulers.background import BackgroundScheduler -from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.combining import OrTrigger -from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR, JobEvent +from apscheduler.triggers.cron import CronTrigger from telegram.ext.callbackcontext import CallbackContext - -from typing import TYPE_CHECKING, Union, Callable, Tuple, Optional, List, Any, cast, overload from telegram.utils.types import JSONDict if TYPE_CHECKING: - from telegram.ext import Dispatcher from telegram import Bot + from telegram.ext import Dispatcher class Days: @@ -76,7 +76,7 @@ class JobQueue: def _tz_now(self) -> datetime.datetime: return datetime.datetime.now(self.scheduler.timezone) - def _update_persistence(self, event: JobEvent) -> None: + def _update_persistence(self, event: JobEvent) -> None: # pylint: disable=W0613 self._dispatcher.update_persistence() def _dispatch_error(self, event: JobEvent) -> None: @@ -114,14 +114,14 @@ class JobQueue: if isinstance(time, datetime.timedelta): return self._tz_now() + time if isinstance(time, datetime.time): - dt = datetime.datetime.combine( + date_time = datetime.datetime.combine( datetime.datetime.now(tz=time.tzinfo or self.scheduler.timezone).date(), time ) - if dt.tzinfo is None: - dt = self.scheduler.timezone.localize(dt) - if shift_day and dt <= datetime.datetime.now(pytz.utc): - dt += datetime.timedelta(days=1) - return dt + if date_time.tzinfo is None: + date_time = self.scheduler.timezone.localize(date_time) + if shift_day and date_time <= datetime.datetime.now(pytz.utc): + date_time += datetime.timedelta(days=1) + return date_time # isinstance(time, datetime.datetime): return time @@ -190,15 +190,15 @@ class JobQueue: name = name or callback.__name__ job = Job(callback, context, name, self) - dt = self._parse_time_input(when, shift_day=True) + date_time = self._parse_time_input(when, shift_day=True) j = self.scheduler.add_job( callback, name=name, trigger='date', - run_date=dt, + run_date=date_time, args=self._build_args(job), - timezone=dt.tzinfo or self.scheduler.timezone, + timezone=date_time.tzinfo or self.scheduler.timezone, **job_kwargs, ) @@ -568,9 +568,9 @@ class Job: self.callback(CallbackContext.from_job(self, dispatcher)) else: self.callback(dispatcher.bot, self) # type: ignore[arg-type,call-arg] - except Exception as e: + except Exception as exc: try: - dispatcher.dispatch_error(None, e) + dispatcher.dispatch_error(None, exc) # Errors should not stop the thread. except Exception: dispatcher.logger.exception( diff --git a/telegram/ext/messagehandler.py b/telegram/ext/messagehandler.py index 5592eb5e7..95061fcaa 100644 --- a/telegram/ext/messagehandler.py +++ b/telegram/ext/messagehandler.py @@ -19,15 +19,14 @@ # TODO: Remove allow_edited """This module contains the MessageHandler class.""" import warnings - -from telegram.utils.deprecate import TelegramDeprecationWarning +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, TypeVar, Union from telegram import Update -from telegram.ext import Filters, BaseFilter -from .handler import Handler - +from telegram.ext import BaseFilter, Filters +from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.types import HandlerArg -from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict + +from .handler import Handler if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher diff --git a/telegram/ext/messagequeue.py b/telegram/ext/messagequeue.py index d268b18fd..0267ca56a 100644 --- a/telegram/ext/messagequeue.py +++ b/telegram/ext/messagequeue.py @@ -20,14 +20,13 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/] """A throughput-limiting message processor for Telegram bots.""" -from telegram.utils import promise - import functools -import time -import threading import queue as q +import threading +import time +from typing import TYPE_CHECKING, Any, Callable, List, NoReturn -from typing import Callable, Any, TYPE_CHECKING, List, NoReturn +from telegram.utils.promise import Promise if TYPE_CHECKING: from telegram import Bot @@ -39,8 +38,6 @@ curtime = time.perf_counter class DelayQueueError(RuntimeError): """Indicates processing errors.""" - pass - class DelayQueue(threading.Thread): """ @@ -304,12 +301,13 @@ def queuedmessage(method: Callable) -> Callable: @functools.wraps(method) def wrapped(self: 'Bot', *args: Any, **kwargs: Any) -> Any: + # 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.Promise(method, (self,) + args, kwargs) + prom = Promise(method, (self,) + args, kwargs) return self._msg_queue(prom, isgroup) # type: ignore[attr-defined] return method(self, *args, **kwargs) diff --git a/telegram/ext/picklepersistence.py b/telegram/ext/picklepersistence.py index f4c63082a..432c8fad6 100644 --- a/telegram/ext/picklepersistence.py +++ b/telegram/ext/picklepersistence.py @@ -20,10 +20,9 @@ import pickle from collections import defaultdict from copy import deepcopy +from typing import Any, DefaultDict, Dict, Optional, Tuple from telegram.ext import BasePersistence - -from typing import DefaultDict, Dict, Any, Tuple, Optional from telegram.utils.types import ConversationDict @@ -99,8 +98,8 @@ class PicklePersistence(BasePersistence): def load_singlefile(self) -> None: try: filename = self.filename - with open(self.filename, "rb") as f: - data = pickle.load(f) + with open(self.filename, "rb") as file: + data = pickle.load(file) self.user_data = defaultdict(dict, data['user_data']) self.chat_data = defaultdict(dict, data['chat_data']) # For backwards compatibility with files not containing bot data @@ -111,35 +110,37 @@ class PicklePersistence(BasePersistence): self.user_data = defaultdict(dict) self.chat_data = defaultdict(dict) self.bot_data = {} - except pickle.UnpicklingError: - raise TypeError("File {} does not contain valid pickle data".format(filename)) - except Exception: - raise TypeError("Something went wrong unpickling {}".format(filename)) + except pickle.UnpicklingError as exc: + raise TypeError("File {} does not contain valid pickle data".format(filename)) from exc + except Exception as exc: + raise TypeError("Something went wrong unpickling {}".format(filename)) from exc - def load_file(self, filename: str) -> Any: + @staticmethod + def load_file(filename: str) -> Any: try: - with open(filename, "rb") as f: - return pickle.load(f) + with open(filename, "rb") as file: + return pickle.load(file) except IOError: return None - except pickle.UnpicklingError: - raise TypeError("File {} does not contain valid pickle data".format(filename)) - except Exception: - raise TypeError("Something went wrong unpickling {}".format(filename)) + except pickle.UnpicklingError as exc: + raise TypeError("File {} does not contain valid pickle data".format(filename)) from exc + except Exception as exc: + raise TypeError("Something went wrong unpickling {}".format(filename)) from exc def dump_singlefile(self) -> None: - with open(self.filename, "wb") as f: + 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, } - pickle.dump(data, f) + pickle.dump(data, file) - def dump_file(self, filename: str, data: Any) -> None: - with open(filename, "wb") as f: - pickle.dump(data, f) + @staticmethod + def dump_file(filename: str, data: Any) -> None: + with open(filename, "wb") as file: + pickle.dump(data, file) def get_user_data(self) -> DefaultDict[int, Dict[Any, Any]]: """Returns the user_data from the pickle file if it exists or an empty :obj:`defaultdict`. diff --git a/telegram/ext/pollanswerhandler.py b/telegram/ext/pollanswerhandler.py index 5a779ff42..aa83a1fcc 100644 --- a/telegram/ext/pollanswerhandler.py +++ b/telegram/ext/pollanswerhandler.py @@ -16,12 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the PollAnswerHandler class.""" from telegram import Update -from .handler import Handler - from telegram.utils.types import HandlerArg +from .handler import Handler + class PollAnswerHandler(Handler): """Handler class to handle Telegram updates that contain a poll answer. diff --git a/telegram/ext/pollhandler.py b/telegram/ext/pollhandler.py index 10ad32e87..e317bbbb6 100644 --- a/telegram/ext/pollhandler.py +++ b/telegram/ext/pollhandler.py @@ -16,12 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +"""This module contains the PollHandler classes.""" from telegram import Update -from .handler import Handler - from telegram.utils.types import HandlerArg +from .handler import Handler + class PollHandler(Handler): """Handler class to handle Telegram updates that contain a poll. diff --git a/telegram/ext/precheckoutqueryhandler.py b/telegram/ext/precheckoutqueryhandler.py index 0fb552c7e..bd611013c 100644 --- a/telegram/ext/precheckoutqueryhandler.py +++ b/telegram/ext/precheckoutqueryhandler.py @@ -19,10 +19,10 @@ """This module contains the PreCheckoutQueryHandler class.""" from telegram import Update -from .handler import Handler - from telegram.utils.types import HandlerArg +from .handler import Handler + class PreCheckoutQueryHandler(Handler): """Handler class to handle Telegram PreCheckout callback queries. diff --git a/telegram/ext/regexhandler.py b/telegram/ext/regexhandler.py index 343b52133..04f428d6e 100644 --- a/telegram/ext/regexhandler.py +++ b/telegram/ext/regexhandler.py @@ -20,13 +20,11 @@ """This module contains the RegexHandler class.""" import warnings +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Pattern, TypeVar, Union +from telegram.ext import Filters, MessageHandler from telegram.utils.deprecate import TelegramDeprecationWarning - -from telegram.ext import MessageHandler, Filters - from telegram.utils.types import HandlerArg -from typing import Callable, TYPE_CHECKING, Any, Optional, Union, TypeVar, Dict, Pattern if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher @@ -119,7 +117,7 @@ class RegexHandler(MessageHandler): pass_job_queue: bool = False, pass_user_data: bool = False, pass_chat_data: bool = False, - allow_edited: bool = False, + allow_edited: bool = False, # pylint: disable=W0613 message_updates: bool = True, channel_post_updates: bool = False, edited_updates: bool = False, diff --git a/telegram/ext/shippingqueryhandler.py b/telegram/ext/shippingqueryhandler.py index 527b4cb3c..756a20a0f 100644 --- a/telegram/ext/shippingqueryhandler.py +++ b/telegram/ext/shippingqueryhandler.py @@ -19,10 +19,10 @@ """This module contains the ShippingQueryHandler class.""" from telegram import Update -from .handler import Handler - from telegram.utils.types import HandlerArg +from .handler import Handler + class ShippingQueryHandler(Handler): """Handler class to handle Telegram shipping callback queries. diff --git a/telegram/ext/stringcommandhandler.py b/telegram/ext/stringcommandhandler.py index bf84d979b..ac8089b61 100644 --- a/telegram/ext/stringcommandhandler.py +++ b/telegram/ext/stringcommandhandler.py @@ -18,10 +18,11 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the StringCommandHandler class.""" -from .handler import Handler +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, TypeVar from telegram.utils.types import HandlerArg -from typing import Callable, TYPE_CHECKING, Any, Optional, TypeVar, Dict, List + +from .handler import Handler if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher diff --git a/telegram/ext/stringregexhandler.py b/telegram/ext/stringregexhandler.py index f959dba77..f89bb85b9 100644 --- a/telegram/ext/stringregexhandler.py +++ b/telegram/ext/stringregexhandler.py @@ -19,12 +19,12 @@ """This module contains the StringRegexHandler class.""" import re +from typing import TYPE_CHECKING, Any, Callable, Dict, Match, Optional, Pattern, TypeVar, Union + +from telegram.utils.types import HandlerArg from .handler import Handler -from typing import Callable, TYPE_CHECKING, Optional, TypeVar, Match, Dict, Any, Union, Pattern -from telegram.utils.types import HandlerArg - if TYPE_CHECKING: from telegram.ext import CallbackContext, Dispatcher diff --git a/telegram/ext/typehandler.py b/telegram/ext/typehandler.py index 457eeae0d..74ba5c5c5 100644 --- a/telegram/ext/typehandler.py +++ b/telegram/ext/typehandler.py @@ -18,11 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the TypeHandler class.""" +from typing import TYPE_CHECKING, Any, Callable, Type, TypeVar + from .handler import Handler - -from typing import Callable, TYPE_CHECKING, TypeVar, Type, Any - if TYPE_CHECKING: from telegram.ext import CallbackContext @@ -76,7 +75,7 @@ class TypeHandler(Handler): def __init__( self, - type: Type, + type: Type, # pylint: disable=W0622 callback: Callable[[Any, 'CallbackContext'], RT], strict: bool = False, pass_update_queue: bool = False, @@ -104,5 +103,4 @@ class TypeHandler(Handler): """ if not self.strict: return isinstance(update, self.type) - else: - return type(update) is self.type + return type(update) is self.type # pylint: disable=C0123 diff --git a/telegram/ext/updater.py b/telegram/ext/updater.py index 5fd4d936c..365caaa90 100644 --- a/telegram/ext/updater.py +++ b/telegram/ext/updater.py @@ -21,20 +21,19 @@ import logging import ssl import warnings -from threading import Thread, Lock, current_thread, Event -from time import sleep -from signal import signal, SIGINT, SIGTERM, SIGABRT from queue import Queue +from signal import SIGABRT, SIGINT, SIGTERM, signal +from threading import Event, Lock, Thread, current_thread +from time import sleep +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union, no_type_check from telegram import Bot, TelegramError +from telegram.error import InvalidToken, RetryAfter, TimedOut, Unauthorized from telegram.ext import Dispatcher, JobQueue -from telegram.error import Unauthorized, InvalidToken, RetryAfter, TimedOut from telegram.utils.deprecate import TelegramDeprecationWarning from telegram.utils.helpers import get_signal_name from telegram.utils.request import Request -from telegram.utils.webhookhandler import WebhookServer, WebhookAppClass - -from typing import Callable, Dict, TYPE_CHECKING, Any, List, Union, Tuple, no_type_check, Optional +from telegram.utils.webhookhandler import WebhookAppClass, WebhookServer if TYPE_CHECKING: from telegram.ext import BasePersistence, Defaults @@ -232,14 +231,14 @@ class Updater: def _thread_wrapper(self, target: Callable, *args: Any, **kwargs: Any) -> None: thr_name = current_thread().name - self.logger.debug('{} - started'.format(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) raise - self.logger.debug('{} - ended'.format(thr_name)) + self.logger.debug('%s - ended', thr_name) def start_polling( self, @@ -465,9 +464,9 @@ class Updater: try: if not action_cb(): break - except RetryAfter as e: - self.logger.info('%s', e) - cur_interval = 0.5 + e.retry_after + except RetryAfter as 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) # If failure is due to timeout, we should retry asap. @@ -475,9 +474,9 @@ class Updater: except InvalidToken as pex: self.logger.error('Invalid token; aborting') raise pex - except TelegramError as te: - self.logger.error('Error while %s: %s', description, te) - onerr_cb(te) + except TelegramError as 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: cur_interval = interval @@ -525,8 +524,8 @@ class Updater: try: ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ssl_ctx.load_cert_chain(cert, key) - except ssl.SSLError: - raise TelegramError('Invalid SSL Certificate') + except ssl.SSLError as exc: + raise TelegramError('Invalid SSL Certificate') from exc else: ssl_ctx = None @@ -661,9 +660,9 @@ class Updater: @no_type_check def _join_threads(self) -> None: for thr in self.__threads: - self.logger.debug('Waiting for {} thread to end'.format(thr.name)) + self.logger.debug('Waiting for %s thread to end', thr.name) thr.join() - self.logger.debug('{} thread has ended'.format(thr.name)) + self.logger.debug('%s thread has ended', thr.name) self.__threads = [] @no_type_check @@ -671,7 +670,7 @@ class Updater: self.is_idle = False if self.running: self.logger.info( - 'Received signal {} ({}), stopping...'.format(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 @@ -682,6 +681,7 @@ class Updater: self.user_sig_handler(signum, frame) else: self.logger.warning('Exiting immediately!') + # pylint: disable=C0415,W0212 import os os._exit(1) diff --git a/telegram/files/animation.py b/telegram/files/animation.py index 61944118a..3e0b82b4a 100644 --- a/telegram/files/animation.py +++ b/telegram/files/animation.py @@ -17,11 +17,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Animation.""" -from telegram import PhotoSize -from telegram import TelegramObject +from typing import TYPE_CHECKING, Any, Optional +from telegram import PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -66,7 +65,7 @@ class Animation(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, width: int, diff --git a/telegram/files/audio.py b/telegram/files/audio.py index 0ebc73074..32ecadf2b 100644 --- a/telegram/files/audio.py +++ b/telegram/files/audio.py @@ -18,10 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Audio.""" -from telegram import TelegramObject, PhotoSize +from typing import TYPE_CHECKING, Any, Optional +from telegram import PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -67,7 +67,7 @@ class Audio(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, duration: int, diff --git a/telegram/files/chatphoto.py b/telegram/files/chatphoto.py index 0af6d246e..d5bf1fe07 100644 --- a/telegram/files/chatphoto.py +++ b/telegram/files/chatphoto.py @@ -17,9 +17,9 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ChatPhoto.""" -from telegram import TelegramObject +from typing import TYPE_CHECKING, Any -from typing import Any, TYPE_CHECKING +from telegram import TelegramObject if TYPE_CHECKING: from telegram import Bot, File @@ -63,7 +63,7 @@ class ChatPhoto(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 small_file_id: str, small_file_unique_id: str, big_file_id: str, diff --git a/telegram/files/contact.py b/telegram/files/contact.py index aa8dacdbe..ae16f2bc7 100644 --- a/telegram/files/contact.py +++ b/telegram/files/contact.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Contact.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class Contact(TelegramObject): """This object represents a phone contact. @@ -46,7 +47,7 @@ class Contact(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 phone_number: str, first_name: str, last_name: str = None, diff --git a/telegram/files/document.py b/telegram/files/document.py index 15d8b6c2f..1696881c4 100644 --- a/telegram/files/document.py +++ b/telegram/files/document.py @@ -18,10 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Document.""" -from telegram import PhotoSize, TelegramObject +from typing import TYPE_CHECKING, Any, Optional +from telegram import PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -62,7 +62,7 @@ class Document(TelegramObject): _id_keys = ('file_id',) def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, thumb: PhotoSize = None, diff --git a/telegram/files/file.py b/telegram/files/file.py index 27d60fe60..b4cd1c6b2 100644 --- a/telegram/files/file.py +++ b/telegram/files/file.py @@ -17,17 +17,15 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram File.""" +import os +import urllib.parse as urllib_parse from base64 import b64decode from os.path import basename -import os - -import urllib.parse as urllib_parse +from typing import IO, TYPE_CHECKING, Any, Optional, Union from telegram import TelegramObject from telegram.passport.credentials import decrypt -from typing import Any, Optional, IO, Union, TYPE_CHECKING - if TYPE_CHECKING: from telegram import Bot, FileCredentials @@ -70,7 +68,7 @@ class File(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, bot: 'Bot' = None, @@ -132,22 +130,22 @@ class File(TelegramObject): ) out.write(buf) return out - else: - if custom_path: - filename = custom_path - elif self.file_path: - filename = basename(self.file_path) - else: - filename = os.path.join(os.getcwd(), self.file_id) - buf = self.bot.request.retrieve(url, timeout=timeout) - if self._credentials: - buf = decrypt( - b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf - ) - with open(filename, 'wb') as fobj: - fobj.write(buf) - return filename + if custom_path: + filename = custom_path + elif self.file_path: + filename = basename(self.file_path) + else: + filename = os.path.join(os.getcwd(), self.file_id) + + buf = self.bot.request.retrieve(url, timeout=timeout) + if self._credentials: + buf = decrypt( + b64decode(self._credentials.secret), b64decode(self._credentials.hash), buf + ) + with open(filename, 'wb') as fobj: + fobj.write(buf) + return filename def _get_encoded_url(self) -> str: """Convert any UTF-8 char in :obj:`File.file_path` into a url encoded ASCII string.""" diff --git a/telegram/files/inputfile.py b/telegram/files/inputfile.py index 97e4eaf0c..d7edcf548 100644 --- a/telegram/files/inputfile.py +++ b/telegram/files/inputfile.py @@ -22,12 +22,11 @@ import imghdr import mimetypes import os +from typing import IO, Optional, Tuple from uuid import uuid4 from telegram import TelegramError -from typing import IO, Tuple, Optional - DEFAULT_MIME_TYPE = 'application/octet-stream' diff --git a/telegram/files/inputmedia.py b/telegram/files/inputmedia.py index 21f3f6d5e..379c7a177 100644 --- a/telegram/files/inputmedia.py +++ b/telegram/files/inputmedia.py @@ -18,11 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """Base class for Telegram InputMedia Objects.""" -from telegram import TelegramObject, InputFile, PhotoSize, Animation, Video, Audio, Document +from typing import IO, Union, cast + +from telegram import Animation, Audio, Document, InputFile, PhotoSize, TelegramObject, Video from telegram.utils.helpers import DEFAULT_NONE, DefaultValue - -from typing import Union, IO, cast - from telegram.utils.types import FileLike @@ -35,8 +34,6 @@ class InputMedia(TelegramObject): """ - pass - class InputMediaAnimation(InputMedia): """Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. diff --git a/telegram/files/location.py b/telegram/files/location.py index ad23fe331..e39f26bc5 100644 --- a/telegram/files/location.py +++ b/telegram/files/location.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Location.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class Location(TelegramObject): """This object represents a point on the map. @@ -39,7 +40,7 @@ class Location(TelegramObject): """ - def __init__(self, longitude: float, latitude: float, **kwargs: Any): + def __init__(self, longitude: float, latitude: float, **kwargs: Any): # pylint: disable=W0613 # Required self.longitude = float(longitude) self.latitude = float(latitude) diff --git a/telegram/files/photosize.py b/telegram/files/photosize.py index 7c64705e3..86df8f772 100644 --- a/telegram/files/photosize.py +++ b/telegram/files/photosize.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram PhotoSize.""" +from typing import TYPE_CHECKING, Any + from telegram import TelegramObject from telegram.utils.types import JSONDict -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -57,7 +58,7 @@ class PhotoSize(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, width: int, diff --git a/telegram/files/sticker.py b/telegram/files/sticker.py index bc97b5dab..9befcd790 100644 --- a/telegram/files/sticker.py +++ b/telegram/files/sticker.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains objects that represents stickers.""" +from typing import TYPE_CHECKING, Any, List, Optional, ClassVar + from telegram import PhotoSize, TelegramObject, constants from telegram.utils.types import JSONDict -from typing import Any, Optional, List, TYPE_CHECKING, ClassVar if TYPE_CHECKING: from telegram import Bot, File @@ -73,7 +74,7 @@ class Sticker(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, width: int, @@ -162,13 +163,13 @@ class StickerSet(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 name: str, title: str, is_animated: bool, contains_masks: bool, stickers: List[Sticker], - bot: 'Bot' = None, + bot: 'Bot' = None, # pylint: disable=W0613 thumb: PhotoSize = None, **kwargs: Any, ): @@ -242,7 +243,9 @@ class MaskPosition(TelegramObject): CHIN: ClassVar[str] = constants.STICKER_CHIN """:const:`telegram.constants.STICKER_CHIN`""" - def __init__(self, point: str, x_shift: float, y_shift: float, scale: float, **kwargs: Any): + def __init__( + self, point: str, x_shift: float, y_shift: float, scale: float, **kwargs: Any + ): # pylint: disable=W0613 self.point = point self.x_shift = x_shift self.y_shift = y_shift diff --git a/telegram/files/venue.py b/telegram/files/venue.py index df45bf0a4..6b4ed5ff4 100644 --- a/telegram/files/venue.py +++ b/telegram/files/venue.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Venue.""" -from telegram import TelegramObject, Location +from typing import TYPE_CHECKING, Any, Optional + +from telegram import Location, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -52,7 +53,7 @@ class Venue(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 location: Location, title: str, address: str, diff --git a/telegram/files/video.py b/telegram/files/video.py index 1d13e5a1e..0bc930a20 100644 --- a/telegram/files/video.py +++ b/telegram/files/video.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Video.""" +from typing import TYPE_CHECKING, Any, Optional + from telegram import PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -63,7 +64,7 @@ class Video(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, width: int, diff --git a/telegram/files/videonote.py b/telegram/files/videonote.py index 0af08a0e8..bad28edd7 100644 --- a/telegram/files/videonote.py +++ b/telegram/files/videonote.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram VideoNote.""" +from typing import TYPE_CHECKING, Any, Optional + from telegram import PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -60,7 +61,7 @@ class VideoNote(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, length: int, diff --git a/telegram/files/voice.py b/telegram/files/voice.py index f83576ce5..96a797b84 100644 --- a/telegram/files/voice.py +++ b/telegram/files/voice.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Voice.""" +from typing import TYPE_CHECKING, Any + from telegram import TelegramObject from telegram.utils.types import JSONDict -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File @@ -57,7 +58,7 @@ class Voice(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, duration: int, diff --git a/telegram/forcereply.py b/telegram/forcereply.py index cd8ac7330..2e7cc2850 100644 --- a/telegram/forcereply.py +++ b/telegram/forcereply.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ForceReply.""" -from telegram import ReplyMarkup from typing import Any +from telegram import ReplyMarkup + class ForceReply(ReplyMarkup): """ @@ -49,7 +50,9 @@ class ForceReply(ReplyMarkup): """ - def __init__(self, force_reply: bool = True, selective: bool = False, **kwargs: Any): + def __init__( + self, force_reply: bool = True, selective: bool = False, **kwargs: Any + ): # pylint: disable=W0613 # Required self.force_reply = bool(force_reply) # Optionals diff --git a/telegram/games/game.py b/telegram/games/game.py index 71af004f3..98c3a8d74 100644 --- a/telegram/games/game.py +++ b/telegram/games/game.py @@ -19,10 +19,10 @@ """This module contains an object that represents a Telegram Game.""" import sys +from typing import TYPE_CHECKING, Any, Dict, List, Optional -from telegram import MessageEntity, TelegramObject, Animation, PhotoSize +from telegram import Animation, MessageEntity, PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import List, Any, Dict, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -68,7 +68,7 @@ class Game(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 title: str, description: str, photo: List[PhotoSize], @@ -135,9 +135,8 @@ class Game(TelegramObject): # 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] - else: - entity_text = self.text.encode('utf-16-le') - entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] + 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') diff --git a/telegram/games/gamehighscore.py b/telegram/games/gamehighscore.py index be95a7e07..799a3266b 100644 --- a/telegram/games/gamehighscore.py +++ b/telegram/games/gamehighscore.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram GameHighScore.""" +from typing import TYPE_CHECKING, Optional + from telegram import TelegramObject, User from telegram.utils.types import JSONDict -from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot diff --git a/telegram/inline/inlinekeyboardbutton.py b/telegram/inline/inlinekeyboardbutton.py index b25edb214..803803e92 100644 --- a/telegram/inline/inlinekeyboardbutton.py +++ b/telegram/inline/inlinekeyboardbutton.py @@ -18,8 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineKeyboardButton.""" +from typing import TYPE_CHECKING, Any + from telegram import TelegramObject -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import CallbackGame, LoginUrl @@ -83,7 +84,7 @@ class InlineKeyboardButton(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 text: str, url: str = None, callback_data: str = None, diff --git a/telegram/inline/inlinekeyboardmarkup.py b/telegram/inline/inlinekeyboardmarkup.py index 12045e249..338b5b04b 100644 --- a/telegram/inline/inlinekeyboardmarkup.py +++ b/telegram/inline/inlinekeyboardmarkup.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineKeyboardMarkup.""" -from telegram import ReplyMarkup, InlineKeyboardButton +from typing import TYPE_CHECKING, Any, List, Optional + +from telegram import InlineKeyboardButton, ReplyMarkup from telegram.utils.types import JSONDict -from typing import Any, List, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -44,7 +45,9 @@ class InlineKeyboardMarkup(ReplyMarkup): """ - def __init__(self, inline_keyboard: List[List[InlineKeyboardButton]], **kwargs: Any): + def __init__( + self, inline_keyboard: List[List[InlineKeyboardButton]], **kwargs: Any + ): # pylint: disable=W0613 # Required self.inline_keyboard = inline_keyboard @@ -138,7 +141,7 @@ class InlineKeyboardMarkup(ReplyMarkup): if button != other.inline_keyboard[idx][jdx]: return False return True - return super(InlineKeyboardMarkup, self).__eq__(other) # pylint: disable=no-member + return super().__eq__(other) def __hash__(self) -> int: return hash(tuple(tuple(button for button in row) for row in self.inline_keyboard)) diff --git a/telegram/inline/inlinequery.py b/telegram/inline/inlinequery.py index 55373fb31..8a5fa92ae 100644 --- a/telegram/inline/inlinequery.py +++ b/telegram/inline/inlinequery.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# pylint: disable=R0902,R0912,R0913 +# pylint: disable=R0902,R0913 # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2020 @@ -19,9 +19,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineQuery.""" -from telegram import TelegramObject, User, Location +from typing import TYPE_CHECKING, Any, Optional + +from telegram import Location, TelegramObject, User from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -59,8 +60,8 @@ class InlineQuery(TelegramObject): """ def __init__( - self, - id: str, + self, # pylint: disable=W0613 + id: str, # pylint: disable=W0622 from_user: User, query: str, offset: str, @@ -69,7 +70,7 @@ class InlineQuery(TelegramObject): **kwargs: Any, ): # Required - self.id = id + self.id = id # pylint: disable=C0103 self.from_user = from_user self.query = query self.offset = offset diff --git a/telegram/inline/inlinequeryresult.py b/telegram/inline/inlinequeryresult.py index b11809541..9cadc35bf 100644 --- a/telegram/inline/inlinequeryresult.py +++ b/telegram/inline/inlinequeryresult.py @@ -16,11 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=W0622 """This module contains the classes that represent Telegram InlineQueryResult.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class InlineQueryResult(TelegramObject): """Baseclass for the InlineQueryResult* classes. @@ -39,10 +41,10 @@ class InlineQueryResult(TelegramObject): """ - def __init__(self, type: str, id: str, **kwargs: Any): + def __init__(self, type: str, id: str, **kwargs: Any): # pylint: disable=W0613 # Required self.type = str(type) - self.id = str(id) + self.id = str(id) # pylint: disable=C0103 self._id_attrs = (self.id,) diff --git a/telegram/inline/inlinequeryresultarticle.py b/telegram/inline/inlinequeryresultarticle.py index 847f60f9d..109e0aa1f 100644 --- a/telegram/inline/inlinequeryresultarticle.py +++ b/telegram/inline/inlinequeryresultarticle.py @@ -18,8 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultArticle.""" +from typing import TYPE_CHECKING, Any + from telegram import InlineQueryResult -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -64,7 +65,7 @@ class InlineQueryResultArticle(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 title: str, input_message_content: 'InputMessageContent', reply_markup: 'ReplyMarkup' = None, @@ -74,7 +75,7 @@ class InlineQueryResultArticle(InlineQueryResult): thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inlinequeryresultaudio.py b/telegram/inline/inlinequeryresultaudio.py index fd85720bf..7e0832b7b 100644 --- a/telegram/inline/inlinequeryresultaudio.py +++ b/telegram/inline/inlinequeryresultaudio.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultAudio.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -68,7 +69,7 @@ class InlineQueryResultAudio(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 audio_url: str, title: str, performer: str = None, @@ -77,7 +78,7 @@ class InlineQueryResultAudio(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inlinequeryresultcachedaudio.py b/telegram/inline/inlinequeryresultcachedaudio.py index 9ca1aab74..01c3d3566 100644 --- a/telegram/inline/inlinequeryresultcachedaudio.py +++ b/telegram/inline/inlinequeryresultcachedaudio.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedAudio.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -62,13 +63,13 @@ class InlineQueryResultCachedAudio(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 audio_file_id: str, caption: str = None, reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('audio', id) diff --git a/telegram/inline/inlinequeryresultcacheddocument.py b/telegram/inline/inlinequeryresultcacheddocument.py index 87e3aec06..785ff2365 100644 --- a/telegram/inline/inlinequeryresultcacheddocument.py +++ b/telegram/inline/inlinequeryresultcacheddocument.py @@ -16,11 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=W0622 """This module contains the classes that represent Telegram InlineQueryResultCachedDocument.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -68,7 +70,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 title: str, document_file_id: str, description: str = None, @@ -76,7 +78,7 @@ class InlineQueryResultCachedDocument(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('document', id) diff --git a/telegram/inline/inlinequeryresultcachedgif.py b/telegram/inline/inlinequeryresultcachedgif.py index 14eb8588d..4c3470f7b 100644 --- a/telegram/inline/inlinequeryresultcachedgif.py +++ b/telegram/inline/inlinequeryresultcachedgif.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedGif.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -67,14 +68,14 @@ class InlineQueryResultCachedGif(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 gif_file_id: str, title: str = None, caption: str = None, reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('gif', id) diff --git a/telegram/inline/inlinequeryresultcachedmpeg4gif.py b/telegram/inline/inlinequeryresultcachedmpeg4gif.py index 604046b29..f2928a86b 100644 --- a/telegram/inline/inlinequeryresultcachedmpeg4gif.py +++ b/telegram/inline/inlinequeryresultcachedmpeg4gif.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -67,14 +68,14 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 mpeg4_file_id: str, title: str = None, caption: str = None, reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('mpeg4_gif', id) diff --git a/telegram/inline/inlinequeryresultcachedphoto.py b/telegram/inline/inlinequeryresultcachedphoto.py index fe83974ad..086671b69 100644 --- a/telegram/inline/inlinequeryresultcachedphoto.py +++ b/telegram/inline/inlinequeryresultcachedphoto.py @@ -16,11 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=W0622 """This module contains the classes that represent Telegram InlineQueryResultPhoto""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -69,7 +71,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 photo_file_id: str, title: str = None, description: str = None, @@ -77,7 +79,7 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('photo', id) diff --git a/telegram/inline/inlinequeryresultcachedsticker.py b/telegram/inline/inlinequeryresultcachedsticker.py index 3a764739e..d0cba7f1c 100644 --- a/telegram/inline/inlinequeryresultcachedsticker.py +++ b/telegram/inline/inlinequeryresultcachedsticker.py @@ -18,11 +18,12 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedSticker.""" +from typing import TYPE_CHECKING, Any + from telegram import InlineQueryResult -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: - from telegram import ReplyMarkup, InputMessageContent + from telegram import InputMessageContent, ReplyMarkup class InlineQueryResultCachedSticker(InlineQueryResult): @@ -53,11 +54,11 @@ class InlineQueryResultCachedSticker(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 sticker_file_id: str, reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('sticker', id) diff --git a/telegram/inline/inlinequeryresultcachedvideo.py b/telegram/inline/inlinequeryresultcachedvideo.py index 6dd2ad820..986443ff4 100644 --- a/telegram/inline/inlinequeryresultcachedvideo.py +++ b/telegram/inline/inlinequeryresultcachedvideo.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVideo.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -69,7 +70,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 video_file_id: str, title: str, description: str = None, @@ -77,7 +78,7 @@ class InlineQueryResultCachedVideo(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('video', id) diff --git a/telegram/inline/inlinequeryresultcachedvoice.py b/telegram/inline/inlinequeryresultcachedvoice.py index 04f263d57..6e41cf8a2 100644 --- a/telegram/inline/inlinequeryresultcachedvoice.py +++ b/telegram/inline/inlinequeryresultcachedvoice.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultCachedVoice.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -64,14 +65,14 @@ class InlineQueryResultCachedVoice(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 voice_file_id: str, title: str, caption: str = None, reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('voice', id) diff --git a/telegram/inline/inlinequeryresultcontact.py b/telegram/inline/inlinequeryresultcontact.py index ef380b93e..c5ba4f400 100644 --- a/telegram/inline/inlinequeryresultcontact.py +++ b/telegram/inline/inlinequeryresultcontact.py @@ -18,11 +18,12 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultContact.""" +from typing import TYPE_CHECKING, Any + from telegram import InlineQueryResult -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: - from telegram import ReplyMarkup, InputMessageContent + from telegram import InputMessageContent, ReplyMarkup class InlineQueryResultContact(InlineQueryResult): @@ -67,7 +68,7 @@ class InlineQueryResultContact(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 phone_number: str, first_name: str, last_name: str = None, @@ -77,7 +78,7 @@ class InlineQueryResultContact(InlineQueryResult): thumb_width: int = None, thumb_height: int = None, vcard: str = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('contact', id) diff --git a/telegram/inline/inlinequeryresultdocument.py b/telegram/inline/inlinequeryresultdocument.py index 7809d089a..d16998b27 100644 --- a/telegram/inline/inlinequeryresultdocument.py +++ b/telegram/inline/inlinequeryresultdocument.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultDocument""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -79,7 +80,7 @@ class InlineQueryResultDocument(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 document_url: str, title: str, mime_type: str, @@ -91,7 +92,7 @@ class InlineQueryResultDocument(InlineQueryResult): thumb_width: int = None, thumb_height: int = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('document', id) diff --git a/telegram/inline/inlinequeryresultgame.py b/telegram/inline/inlinequeryresultgame.py index 5cd5d7cdb..123dd7fad 100644 --- a/telegram/inline/inlinequeryresultgame.py +++ b/telegram/inline/inlinequeryresultgame.py @@ -18,8 +18,9 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultGame.""" +from typing import TYPE_CHECKING, Any + from telegram import InlineQueryResult -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import ReplyMarkup @@ -45,11 +46,15 @@ class InlineQueryResultGame(InlineQueryResult): """ def __init__( - self, id: str, game_short_name: str, reply_markup: 'ReplyMarkup' = None, **kwargs: Any + self, + id: str, # pylint: disable=W0622 + game_short_name: str, + reply_markup: 'ReplyMarkup' = None, + **kwargs: Any, ): # Required super().__init__('game', id) - self.id = id + self.id = id # pylint: disable=W0622 self.game_short_name = game_short_name self.reply_markup = reply_markup diff --git a/telegram/inline/inlinequeryresultgif.py b/telegram/inline/inlinequeryresultgif.py index 93f058ecc..ee92022eb 100644 --- a/telegram/inline/inlinequeryresultgif.py +++ b/telegram/inline/inlinequeryresultgif.py @@ -16,11 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=W0622 """This module contains the classes that represent Telegram InlineQueryResultGif.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -79,7 +81,7 @@ class InlineQueryResultGif(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 gif_url: str, thumb_url: str, gif_width: int = None, @@ -91,7 +93,7 @@ class InlineQueryResultGif(InlineQueryResult): gif_duration: int = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, thumb_mime_type: str = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inlinequeryresultlocation.py b/telegram/inline/inlinequeryresultlocation.py index 772e0348e..815053a3f 100644 --- a/telegram/inline/inlinequeryresultlocation.py +++ b/telegram/inline/inlinequeryresultlocation.py @@ -18,11 +18,12 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultLocation.""" +from typing import TYPE_CHECKING, Any + from telegram import InlineQueryResult -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: - from telegram import ReplyMarkup, InputMessageContent + from telegram import InputMessageContent, ReplyMarkup class InlineQueryResultLocation(InlineQueryResult): @@ -67,7 +68,7 @@ class InlineQueryResultLocation(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 latitude: float, longitude: float, title: str, @@ -77,7 +78,7 @@ class InlineQueryResultLocation(InlineQueryResult): thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('location', id) diff --git a/telegram/inline/inlinequeryresultmpeg4gif.py b/telegram/inline/inlinequeryresultmpeg4gif.py index e5d563bc2..934404d3a 100644 --- a/telegram/inline/inlinequeryresultmpeg4gif.py +++ b/telegram/inline/inlinequeryresultmpeg4gif.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultMpeg4Gif.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -79,7 +80,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 mpeg4_url: str, thumb_url: str, mpeg4_width: int = None, @@ -91,7 +92,7 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): mpeg4_duration: int = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, thumb_mime_type: str = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inlinequeryresultphoto.py b/telegram/inline/inlinequeryresultphoto.py index 58706bc96..61a38655e 100644 --- a/telegram/inline/inlinequeryresultphoto.py +++ b/telegram/inline/inlinequeryresultphoto.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultPhoto.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -76,7 +77,7 @@ class InlineQueryResultPhoto(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 photo_url: str, thumb_url: str, photo_width: int = None, @@ -87,7 +88,7 @@ class InlineQueryResultPhoto(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required super().__init__('photo', id) diff --git a/telegram/inline/inlinequeryresultvenue.py b/telegram/inline/inlinequeryresultvenue.py index cfbae6e72..3a9866a31 100644 --- a/telegram/inline/inlinequeryresultvenue.py +++ b/telegram/inline/inlinequeryresultvenue.py @@ -18,11 +18,12 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVenue.""" +from typing import TYPE_CHECKING, Any + from telegram import InlineQueryResult -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: - from telegram import ReplyMarkup, InputMessageContent + from telegram import InputMessageContent, ReplyMarkup class InlineQueryResultVenue(InlineQueryResult): @@ -73,7 +74,7 @@ class InlineQueryResultVenue(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 latitude: float, longitude: float, title: str, @@ -85,7 +86,7 @@ class InlineQueryResultVenue(InlineQueryResult): thumb_url: str = None, thumb_width: int = None, thumb_height: int = None, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inlinequeryresultvideo.py b/telegram/inline/inlinequeryresultvideo.py index bfa433834..828bde72d 100644 --- a/telegram/inline/inlinequeryresultvideo.py +++ b/telegram/inline/inlinequeryresultvideo.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVideo.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -86,7 +87,7 @@ class InlineQueryResultVideo(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 video_url: str, mime_type: str, thumb_url: str, @@ -99,7 +100,7 @@ class InlineQueryResultVideo(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inlinequeryresultvoice.py b/telegram/inline/inlinequeryresultvoice.py index 4b61651e0..79c74551c 100644 --- a/telegram/inline/inlinequeryresultvoice.py +++ b/telegram/inline/inlinequeryresultvoice.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InlineQueryResultVoice.""" +from typing import TYPE_CHECKING, Any, Union + from telegram import InlineQueryResult from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union, TYPE_CHECKING if TYPE_CHECKING: from telegram import InputMessageContent, ReplyMarkup @@ -67,7 +68,7 @@ class InlineQueryResultVoice(InlineQueryResult): def __init__( self, - id: str, + id: str, # pylint: disable=W0622 voice_url: str, title: str, voice_duration: int = None, @@ -75,7 +76,7 @@ class InlineQueryResultVoice(InlineQueryResult): reply_markup: 'ReplyMarkup' = None, input_message_content: 'InputMessageContent' = None, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ): # Required diff --git a/telegram/inline/inputcontactmessagecontent.py b/telegram/inline/inputcontactmessagecontent.py index baca0558c..66a1ca011 100644 --- a/telegram/inline/inputcontactmessagecontent.py +++ b/telegram/inline/inputcontactmessagecontent.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputContactMessageContent.""" -from telegram import InputMessageContent from typing import Any +from telegram import InputMessageContent + class InputContactMessageContent(InputMessageContent): """Represents the content of a contact message to be sent as the result of an inline query. @@ -46,7 +47,7 @@ class InputContactMessageContent(InputMessageContent): """ def __init__( - self, + self, # pylint: disable=W0613 phone_number: str, first_name: str, last_name: str = None, diff --git a/telegram/inline/inputlocationmessagecontent.py b/telegram/inline/inputlocationmessagecontent.py index 8e938bc70..e59df3756 100644 --- a/telegram/inline/inputlocationmessagecontent.py +++ b/telegram/inline/inputlocationmessagecontent.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputLocationMessageContent.""" -from telegram import InputMessageContent from typing import Any +from telegram import InputMessageContent + class InputLocationMessageContent(InputMessageContent): # fmt: off @@ -46,7 +47,9 @@ class InputLocationMessageContent(InputMessageContent): """ # fmt: on - def __init__(self, latitude: float, longitude: float, live_period: int = None, **kwargs: Any): + def __init__( + self, latitude: float, longitude: float, live_period: int = None, **kwargs: Any + ): # pylint: disable=W0613 # Required self.latitude = latitude self.longitude = longitude diff --git a/telegram/inline/inputtextmessagecontent.py b/telegram/inline/inputtextmessagecontent.py index 8c42a048e..1f0d9137a 100644 --- a/telegram/inline/inputtextmessagecontent.py +++ b/telegram/inline/inputtextmessagecontent.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputTextMessageContent.""" +from typing import Any, Union + from telegram import InputMessageContent from telegram.utils.helpers import DEFAULT_NONE, DefaultValue -from typing import Any, Union class InputTextMessageContent(InputMessageContent): @@ -52,7 +53,7 @@ class InputTextMessageContent(InputMessageContent): """ def __init__( - self, + self, # pylint: disable=W0613 message_text: str, parse_mode: Union[str, DefaultValue] = DEFAULT_NONE, disable_web_page_preview: Union[bool, DefaultValue] = DEFAULT_NONE, diff --git a/telegram/inline/inputvenuemessagecontent.py b/telegram/inline/inputvenuemessagecontent.py index e36de1543..23c3ceeb4 100644 --- a/telegram/inline/inputvenuemessagecontent.py +++ b/telegram/inline/inputvenuemessagecontent.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains the classes that represent Telegram InputVenueMessageContent.""" -from telegram import InputMessageContent from typing import Any +from telegram import InputMessageContent + class InputVenueMessageContent(InputMessageContent): """Represents the content of a venue message to be sent as the result of an inline query. @@ -53,7 +54,7 @@ class InputVenueMessageContent(InputMessageContent): """ def __init__( - self, + self, # pylint: disable=W0613 latitude: float, longitude: float, title: str, diff --git a/telegram/keyboardbutton.py b/telegram/keyboardbutton.py index ecbbba232..7310d3684 100644 --- a/telegram/keyboardbutton.py +++ b/telegram/keyboardbutton.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram KeyboardButton.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class KeyboardButton(TelegramObject): """ @@ -61,7 +62,7 @@ class KeyboardButton(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 text: str, request_contact: bool = None, request_location: bool = None, diff --git a/telegram/keyboardbuttonpolltype.py b/telegram/keyboardbuttonpolltype.py index d72149def..c55c83648 100644 --- a/telegram/keyboardbuttonpolltype.py +++ b/telegram/keyboardbuttonpolltype.py @@ -18,9 +18,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a type of a Telegram Poll.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class KeyboardButtonPollType(TelegramObject): """This object represents type of a poll, which is allowed to be created @@ -36,7 +37,7 @@ class KeyboardButtonPollType(TelegramObject): create a poll of any type. """ - def __init__(self, type: str = None, **kwargs: Any): + def __init__(self, type: str = None, **kwargs: Any): # pylint: disable=W0613, W0622 self.type = type self._id_attrs = (self.type,) diff --git a/telegram/loginurl.py b/telegram/loginurl.py index c8a8c5ace..c9c68abea 100644 --- a/telegram/loginurl.py +++ b/telegram/loginurl.py @@ -18,9 +18,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram LoginUrl.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class LoginUrl(TelegramObject): """This object represents a parameter of the inline keyboard button used to automatically @@ -68,7 +69,7 @@ class LoginUrl(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 url: str, forward_text: bool = None, bot_username: str = None, diff --git a/telegram/message.py b/telegram/message.py index 5b39ad547..5c11ef176 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# pylint: disable=R0902,R0912,R0913 +# pylint: disable=R0902,R0913 # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2020 @@ -18,42 +18,41 @@ # 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 Message.""" -import sys import datetime +import sys from html import escape +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, ClassVar from telegram import ( Animation, Audio, - Contact, - Document, Chat, + Contact, + Dice, + Document, + Game, + InlineKeyboardMarkup, + Invoice, Location, + MessageEntity, + ParseMode, + PassportData, PhotoSize, + Poll, Sticker, + SuccessfulPayment, TelegramObject, User, - Video, - Voice, Venue, - MessageEntity, - Game, - Invoice, - SuccessfulPayment, + Video, VideoNote, - PassportData, - Poll, - InlineKeyboardMarkup, - Dice, + Voice, ) -from telegram import ParseMode -from telegram.utils.helpers import escape_markdown, to_timestamp, from_timestamp - +from telegram.utils.helpers import escape_markdown, from_timestamp, to_timestamp from telegram.utils.types import JSONDict -from typing import Any, List, Dict, Optional, Union, TYPE_CHECKING, ClassVar if TYPE_CHECKING: - from telegram import Bot, InputMedia, GameHighScore + from telegram import Bot, GameHighScore, InputMedia _UNDEFINED = object() @@ -294,7 +293,7 @@ class Message(TelegramObject): ] + ATTACHMENT_TYPES def __init__( - self, + self, # pylint: disable=W0613 message_id: int, date: datetime.datetime, chat: Chat, @@ -512,10 +511,10 @@ class Message(TelegramObject): return self._effective_attachment # type: ignore - def __getitem__(self, item: str) -> Any: + def __getitem__(self, item: str) -> Any: # pylint: disable=R1710 if item in self.__dict__.keys(): return self.__dict__[item] - elif item == 'chat_id': + if item == 'chat_id': return self.chat.id def to_dict(self) -> JSONDict: @@ -1169,11 +1168,10 @@ class Message(TelegramObject): # 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] - else: - 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') + 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') def parse_caption_entity(self, entity: MessageEntity) -> str: """Returns the text from a given :class:`telegram.MessageEntity`. @@ -1200,11 +1198,10 @@ class Message(TelegramObject): # Is it a narrow build, if so we don't need to convert if sys.maxunicode == 0xFFFF: return self.caption[entity.offset : entity.offset + entity.length] - else: - 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') + 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') def parse_entities(self, types: List[str] = None) -> Dict[MessageEntity, str]: """ @@ -1280,7 +1277,7 @@ class Message(TelegramObject): if message_text is None: return None - if not sys.maxunicode == 0xFFFF: + if sys.maxunicode != 0xFFFF: message_text = message_text.encode('utf-16-le') # type: ignore html_text = '' @@ -1298,7 +1295,7 @@ class Message(TelegramObject): and e.offset + e.length <= entity.offset + entity.length and e != entity } - parsed_entities.extend([e for e in nested_entities.keys()]) + parsed_entities.extend(list(nested_entities.keys())) text = escape(text) @@ -1442,7 +1439,7 @@ class Message(TelegramObject): if message_text is None: return None - if not sys.maxunicode == 0xFFFF: + if sys.maxunicode != 0xFFFF: message_text = message_text.encode('utf-16-le') # type: ignore markdown_text = '' @@ -1460,7 +1457,7 @@ class Message(TelegramObject): and e.offset + e.length <= entity.offset + entity.length and e != entity } - parsed_entities.extend([e for e in nested_entities.keys()]) + parsed_entities.extend(list(nested_entities.keys())) orig_text = text text = escape_markdown(text, version=version) diff --git a/telegram/messageentity.py b/telegram/messageentity.py index 92dc53fff..ea80e9590 100644 --- a/telegram/messageentity.py +++ b/telegram/messageentity.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram MessageEntity.""" -from telegram import User, TelegramObject, constants +from typing import TYPE_CHECKING, Any, List, Optional, ClassVar + +from telegram import TelegramObject, User, constants from telegram.utils.types import JSONDict -from typing import Any, Optional, List, TYPE_CHECKING, ClassVar if TYPE_CHECKING: from telegram import Bot @@ -59,8 +60,8 @@ class MessageEntity(TelegramObject): """ def __init__( - self, - type: str, + self, # pylint: disable=W0613 + type: str, # pylint: disable=W0622 offset: int, length: int, url: str = None, diff --git a/telegram/passport/credentials.py b/telegram/passport/credentials.py index 3fb94cb11..9004e546d 100644 --- a/telegram/passport/credentials.py +++ b/telegram/passport/credentials.py @@ -16,22 +16,24 @@ # # 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=C0114, E0401, W0622 try: import ujson as json except ImportError: import json # type: ignore[no-redef] + from base64 import b64decode +from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union, no_type_check from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric.padding import OAEP, MGF1 +from cryptography.hazmat.primitives.asymmetric.padding import MGF1, OAEP from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC -from cryptography.hazmat.primitives.hashes import SHA512, SHA256, Hash, SHA1 +from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512, Hash -from telegram import TelegramObject, TelegramError +from telegram import TelegramError, TelegramObject from telegram.utils.types import JSONDict -from typing import Union, Any, Optional, TYPE_CHECKING, List, no_type_check, Tuple if TYPE_CHECKING: from telegram import Bot @@ -77,9 +79,9 @@ def decrypt(secret, hash, data): digest.update(secret + hash) secret_hash_hash = digest.finalize() # First 32 chars is our key, next 16 is the initialisation vector - key, iv = secret_hash_hash[:32], secret_hash_hash[32 : 32 + 16] + key, init_vector = secret_hash_hash[:32], secret_hash_hash[32 : 32 + 16] # Init a AES-CBC cipher and decrypt the data - cipher = Cipher(AES(key), CBC(iv), backend=default_backend()) + cipher = Cipher(AES(key), CBC(init_vector), backend=default_backend()) decryptor = cipher.decryptor() data = decryptor.update(data) + decryptor.finalize() # Calculate SHA256 hash of the decrypted data @@ -129,7 +131,9 @@ class EncryptedCredentials(TelegramObject): """ - 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 + ): # pylint: disable=W0613 # Required self.data = data self.hash = hash @@ -162,9 +166,9 @@ class EncryptedCredentials(TelegramObject): b64decode(self.secret), OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None), ) - except ValueError as e: + except ValueError as exception: # If decryption fails raise exception - raise TelegramDecryptionError(e) + raise TelegramDecryptionError(exception) from exception return self._decrypted_secret @property @@ -193,7 +197,9 @@ class Credentials(TelegramObject): nonce (:obj:`str`): Bot-specified nonce """ - 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 + ): # pylint: disable=W0613 # Required self.secure_data = secure_data self.nonce = nonce @@ -241,7 +247,7 @@ class SecureData(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 personal_details: 'SecureValue' = None, passport: 'SecureValue' = None, internal_passport: 'SecureValue' = None, @@ -325,7 +331,7 @@ class SecureValue(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 data: 'DataCredentials' = None, front_side: 'FileCredentials' = None, reverse_side: 'FileCredentials' = None, @@ -372,7 +378,9 @@ class SecureValue(TelegramObject): class _CredentialsBase(TelegramObject): """Base class for DataCredentials and FileCredentials.""" - def __init__(self, hash: str, secret: str, bot: 'Bot' = None, **kwargs: Any): + def __init__( + self, hash: str, secret: str, bot: 'Bot' = None, **kwargs: Any + ): # pylint: disable=W0613 self.hash = hash self.secret = secret @@ -397,7 +405,7 @@ class DataCredentials(_CredentialsBase): secret (:obj:`str`): Secret of encrypted data """ - def __init__(self, data_hash: str, secret: str, **kwargs: Any): + def __init__(self, data_hash: str, secret: str, **kwargs: Any): # pylint: disable=W0613 super().__init__(data_hash, secret, **kwargs) def to_dict(self) -> JSONDict: @@ -423,7 +431,7 @@ class FileCredentials(_CredentialsBase): secret (:obj:`str`): Secret of encrypted file """ - def __init__(self, file_hash: str, secret: str, **kwargs: Any): + def __init__(self, file_hash: str, secret: str, **kwargs: Any): # pylint: disable=W0613 super().__init__(file_hash, secret, **kwargs) def to_dict(self) -> JSONDict: diff --git a/telegram/passport/data.py b/telegram/passport/data.py index b692f3aae..d4322a1c1 100644 --- a/telegram/passport/data.py +++ b/telegram/passport/data.py @@ -16,8 +16,10 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=C0114 +from typing import TYPE_CHECKING, Any + from telegram import TelegramObject -from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -45,7 +47,7 @@ class PersonalDetails(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 first_name: str, last_name: str, birth_date: str, @@ -88,7 +90,7 @@ class ResidentialAddress(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 street_line1: str, street_line2: str, city: str, @@ -118,7 +120,9 @@ class IdDocumentData(TelegramObject): expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format. """ - 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 + ): # pylint: disable=W0613 self.document_no = document_no self.expiry_date = expiry_date diff --git a/telegram/passport/encryptedpassportelement.py b/telegram/passport/encryptedpassportelement.py index f6589b529..32934bbfe 100644 --- a/telegram/passport/encryptedpassportelement.py +++ b/telegram/passport/encryptedpassportelement.py @@ -18,6 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram EncryptedPassportElement.""" from base64 import b64decode +from typing import TYPE_CHECKING, Any, List, Optional from telegram import ( IdDocumentData, @@ -27,9 +28,7 @@ from telegram import ( TelegramObject, ) from telegram.passport.credentials import decrypt_json - from telegram.utils.types import JSONDict -from typing import List, Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, Credentials @@ -49,7 +48,8 @@ class EncryptedPassportElement(TelegramObject): "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email". - data (:class:`telegram.PersonalDetails` or :class:`telegram.IdDocument` or :class:`telegram.ResidentialAddress` or :obj:`str`): + data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ + :class:`telegram.ResidentialAddress` | :obj:`str`): Optional. Decrypted or encrypted data, available for "personal_details", "passport", "driver_license", "identity_card", "identity_passport" and "address" types. phone_number (:obj:`str`): Optional. User's verified phone number, available only for @@ -82,7 +82,8 @@ class EncryptedPassportElement(TelegramObject): "identity_card", "internal_passport", "address", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", "phone_number", "email". - data (:class:`telegram.PersonalDetails` or :class:`telegram.IdDocument` or :class:`telegram.ResidentialAddress` or :obj:`str`, optional): + data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocument` | \ + :class:`telegram.ResidentialAddress` | :obj:`str`, optional): Decrypted or encrypted data, available for "personal_details", "passport", "driver_license", "identity_card", "identity_passport" and "address" types. phone_number (:obj:`str`, optional): User's verified phone number, available only for @@ -117,8 +118,8 @@ class EncryptedPassportElement(TelegramObject): """ def __init__( - self, - type: str, + self, # pylint: disable=W0613 + type: str, # pylint: disable=W0622 data: PersonalDetails = None, phone_number: str = None, email: str = None, @@ -127,9 +128,9 @@ class EncryptedPassportElement(TelegramObject): reverse_side: PassportFile = None, selfie: PassportFile = None, translation: List[PassportFile] = None, - hash: str = None, + hash: str = None, # pylint: disable=W0622 bot: 'Bot' = None, - credentials: 'Credentials' = None, + credentials: 'Credentials' = None, # pylint: disable=W0613 **kwargs: Any, ): # Required diff --git a/telegram/passport/passportdata.py b/telegram/passport/passportdata.py index 45d6f7a25..2310d0341 100644 --- a/telegram/passport/passportdata.py +++ b/telegram/passport/passportdata.py @@ -18,10 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """Contains information about Telegram Passport data shared with the bot by the user.""" -from telegram import EncryptedCredentials, EncryptedPassportElement, TelegramObject +from typing import TYPE_CHECKING, Any, List, Optional +from telegram import EncryptedCredentials, EncryptedPassportElement, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, List, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, Credentials @@ -52,7 +52,7 @@ class PassportData(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 data: List[EncryptedPassportElement], credentials: EncryptedCredentials, bot: 'Bot' = None, diff --git a/telegram/passport/passportelementerrors.py b/telegram/passport/passportelementerrors.py index cc9ab51b6..f287b42a2 100644 --- a/telegram/passport/passportelementerrors.py +++ b/telegram/passport/passportelementerrors.py @@ -16,11 +16,13 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=W0622 """This module contains the classes that represent Telegram PassportElementError.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class PassportElementError(TelegramObject): """Baseclass for the PassportElementError* classes. @@ -40,7 +42,9 @@ class PassportElementError(TelegramObject): """ - def __init__(self, source: str, type: str, message: str, **kwargs: Any): + def __init__( + self, source: str, type: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required self.source = str(source) self.type = str(type) @@ -77,7 +81,9 @@ class PassportElementErrorDataField(PassportElementError): """ - def __init__(self, type: str, field_name: str, data_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, field_name: str, data_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('data', type, message) self.field_name = field_name @@ -112,7 +118,9 @@ class PassportElementErrorFile(PassportElementError): """ - def __init__(self, type: str, file_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('file', type, message) self.file_hash = file_hash @@ -146,14 +154,14 @@ class PassportElementErrorFiles(PassportElementError): """ - def __init__(self, type: str, file_hashes: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hashes: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('files', type, message) self.file_hashes = file_hashes - self._id_attrs = (self.source, self.type, self.message) + tuple( - [file_hash for file_hash in file_hashes] - ) + self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) class PassportElementErrorFrontSide(PassportElementError): @@ -182,7 +190,9 @@ class PassportElementErrorFrontSide(PassportElementError): """ - def __init__(self, type: str, file_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('front_side', type, message) self.file_hash = file_hash @@ -216,7 +226,9 @@ class PassportElementErrorReverseSide(PassportElementError): """ - def __init__(self, type: str, file_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('reverse_side', type, message) self.file_hash = file_hash @@ -248,7 +260,9 @@ class PassportElementErrorSelfie(PassportElementError): """ - def __init__(self, type: str, file_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('selfie', type, message) self.file_hash = file_hash @@ -284,7 +298,9 @@ class PassportElementErrorTranslationFile(PassportElementError): """ - def __init__(self, type: str, file_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('translation_file', type, message) self.file_hash = file_hash @@ -320,14 +336,14 @@ class PassportElementErrorTranslationFiles(PassportElementError): """ - def __init__(self, type: str, file_hashes: str, message: str, **kwargs: Any): + def __init__( + self, type: str, file_hashes: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('translation_files', type, message) self.file_hashes = file_hashes - self._id_attrs = (self.source, self.type, self.message) + tuple( - [file_hash for file_hash in file_hashes] - ) + self._id_attrs = (self.source, self.type, self.message) + tuple(file_hashes) class PassportElementErrorUnspecified(PassportElementError): @@ -352,7 +368,9 @@ class PassportElementErrorUnspecified(PassportElementError): """ - def __init__(self, type: str, element_hash: str, message: str, **kwargs: Any): + def __init__( + self, type: str, element_hash: str, message: str, **kwargs: Any + ): # pylint: disable=W0613 # Required super().__init__('unspecified', type, message) self.element_hash = element_hash diff --git a/telegram/passport/passportfile.py b/telegram/passport/passportfile.py index 4c8a7771d..bb35dee82 100644 --- a/telegram/passport/passportfile.py +++ b/telegram/passport/passportfile.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Encrypted PassportFile.""" +from typing import TYPE_CHECKING, Any, List, Optional + from telegram import TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, List, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot, File, FileCredentials @@ -57,7 +58,7 @@ class PassportFile(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 file_id: str, file_unique_id: str, file_date: int, diff --git a/telegram/payment/invoice.py b/telegram/payment/invoice.py index 5e1751af5..e0bdb651a 100644 --- a/telegram/payment/invoice.py +++ b/telegram/payment/invoice.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Invoice.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class Invoice(TelegramObject): """This object contains basic information about an invoice. @@ -53,7 +54,7 @@ class Invoice(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 title: str, description: str, start_parameter: str, diff --git a/telegram/payment/labeledprice.py b/telegram/payment/labeledprice.py index ed5db0e23..f23965918 100644 --- a/telegram/payment/labeledprice.py +++ b/telegram/payment/labeledprice.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram LabeledPrice.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class LabeledPrice(TelegramObject): """This object represents a portion of the price for goods or services. @@ -44,7 +45,7 @@ class LabeledPrice(TelegramObject): """ - def __init__(self, label: str, amount: int, **kwargs: Any): + def __init__(self, label: str, amount: int, **kwargs: Any): # pylint: disable=W0613 self.label = label self.amount = amount diff --git a/telegram/payment/orderinfo.py b/telegram/payment/orderinfo.py index 4512af55d..87d19b0d3 100644 --- a/telegram/payment/orderinfo.py +++ b/telegram/payment/orderinfo.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram OrderInfo.""" -from telegram import TelegramObject, ShippingAddress +from typing import TYPE_CHECKING, Any, Optional + +from telegram import ShippingAddress, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -49,7 +50,7 @@ class OrderInfo(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 name: str = None, phone_number: str = None, email: str = None, diff --git a/telegram/payment/precheckoutquery.py b/telegram/payment/precheckoutquery.py index bc09fe47f..945d64693 100644 --- a/telegram/payment/precheckoutquery.py +++ b/telegram/payment/precheckoutquery.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram PreCheckoutQuery.""" -from telegram import TelegramObject, User, OrderInfo +from typing import TYPE_CHECKING, Any, Optional + +from telegram import OrderInfo, TelegramObject, User from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -66,8 +67,8 @@ class PreCheckoutQuery(TelegramObject): """ def __init__( - self, - id: str, + self, # pylint: disable=W0613 + id: str, # pylint: disable=W0622 from_user: User, currency: str, total_amount: int, @@ -77,7 +78,7 @@ class PreCheckoutQuery(TelegramObject): bot: 'Bot' = None, **kwargs: Any, ): - self.id = id + self.id = id # pylint: disable=C0103 self.from_user = from_user self.currency = currency self.total_amount = total_amount diff --git a/telegram/payment/shippingaddress.py b/telegram/payment/shippingaddress.py index 46a9262bd..d2e980e7f 100644 --- a/telegram/payment/shippingaddress.py +++ b/telegram/payment/shippingaddress.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingAddress.""" -from telegram import TelegramObject from typing import Any +from telegram import TelegramObject + class ShippingAddress(TelegramObject): """This object represents a Telegram ShippingAddress. @@ -49,7 +50,7 @@ class ShippingAddress(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 country_code: str, state: str, city: str, diff --git a/telegram/payment/shippingoption.py b/telegram/payment/shippingoption.py index ac97a4ab7..29907f7d8 100644 --- a/telegram/payment/shippingoption.py +++ b/telegram/payment/shippingoption.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingOption.""" +from typing import TYPE_CHECKING, Any, List + from telegram import TelegramObject from telegram.utils.types import JSONDict -from typing import List, Any, TYPE_CHECKING if TYPE_CHECKING: from telegram import LabeledPrice # noqa @@ -45,8 +46,14 @@ class ShippingOption(TelegramObject): """ - def __init__(self, id: str, title: str, prices: List['LabeledPrice'], **kwargs: Any): - self.id = id + def __init__( + self, # pylint: disable=W0613 + id: str, # pylint: disable=W0622 + title: str, + prices: List['LabeledPrice'], + **kwargs: Any, + ): + self.id = id # pylint: disable=C0103 self.title = title self.prices = prices diff --git a/telegram/payment/shippingquery.py b/telegram/payment/shippingquery.py index da9e66533..156539e49 100644 --- a/telegram/payment/shippingquery.py +++ b/telegram/payment/shippingquery.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ShippingQuery.""" -from telegram import TelegramObject, User, ShippingAddress +from typing import TYPE_CHECKING, Any, Optional + +from telegram import ShippingAddress, TelegramObject, User from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -53,15 +54,15 @@ class ShippingQuery(TelegramObject): """ def __init__( - self, - id: str, + self, # pylint: disable=W0613 + id: str, # pylint: disable=W0622 from_user: User, invoice_payload: str, shipping_address: ShippingAddress, bot: 'Bot' = None, **kwargs: Any, ): - self.id = id + self.id = id # pylint: disable=C0103 self.from_user = from_user self.invoice_payload = invoice_payload self.shipping_address = shipping_address diff --git a/telegram/payment/successfulpayment.py b/telegram/payment/successfulpayment.py index 490684b05..b2417eb89 100644 --- a/telegram/payment/successfulpayment.py +++ b/telegram/payment/successfulpayment.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram SuccessfulPayment.""" -from telegram import TelegramObject, OrderInfo +from typing import TYPE_CHECKING, Any, Optional + +from telegram import OrderInfo, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -62,7 +63,7 @@ class SuccessfulPayment(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 currency: str, total_amount: int, invoice_payload: str, diff --git a/telegram/poll.py b/telegram/poll.py index 4c394e74a..ada093416 100644 --- a/telegram/poll.py +++ b/telegram/poll.py @@ -19,13 +19,13 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Poll.""" -import sys import datetime +import sys +from typing import TYPE_CHECKING, Any, Dict, List, Optional, ClassVar -from telegram import TelegramObject, User, MessageEntity, constants -from telegram.utils.helpers import to_timestamp, from_timestamp +from telegram import MessageEntity, TelegramObject, User, constants +from telegram.utils.helpers import from_timestamp, to_timestamp from telegram.utils.types import JSONDict -from typing import Any, Dict, Optional, List, TYPE_CHECKING, ClassVar if TYPE_CHECKING: from telegram import Bot @@ -48,7 +48,7 @@ class PollOption(TelegramObject): """ - def __init__(self, text: str, voter_count: int, **kwargs: Any): + def __init__(self, text: str, voter_count: int, **kwargs: Any): # pylint: disable=W0613 self.text = text self.voter_count = voter_count @@ -75,7 +75,9 @@ class PollAnswer(TelegramObject): """ - def __init__(self, poll_id: str, user: User, option_ids: List[int], **kwargs: Any): + def __init__( + self, poll_id: str, user: User, option_ids: List[int], **kwargs: Any + ): # pylint: disable=W0613 self.poll_id = poll_id self.user = user self.option_ids = option_ids @@ -143,14 +145,14 @@ class Poll(TelegramObject): """ def __init__( - self, - id: str, + self, # pylint: disable=W0613 + id: str, # pylint: disable=W0622 question: str, options: List[PollOption], total_voter_count: int, is_closed: bool, is_anonymous: bool, - type: str, + type: str, # pylint: disable=W0622 allows_multiple_answers: bool, correct_option_id: int = None, explanation: str = None, @@ -159,7 +161,7 @@ class Poll(TelegramObject): close_date: datetime.datetime = None, **kwargs: Any, ): - self.id = id + self.id = id # pylint: disable=C0103 self.question = question self.options = options self.total_voter_count = total_voter_count @@ -223,9 +225,8 @@ class Poll(TelegramObject): # 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] - else: - entity_text = self.explanation.encode('utf-16-le') - entity_text = entity_text[entity.offset * 2 : (entity.offset + entity.length) * 2] + 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') diff --git a/telegram/replykeyboardmarkup.py b/telegram/replykeyboardmarkup.py index 3d72f9a51..4d5b7712a 100644 --- a/telegram/replykeyboardmarkup.py +++ b/telegram/replykeyboardmarkup.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ReplyKeyboardMarkup.""" -from telegram import ReplyMarkup, KeyboardButton +from typing import Any, List, Union + +from telegram import KeyboardButton, ReplyMarkup from telegram.utils.types import JSONDict -from typing import List, Union, Any class ReplyKeyboardMarkup(ReplyMarkup): @@ -65,7 +66,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): """ def __init__( - self, + self, # pylint: disable=W0613 keyboard: List[List[Union[str, KeyboardButton]]], resize_keyboard: bool = False, one_time_keyboard: bool = False, @@ -75,13 +76,13 @@ class ReplyKeyboardMarkup(ReplyMarkup): # Required self.keyboard = [] for row in keyboard: - r = [] + button_row = [] for button in row: if isinstance(button, KeyboardButton): - r.append(button) # telegram.KeyboardButton + button_row.append(button) # telegram.KeyboardButton else: - r.append(KeyboardButton(button)) # str - self.keyboard.append(r) + button_row.append(KeyboardButton(button)) # str + self.keyboard.append(button_row) # Optionals self.resize_keyboard = bool(resize_keyboard) @@ -93,13 +94,13 @@ class ReplyKeyboardMarkup(ReplyMarkup): data['keyboard'] = [] for row in self.keyboard: - r: List[Union[JSONDict, str]] = [] + button_row: List[Union[JSONDict, str]] = [] for button in row: if isinstance(button, KeyboardButton): - r.append(button.to_dict()) # telegram.KeyboardButton + button_row.append(button.to_dict()) # telegram.KeyboardButton else: - r.append(button) # str - data['keyboard'].append(r) + button_row.append(button) # str + data['keyboard'].append(button_row) return data @classmethod @@ -109,7 +110,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): resize_keyboard: bool = False, one_time_keyboard: bool = False, selective: bool = False, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ) -> 'ReplyKeyboardMarkup': """Shortcut for:: @@ -154,7 +155,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): resize_keyboard: bool = False, one_time_keyboard: bool = False, selective: bool = False, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ) -> 'ReplyKeyboardMarkup': """Shortcut for:: @@ -200,7 +201,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): resize_keyboard: bool = False, one_time_keyboard: bool = False, selective: bool = False, - **kwargs: Any, + **kwargs: Any, # pylint: disable=W0613 ) -> 'ReplyKeyboardMarkup': """Shortcut for:: @@ -251,7 +252,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): if button != other.keyboard[idx][jdx]: return False return True - return super(ReplyKeyboardMarkup, self).__eq__(other) # pylint: disable=no-member + return super().__eq__(other) def __hash__(self) -> int: return hash( diff --git a/telegram/replykeyboardremove.py b/telegram/replykeyboardremove.py index 5003eaa73..806a2da93 100644 --- a/telegram/replykeyboardremove.py +++ b/telegram/replykeyboardremove.py @@ -17,9 +17,10 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ReplyKeyboardRemove.""" -from telegram import ReplyMarkup from typing import Any +from telegram import ReplyMarkup + class ReplyKeyboardRemove(ReplyMarkup): """ @@ -54,7 +55,7 @@ class ReplyKeyboardRemove(ReplyMarkup): """ - def __init__(self, selective: bool = False, **kwargs: Any): + def __init__(self, selective: bool = False, **kwargs: Any): # pylint: disable=W0613 # Required self.remove_keyboard = True # Optionals diff --git a/telegram/replymarkup.py b/telegram/replymarkup.py index 8f5bf8bf1..44b3f8713 100644 --- a/telegram/replymarkup.py +++ b/telegram/replymarkup.py @@ -28,5 +28,3 @@ class ReplyMarkup(TelegramObject): detailed use. """ - - pass diff --git a/telegram/update.py b/telegram/update.py index c4df39bc4..278b74d0d 100644 --- a/telegram/update.py +++ b/telegram/update.py @@ -18,22 +18,23 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram Update.""" +from typing import TYPE_CHECKING, Any, Optional + from telegram import ( - Message, - TelegramObject, - InlineQuery, - ChosenInlineResult, CallbackQuery, - ShippingQuery, - PreCheckoutQuery, + ChosenInlineResult, + InlineQuery, + Message, Poll, + PreCheckoutQuery, + ShippingQuery, + TelegramObject, ) from telegram.poll import PollAnswer from telegram.utils.types import JSONDict -from typing import Any, Optional, TYPE_CHECKING if TYPE_CHECKING: - from telegram import Bot, User, Chat # noqa + from telegram import Bot, Chat, User # noqa class Update(TelegramObject): @@ -97,7 +98,7 @@ class Update(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 update_id: int, message: Message = None, edited_message: Message = None, diff --git a/telegram/user.py b/telegram/user.py index f9ac340ff..210c420a6 100644 --- a/telegram/user.py +++ b/telegram/user.py @@ -19,14 +19,14 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram User.""" +from typing import TYPE_CHECKING, Any, List, Optional + from telegram import TelegramObject, constants from telegram.utils.helpers import mention_html as util_mention_html from telegram.utils.helpers import mention_markdown as util_mention_markdown -from typing import Any, Optional, TYPE_CHECKING, List - if TYPE_CHECKING: - from telegram import Bot, UserProfilePhotos, Message + from telegram import Bot, Message, UserProfilePhotos class User(TelegramObject): @@ -68,7 +68,7 @@ class User(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 id: int, first_name: str, is_bot: bool, diff --git a/telegram/userprofilephotos.py b/telegram/userprofilephotos.py index 773634420..fea31896c 100644 --- a/telegram/userprofilephotos.py +++ b/telegram/userprofilephotos.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram UserProfilePhotos.""" +from typing import TYPE_CHECKING, Any, List, Optional + from telegram import PhotoSize, TelegramObject from telegram.utils.types import JSONDict -from typing import Any, List, Optional, TYPE_CHECKING if TYPE_CHECKING: from telegram import Bot @@ -43,7 +44,9 @@ class UserProfilePhotos(TelegramObject): """ - def __init__(self, total_count: int, photos: List[List[PhotoSize]], **kwargs: Any): + def __init__( + self, total_count: int, photos: List[List[PhotoSize]], **kwargs: Any + ): # pylint: disable=W0613 # Required self.total_count = int(total_count) self.photos = photos diff --git a/telegram/utils/deprecate.py b/telegram/utils/deprecate.py index 2d920321e..356d2c60a 100644 --- a/telegram/utils/deprecate.py +++ b/telegram/utils/deprecate.py @@ -19,7 +19,7 @@ """This module facilitates the deprecation of functions.""" import warnings -from typing import Callable, TypeVar, Any +from typing import Any, Callable, TypeVar RT = TypeVar('RT') @@ -42,8 +42,8 @@ def warn_deprecate_obj(old: str, new: str, stacklevel: int = 3) -> None: def deprecate(func: Callable[..., RT], old: str, new: str) -> Callable[..., RT]: """Warn users invoking old to switch to the new function.""" - def f(*args: Any, **kwargs: Any) -> RT: + def wrapped(*args: Any, **kwargs: Any) -> RT: warn_deprecate_obj(old, new) return func(*args, **kwargs) - return f + return wrapped diff --git a/telegram/utils/helpers.py b/telegram/utils/helpers.py index 3d89d8279..a736a75fb 100644 --- a/telegram/utils/helpers.py +++ b/telegram/utils/helpers.py @@ -22,11 +22,19 @@ import datetime as dtm # dtm = "DateTime Module" import re import signal import time + from collections import defaultdict from html import escape from numbers import Number -import pytz +from typing import TYPE_CHECKING, Any, DefaultDict, Dict, Optional, Tuple, Union + +import pytz # pylint: disable=E0401 + +from telegram.utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import MessageEntity try: import ujson as json @@ -34,12 +42,6 @@ except ImportError: import json # type: ignore[no-redef] -from telegram.utils.types import JSONDict -from typing import Union, Any, Optional, Dict, DefaultDict, Tuple, TYPE_CHECKING - -if TYPE_CHECKING: - from telegram import MessageEntity - # From https://stackoverflow.com/questions/2549939/get-signal-names-from-numbers-in-python _signames = { v: k @@ -69,7 +71,7 @@ def escape_markdown(text: str, version: int = 1, entity_type: str = None) -> str if int(version) == 1: escape_chars = r'_*`[' elif int(version) == 2: - if entity_type == 'pre' or entity_type == 'code': + if entity_type in ['pre', 'code']: escape_chars = r'\`' elif entity_type == 'text_link': escape_chars = r'\)' @@ -93,7 +95,7 @@ def _datetime_to_float_timestamp(dt_obj: dtm.datetime) -> float: def to_float_timestamp( - t: Union[int, float, dtm.timedelta, dtm.datetime, dtm.time], + time_object: Union[int, float, dtm.timedelta, dtm.datetime, dtm.time], reference_timestamp: float = None, tzinfo: pytz.BaseTzInfo = None, ) -> float: @@ -105,7 +107,7 @@ def to_float_timestamp( to be in UTC, if ``bot`` is not passed or ``bot.defaults`` is :obj:`None`. Args: - t (int | float | datetime.timedelta | datetime.datetime | datetime.time): + time_object (int | float | datetime.timedelta | datetime.datetime | datetime.time): Time value to convert. The semantics of this parameter will depend on its type: * :obj:`int` or :obj:`float` will be interpreted as "seconds from ``reference_t``" @@ -143,23 +145,25 @@ def to_float_timestamp( if reference_timestamp is None: reference_timestamp = time.time() - elif isinstance(t, dtm.datetime): + elif isinstance(time_object, dtm.datetime): raise ValueError('t is an (absolute) datetime while reference_timestamp is not None') - if isinstance(t, dtm.timedelta): - return reference_timestamp + t.total_seconds() - elif isinstance(t, (int, float)): - return reference_timestamp + t + if isinstance(time_object, dtm.timedelta): + return reference_timestamp + time_object.total_seconds() + if isinstance(time_object, (int, float)): + return reference_timestamp + time_object if tzinfo is None: tzinfo = pytz.utc - if isinstance(t, dtm.time): - reference_dt = dtm.datetime.fromtimestamp(reference_timestamp, tz=t.tzinfo or tzinfo) + if isinstance(time_object, dtm.time): + reference_dt = dtm.datetime.fromtimestamp( + reference_timestamp, tz=time_object.tzinfo or tzinfo + ) reference_date = reference_dt.date() reference_time = reference_dt.timetz() - aware_datetime = dtm.datetime.combine(reference_date, t) + aware_datetime = dtm.datetime.combine(reference_date, time_object) if aware_datetime.tzinfo is None: aware_datetime = tzinfo.localize(aware_datetime) @@ -167,14 +171,14 @@ def to_float_timestamp( if reference_time > aware_datetime.timetz(): aware_datetime += dtm.timedelta(days=1) return _datetime_to_float_timestamp(aware_datetime) - elif isinstance(t, dtm.datetime): - if t.tzinfo is None: - t = tzinfo.localize(t) - return _datetime_to_float_timestamp(t) - elif isinstance(t, Number): - return reference_timestamp + t + if isinstance(time_object, dtm.datetime): + if time_object.tzinfo is None: + time_object = tzinfo.localize(time_object) + return _datetime_to_float_timestamp(time_object) + if isinstance(time_object, Number): + return reference_timestamp + time_object - raise TypeError('Unable to convert {} object to timestamp'.format(type(t).__name__)) + raise TypeError('Unable to convert {} object to timestamp'.format(type(time_object).__name__)) def to_timestamp( @@ -216,14 +220,13 @@ def from_timestamp( if tzinfo is not None: return dtm.datetime.fromtimestamp(unixtime, tz=tzinfo) - else: - return dtm.datetime.utcfromtimestamp(unixtime) + return dtm.datetime.utcfromtimestamp(unixtime) # -------- end -------- -def mention_html(user_id: int, name: str) -> Optional[str]: +def mention_html(user_id: Union[int, str], name: str) -> str: """ Args: user_id (:obj:`int`) The user's id which you want to mention. @@ -232,11 +235,10 @@ def mention_html(user_id: int, name: str) -> Optional[str]: Returns: :obj:`str`: The inline mention for the user as html. """ - if isinstance(user_id, int): - return u'{}'.format(user_id, escape(name)) + return u'{}'.format(user_id, escape(name)) -def mention_markdown(user_id: int, name: str, version: int = 1) -> Optional[str]: +def mention_markdown(user_id: Union[int, str], name: str, version: int = 1) -> str: """ Args: user_id (:obj:`int`) The user's id which you want to mention. @@ -247,8 +249,7 @@ def mention_markdown(user_id: int, name: str, version: int = 1) -> Optional[str] Returns: :obj:`str`: The inline mention for the user as markdown. """ - if isinstance(user_id, int): - return u'[{}](tg://user?id={})'.format(escape_markdown(name, version=version), user_id) + return u'[{}](tg://user?id={})'.format(escape_markdown(name, version=version), user_id) def effective_message_type(entity: 'MessageEntity') -> Optional[str]: @@ -265,8 +266,7 @@ def effective_message_type(entity: 'MessageEntity') -> Optional[str]: """ # Importing on file-level yields cyclic Import Errors - from telegram import Message - from telegram import Update + from telegram import Message, Update # pylint: disable=C0415 if isinstance(entity, Message): message = entity diff --git a/telegram/utils/promise.py b/telegram/utils/promise.py index 02905ef60..6142b9531 100644 --- a/telegram/utils/promise.py +++ b/telegram/utils/promise.py @@ -20,8 +20,9 @@ import logging from threading import Event -from telegram.utils.types import JSONDict, HandlerArg -from typing import Callable, List, Tuple, Optional, Union, TypeVar +from typing import Callable, List, Optional, Tuple, TypeVar, Union + +from telegram.utils.types import HandlerArg, JSONDict RT = TypeVar('RT') diff --git a/telegram/utils/request.py b/telegram/utils/request.py index ea45d2147..69a509e02 100644 --- a/telegram/utils/request.py +++ b/telegram/utils/request.py @@ -28,21 +28,23 @@ try: except ImportError: import json # type: ignore[no-redef] -import certifi +from typing import Any, Union + +import certifi # pylint: disable=E0401 try: import telegram.vendor.ptb_urllib3.urllib3 as urllib3 import telegram.vendor.ptb_urllib3.urllib3.contrib.appengine as appengine from telegram.vendor.ptb_urllib3.urllib3.connection import HTTPConnection - from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout from telegram.vendor.ptb_urllib3.urllib3.fields import RequestField + from telegram.vendor.ptb_urllib3.urllib3.util.timeout import Timeout except ImportError: # pragma: no cover try: import urllib3 # type: ignore[no-redef] import urllib3.contrib.appengine as appengine # type: ignore[no-redef] from urllib3.connection import HTTPConnection # type: ignore[no-redef] - from urllib3.util.timeout import Timeout # type: ignore[no-redef] from urllib3.fields import RequestField # type: ignore[no-redef] + from urllib3.util.timeout import Timeout # type: ignore[no-redef] warnings.warn( 'python-telegram-bot is using upstream urllib3. This is allowed but not ' @@ -55,24 +57,22 @@ except ImportError: # pragma: no cover ) raise - -from telegram import InputFile, TelegramError, InputMedia +# pylint: disable=C0412 +from telegram import InputFile, InputMedia, TelegramError from telegram.error import ( - Unauthorized, - NetworkError, - TimedOut, BadRequest, ChatMigrated, - RetryAfter, - InvalidToken, Conflict, + InvalidToken, + NetworkError, + RetryAfter, + TimedOut, + Unauthorized, ) - from telegram.utils.types import JSONDict -from typing import Any, Union -def _render_part(self: RequestField, name: str, value: str) -> str: +def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: disable=W0613 """ Monkey patch urllib3.urllib3.fields.RequestField to make it *not* support RFC2231 compliant Content-Disposition headers since telegram servers don't understand it. Instead just escape @@ -83,7 +83,7 @@ def _render_part(self: RequestField, name: str, value: str) -> str: return u'{}="{}"'.format(name, value) -RequestField._render_part = _render_part # type: ignore +RequestField._render_part = _render_part # type: ignore # pylint: disable=W0212 logging.getLogger('urllib3').setLevel(logging.WARNING) @@ -174,9 +174,10 @@ class Request: kwargs.update(urllib3_proxy_kwargs) if proxy_url.startswith('socks'): try: + # pylint: disable=C0415 from telegram.vendor.ptb_urllib3.urllib3.contrib.socks import SOCKSProxyManager - except ImportError: - raise RuntimeError('PySocks is missing') + except ImportError as exc: + raise RuntimeError('PySocks is missing') from exc self._con_pool = SOCKSProxyManager(proxy_url, **kwargs) else: mgr = urllib3.proxy_from_url(proxy_url, **kwargs) @@ -207,8 +208,8 @@ class Request: decoded_s = json_data.decode('utf-8', 'replace') try: data = json.loads(decoded_s) - except ValueError: - raise TelegramError('Invalid server response') + except ValueError as exc: + raise TelegramError('Invalid server response') from exc if not data.get('ok'): # pragma: no cover description = data.get('description') @@ -249,12 +250,12 @@ class Request: try: resp = self._con_pool.request(*args, **kwargs) - except urllib3.exceptions.TimeoutError: - raise TimedOut() + except urllib3.exceptions.TimeoutError as error: + raise TimedOut() from error 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('urllib3 HTTPError {}'.format(error)) + raise NetworkError('urllib3 HTTPError {}'.format(error)) from error if 200 <= resp.status <= 299: # 200-299 range are HTTP success statuses @@ -267,22 +268,20 @@ class Request: if resp.status in (401, 403): raise Unauthorized(message) - elif resp.status == 400: + if resp.status == 400: raise BadRequest(message) - elif resp.status == 404: + if resp.status == 404: raise InvalidToken() - elif resp.status == 409: + if resp.status == 409: raise Conflict(message) - elif resp.status == 413: + if resp.status == 413: raise NetworkError( 'File too large. Check telegram api limits ' 'https://core.telegram.org/bots/api#senddocument' ) - - elif resp.status == 502: + if resp.status == 502: raise NetworkError('Bad Gateway') - else: - raise NetworkError('{} ({})'.format(message, resp.status)) + raise NetworkError('{} ({})'.format(message, resp.status)) def post(self, url: str, data: JSONDict, timeout: float = None) -> Union[JSONDict, bool]: """Request an URL. @@ -309,6 +308,7 @@ class Request: # Are we uploading files? files = False + # pylint: disable=R1702 for key, val in data.copy().items(): if isinstance(val, InputFile): # Convert the InputFile to urllib3 field format @@ -327,14 +327,14 @@ class Request: else: # Attach and set val to attached name for all media = [] - for m in val: - media_dict = m.to_dict() + for med in val: + media_dict = med.to_dict() media.append(media_dict) - if isinstance(m.media, InputFile): - data[m.media.attach] = m.media.field_tuple + if isinstance(med.media, InputFile): + data[med.media.attach] = med.media.field_tuple # if the file has a thumb, we also need to attach it to the data if "thumb" in media_dict: - data[m.thumb.attach] = m.thumb.field_tuple + data[med.thumb.attach] = med.thumb.field_tuple data[key] = json.dumps(media) files = True diff --git a/telegram/utils/types.py b/telegram/utils/types.py index bbaecb1a2..eeb52327f 100644 --- a/telegram/utils/types.py +++ b/telegram/utils/types.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains custom typing aliases.""" -from typing import Union, Any, Dict, TYPE_CHECKING, IO, Tuple, Optional +from typing import IO, TYPE_CHECKING, Any, Dict, Optional, Tuple, Union if TYPE_CHECKING: from telegram import InputFile, Update diff --git a/telegram/utils/webhookhandler.py b/telegram/utils/webhookhandler.py index 0283cbd65..8db7e13cb 100644 --- a/telegram/utils/webhookhandler.py +++ b/telegram/utils/webhookhandler.py @@ -16,29 +16,32 @@ # # 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=E0401, C0114 + import asyncio +import logging import os import sys -import logging +from queue import Queue +from ssl import SSLContext +from threading import Event, Lock +from typing import TYPE_CHECKING, Any, Optional + +import tornado.web +from tornado import httputil +from tornado.httpserver import HTTPServer +from tornado.ioloop import IOLoop + from telegram import Update -from threading import Lock, Event +from telegram.utils.types import JSONDict + +if TYPE_CHECKING: + from telegram import Bot try: import ujson as json except ImportError: import json # type: ignore[no-redef] -from tornado.httpserver import HTTPServer -from tornado.ioloop import IOLoop -import tornado.web - -from ssl import SSLContext -from queue import Queue -from telegram.utils.types import JSONDict -from typing import Any, TYPE_CHECKING -from tornado import httputil - -if TYPE_CHECKING: - from telegram import Bot class WebhookServer: @@ -48,7 +51,7 @@ class WebhookServer: self.http_server = HTTPServer(webhook_app, ssl_options=ssl_ctx) self.listen = listen self.port = port - self.loop = None + self.loop: Optional[IOLoop] = None self.logger = logging.getLogger(__name__) self.is_running = False self.server_lock = Lock() @@ -65,7 +68,7 @@ class WebhookServer: if ready is not None: ready.set() - self.loop.start() # type: ignore + self.loop.start() self.logger.debug('Webhook Server stopped.') self.is_running = False @@ -74,10 +77,9 @@ class WebhookServer: if not self.is_running: self.logger.warning('Webhook Server already stopped.') return - else: - self.loop.add_callback(self.loop.stop) # type: ignore + self.loop.add_callback(self.loop.stop) # type: ignore - def handle_error(self, request: Any, client_address: str) -> None: + def handle_error(self, request: Any, client_address: str) -> None: # pylint: disable=W0613 """Handle an error gracefully.""" self.logger.debug( 'Exception happened during processing of request from %s', @@ -120,7 +122,8 @@ class WebhookServer: and hasattr(asyncio, 'WindowsProactorEventLoopPolicy') and ( isinstance( - asyncio.get_event_loop_policy(), asyncio.WindowsProactorEventLoopPolicy + asyncio.get_event_loop_policy(), + asyncio.WindowsProactorEventLoopPolicy, # pylint: disable=E1101 ) ) ): # pylint: disable=E1101 @@ -144,6 +147,7 @@ class WebhookAppClass(tornado.web.Application): # WebhookHandler, process webhook calls +# pylint: disable=W0223 class WebhookHandler(tornado.web.RequestHandler): SUPPORTED_METHODS = ["POST"] @@ -157,6 +161,7 @@ class WebhookHandler(tornado.web.RequestHandler): self.logger = logging.getLogger(__name__) def initialize(self, bot: 'Bot', update_queue: Queue) -> None: + # pylint: disable=W0201 self.bot = bot self.update_queue = update_queue @@ -169,10 +174,10 @@ class WebhookHandler(tornado.web.RequestHandler): json_string = self.request.body.decode() data = json.loads(json_string) self.set_status(200) - self.logger.debug('Webhook received data: ' + 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) self.update_queue.put(update) def _validate_post(self) -> None: @@ -196,6 +201,8 @@ class WebhookHandler(tornado.web.RequestHandler): """ super().write_error(status_code, **kwargs) self.logger.debug( - "{} - - {}".format(self.request.remote_ip, "Exception in WebhookHandler"), + "%s - - %s", + self.request.remote_ip, + "Exception in WebhookHandler", exc_info=kwargs['exc_info'], ) diff --git a/telegram/version.py b/telegram/version.py index aba09f9a5..429453d22 100644 --- a/telegram/version.py +++ b/telegram/version.py @@ -16,5 +16,6 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. +# pylint: disable=C0114 __version__ = '13.0' diff --git a/telegram/webhookinfo.py b/telegram/webhookinfo.py index 9b1dd8c3a..4c31ae0d3 100644 --- a/telegram/webhookinfo.py +++ b/telegram/webhookinfo.py @@ -18,9 +18,10 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram WebhookInfo.""" -from telegram import TelegramObject from typing import Any, List +from telegram import TelegramObject + class WebhookInfo(TelegramObject): """This object represents a Telegram WebhookInfo. @@ -60,7 +61,7 @@ class WebhookInfo(TelegramObject): """ def __init__( - self, + self, # pylint: disable=W0613 url: str, has_custom_certificate: bool, pending_update_count: int, diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index ae7986240..76c6fd9d4 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -568,12 +568,14 @@ class TestConversationHandler: assert len(handler.conversations) == 0 def test_end_on_first_message_async(self, dp, bot, user1): - start_end_async = lambda bot, update: dp.run_async( # noqa: E731 - self.start_end, bot, update - ) - handler = ConversationHandler( - entry_points=[CommandHandler('start', start_end_async)], states={}, fallbacks=[] + entry_points=[ + CommandHandler( + 'start', lambda bot, update: dp.run_async(self.start_end, bot, update) + ) + ], + states={}, + fallbacks=[], ) dp.add_handler(handler) @@ -647,12 +649,14 @@ class TestConversationHandler: assert len(handler.conversations) == 0 def test_none_on_first_message_async(self, dp, bot, user1): - start_none_async = lambda bot, update: dp.run_async( # noqa: E731 - self.start_none, bot, update - ) - handler = ConversationHandler( - entry_points=[CommandHandler('start', start_none_async)], states={}, fallbacks=[] + entry_points=[ + CommandHandler( + 'start', lambda bot, update: dp.run_async(self.start_none, bot, update) + ) + ], + states={}, + fallbacks=[], ) dp.add_handler(handler) @@ -815,7 +819,7 @@ class TestConversationHandler: assert handler.conversations.get((self.group.id, user1.id)) is None assert len(caplog.records) == 1 rec = caplog.records[-1] - assert rec.msg.startswith('DispatcherHandlerStop in TIMEOUT') + assert rec.getMessage().startswith('DispatcherHandlerStop in TIMEOUT') def test_conversation_handler_timeout_update_and_context(self, cdp, bot, user1): context = None diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index adb0e9a1b..f059191ff 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -136,7 +136,7 @@ class TestDispatcher: with caplog.at_level(logging.DEBUG): dp.add_error_handler(self.error_handler) assert len(caplog.records) == 1 - assert caplog.records[-1].msg.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: @@ -238,8 +238,10 @@ class TestDispatcher: dp.update_queue.put(self.message_update) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].msg.startswith( - 'DispatcherHandlerStop is not supported ' 'with async functions' + assert ( + caplog.records[-1] + .getMessage() + .startswith('DispatcherHandlerStop is not supported ' 'with async functions') ) def test_async_raises_exception(self, dp, caplog): @@ -253,7 +255,11 @@ class TestDispatcher: dp.update_queue.put(self.message_update) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].msg.startswith('A promise with deactivated error handling') + assert ( + caplog.records[-1] + .getMessage() + .startswith('A promise with deactivated error handling') + ) def test_add_async_handler(self, dp): dp.add_handler( @@ -277,7 +283,7 @@ class TestDispatcher: dp.run_async(func) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].msg.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): dp.add_handler(MessageHandler(Filters.all, self.callback_raise_error, run_async=True)) @@ -304,7 +310,7 @@ class TestDispatcher: dp.update_queue.put(self.message_update) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].msg.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) @@ -322,7 +328,7 @@ class TestDispatcher: dp.update_queue.put(self.message_update) sleep(0.1) assert len(caplog.records) == 1 - assert caplog.records[-1].msg.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) diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index 556b43eec..183d1a02b 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -306,9 +306,17 @@ class TestJobQueue: time_of_day = expected_reschedule_time.time().replace(tzinfo=timezone) day = now.day - expected_reschedule_time = timezone.normalize( - expected_reschedule_time + dtm.timedelta(calendar.monthrange(now.year, now.month)[1]) - ) + this_months_days = calendar.monthrange(now.year, now.month)[1] + if now.month == 12: + next_months_days = calendar.monthrange(now.year + 1, 1)[1] + else: + next_months_days = calendar.monthrange(now.year, now.month + 1)[1] + + expected_reschedule_time += dtm.timedelta(this_months_days) + if next_months_days < this_months_days: + expected_reschedule_time += dtm.timedelta(next_months_days) + + expected_reschedule_time = timezone.normalize(expected_reschedule_time) # Adjust the hour for the special case that between now and next month a DST switch happens expected_reschedule_time += dtm.timedelta( hours=time_of_day.hour - expected_reschedule_time.hour @@ -473,16 +481,16 @@ class TestJobQueue: sleep(0.1) assert len(caplog.records) == 1 rec = caplog.records[-1] - assert 'processing the job' in rec.msg - assert 'uncaught error was raised while handling' in rec.msg + 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.msg - assert 'uncaught error was raised while handling' in rec.msg + assert 'processing the job' in rec.getMessage() + assert 'uncaught error was raised while handling' in rec.getMessage() caplog.clear() # Remove handler @@ -494,11 +502,11 @@ class TestJobQueue: sleep(0.1) assert len(caplog.records) == 1 rec = caplog.records[-1] - assert 'No error handlers are registered' in rec.msg + 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.msg + assert 'No error handlers are registered' in rec.getMessage() diff --git a/tests/test_persistence.py b/tests/test_persistence.py index 4975af67f..009dacfd5 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -273,13 +273,13 @@ class TestBasePersistence: with caplog.at_level(logging.ERROR): dp.process_update(u) rec = caplog.records[-1] - assert rec.msg == 'No error handlers are registered, logging exception.' + assert rec.getMessage() == 'No error handlers are registered, logging exception.' assert rec.levelname == 'ERROR' rec = caplog.records[-2] - assert rec.msg == 'No error handlers are registered, logging exception.' + assert rec.getMessage() == 'No error handlers are registered, logging exception.' assert rec.levelname == 'ERROR' rec = caplog.records[-3] - assert rec.msg == 'No error handlers are registered, logging exception.' + assert rec.getMessage() == 'No error handlers are registered, logging exception.' assert rec.levelname == 'ERROR' m.from_user = user2 m.chat = chat1 @@ -387,10 +387,10 @@ class TestBasePersistence: sleep(0.1) rec = caplog.records[-1] - assert rec.msg == 'No error handlers are registered, logging exception.' + assert rec.getMessage() == 'No error handlers are registered, logging exception.' assert rec.levelname == 'ERROR' rec = caplog.records[-2] - assert rec.msg == 'No error handlers are registered, logging exception.' + assert rec.getMessage() == 'No error handlers are registered, logging exception.' assert rec.levelname == 'ERROR' m.from_user = user2 m.chat = chat1 diff --git a/tests/test_updater.py b/tests/test_updater.py index 138752663..3da71945d 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -512,11 +512,11 @@ class TestUpdater: updater.idle() rec = caplog.records[-2] - assert rec.msg.startswith('Received signal {}'.format(signal.SIGTERM)) + assert rec.getMessage().startswith('Received signal {}'.format(signal.SIGTERM)) assert rec.levelname == 'INFO' rec = caplog.records[-1] - assert rec.msg.startswith('Scheduler has been shut down') + assert rec.getMessage().startswith('Scheduler has been shut down') assert rec.levelname == 'INFO' # If we get this far, idle() ran through