diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst index a86878bec..e0923ed42 100644 --- a/.github/CONTRIBUTING.rst +++ b/.github/CONTRIBUTING.rst @@ -109,12 +109,6 @@ Here's how to make a one-off code change. - Before making a commit ensure that all automated tests still pass: - .. code-block:: - - $ make test - - If you don't have ``make``, do: - .. code-block:: $ pytest -v @@ -127,18 +121,18 @@ Here's how to make a one-off code change. prior to running the tests. - - To actually make the commit (this will trigger tests for yapf, lint and pep8 automatically): + - If you want run style & type checks before committing run + + .. code-block:: + + $ pre-commit run -a + + - To actually make the commit (this will trigger tests style & type checks automatically): .. code-block:: bash $ git add your-file-changed.py - - yapf may change code formatting, make sure to re-add them to your commit. - - .. code-block:: bash - - $ git commit -a -m "your-commit-message-here" - - Finally, push it to your GitHub fork, run: .. code-block:: bash @@ -196,7 +190,7 @@ Style commandments Assert comparison order ####################### -- assert statements should compare in **actual** == **expected** order. +Assert statements should compare in **actual** == **expected** order. For example (assuming ``test_call`` is the thing being tested): .. code-block:: python diff --git a/.github/workflows/pre-commit_dependencies_notifier.yml b/.github/workflows/pre-commit_dependencies_notifier.yml new file mode 100644 index 000000000..fa159e43e --- /dev/null +++ b/.github/workflows/pre-commit_dependencies_notifier.yml @@ -0,0 +1,17 @@ +name: Warning maintainers +on: + pull_request: + paths: + - requirements.txt + - requirements-dev.txt + - .pre-commit-config.yaml +jobs: + job: + runs-on: ubuntu-latest + name: about pre-commit and dependency change + steps: + - name: running the check + uses: Poolitzer/notifier-action@master + with: + notify-message: Hey! Looks like you edited the (dev) requirements or the pre-commit hooks. I'm just a friendly reminder to keep the pre-commit hook versions in sync with the dev requirements and the additional dependencies for the hooks in sync with the requirements :) + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f5a22a65d..1da327606 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ # Make sure that # * the revs specified here match requirements-dev.txt -# * the makefile checks the same files as pre-commit +# * the additional_dependencies here match requirements.txt repos: - repo: https://github.com/psf/black rev: 20.8b1 @@ -14,21 +14,43 @@ repos: hooks: - id: flake8 - repo: https://github.com/PyCQA/pylint - rev: pylint-2.6.0 + rev: pylint-2.7.2 hooks: - id: pylint files: ^(telegram|examples)/.*\.py$ args: - - --rcfile=setup.cfg + - --rcfile=setup.cfg + additional_dependencies: + - certifi + - tornado>=5.1 + - APScheduler==3.6.3 + - . # this basically does `pip install -e .` - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.800 + rev: v0.812 hooks: - id: mypy - files: ^(telegram|examples)/.*\.py$ + name: mypy-ptb + files: ^telegram/.*\.py$ + additional_dependencies: + - certifi + - tornado>=5.1 + - APScheduler==3.6.3 + - . # this basically does `pip install -e .` + - id: mypy + name: mypy-examples + files: ^examples/.*\.py$ + args: + - --no-strict-optional + - --follow-imports=silent + additional_dependencies: + - certifi + - tornado>=5.1 + - APScheduler==3.6.3 + - . # this basically does `pip install -e .` - repo: https://github.com/asottile/pyupgrade rev: v2.10.0 hooks: - id: pyupgrade files: ^(telegram|examples|tests)/.*\.py$ args: - - --py36-plus + - --py36-plus diff --git a/Makefile b/Makefile deleted file mode 100644 index 81f0dbf1c..000000000 --- a/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -.DEFAULT_GOAL := help -.PHONY: clean pep8 black lint test install - -PYLINT := pylint -PYTEST := pytest -PEP8 := flake8 -BLACK := black -MYPY := mypy -PIP := pip - -clean: - rm -fr build - rm -fr dist - find . -name '*.pyc' -exec rm -f {} \; - find . -name '*.pyo' -exec rm -f {} \; - find . -name '*~' -exec rm -f {} \; - find . -regex "./telegram[0-9]*.\(jpg\|mp3\|mp4\|ogg\|png\|webp\)" -exec rm {} \; - -pep8: - $(PEP8) telegram tests examples - -black: - $(BLACK) . - -lint: - $(PYLINT) --rcfile=setup.cfg telegram examples - -mypy: - $(MYPY) -p telegram - $(MYPY) examples - -test: - $(PYTEST) -v - -install: - $(PIP) install -r requirements.txt -r requirements-dev.txt - -help: - @echo "Available targets:" - @echo "- clean Clean up the source directory" - @echo "- pep8 Check style with flake8" - @echo "- lint Check style with pylint" - @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 "- PEP8 default: $(PEP8)" - @echo "- BLACK default: $(BLACK)" - @echo "- MYPY default: $(MYPY)" - @echo "- PIP default: $(PIP)" diff --git a/examples/conversationbot.py b/examples/conversationbot.py index 3ec2b9a22..9d6e1acf6 100644 --- a/examples/conversationbot.py +++ b/examples/conversationbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -37,7 +36,7 @@ logger = logging.getLogger(__name__) GENDER, PHOTO, LOCATION, BIO = range(4) -def start(update: Update, context: CallbackContext) -> int: +def start(update: Update, _: CallbackContext) -> int: reply_keyboard = [['Boy', 'Girl', 'Other']] update.message.reply_text( @@ -50,7 +49,7 @@ def start(update: Update, context: CallbackContext) -> int: return GENDER -def gender(update: Update, context: CallbackContext) -> int: +def gender(update: Update, _: CallbackContext) -> int: user = update.message.from_user logger.info("Gender of %s: %s", user.first_name, update.message.text) update.message.reply_text( @@ -62,52 +61,52 @@ def gender(update: Update, context: CallbackContext) -> int: return PHOTO -def photo(update: Update, context: CallbackContext) -> int: +def photo(update: Update, _: CallbackContext) -> int: user = update.message.from_user photo_file = update.message.photo[-1].get_file() photo_file.download('user_photo.jpg') logger.info("Photo of %s: %s", user.first_name, 'user_photo.jpg') update.message.reply_text( - 'Gorgeous! Now, send me your location please, ' 'or send /skip if you don\'t want to.' + 'Gorgeous! Now, send me your location please, or send /skip if you don\'t want to.' ) return LOCATION -def skip_photo(update: Update, context: CallbackContext) -> int: +def skip_photo(update: Update, _: CallbackContext) -> int: user = update.message.from_user logger.info("User %s did not send a photo.", user.first_name) update.message.reply_text( - 'I bet you look great! Now, send me your location please, ' 'or send /skip.' + 'I bet you look great! Now, send me your location please, or send /skip.' ) return LOCATION -def location(update: Update, context: CallbackContext) -> int: +def location(update: Update, _: CallbackContext) -> int: user = update.message.from_user user_location = update.message.location logger.info( "Location of %s: %f / %f", user.first_name, user_location.latitude, user_location.longitude ) update.message.reply_text( - 'Maybe I can visit you sometime! ' 'At last, tell me something about yourself.' + 'Maybe I can visit you sometime! At last, tell me something about yourself.' ) return BIO -def skip_location(update: Update, context: CallbackContext) -> int: +def skip_location(update: Update, _: CallbackContext) -> int: user = update.message.from_user logger.info("User %s did not send a location.", user.first_name) update.message.reply_text( - 'You seem a bit paranoid! ' 'At last, tell me something about yourself.' + 'You seem a bit paranoid! At last, tell me something about yourself.' ) return BIO -def bio(update: Update, context: CallbackContext) -> int: +def bio(update: Update, _: 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.') @@ -115,7 +114,7 @@ def bio(update: Update, context: CallbackContext) -> int: return ConversationHandler.END -def cancel(update: Update, context: CallbackContext) -> int: +def cancel(update: Update, _: CallbackContext) -> int: user = update.message.from_user logger.info("User %s canceled the conversation.", user.first_name) update.message.reply_text( diff --git a/examples/conversationbot2.py b/examples/conversationbot2.py index 26e4dfc1a..0397b6499 100644 --- a/examples/conversationbot2.py +++ b/examples/conversationbot2.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -54,7 +53,7 @@ def facts_to_str(user_data: Dict[str, str]) -> str: return "\n".join(facts).join(['\n', '\n']) -def start(update: Update, context: CallbackContext) -> int: +def start(update: Update, _: 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?", @@ -72,9 +71,9 @@ def regular_choice(update: Update, context: CallbackContext) -> int: return TYPING_REPLY -def custom_choice(update: Update, context: CallbackContext) -> int: +def custom_choice(update: Update, _: CallbackContext) -> int: update.message.reply_text( - 'Alright, please send me the category first, ' 'for example "Most impressive skill"' + 'Alright, please send me the category first, for example "Most impressive skill"' ) return TYPING_CHOICE diff --git a/examples/deeplinking.py b/examples/deeplinking.py index 233a85c1e..340b05122 100644 --- a/examples/deeplinking.py +++ b/examples/deeplinking.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """Bot that explains Telegram's "Deep Linking Parameters" functionality. @@ -79,7 +78,7 @@ def deep_linked_level_2(update: Update, context: CallbackContext) -> None: update.message.reply_text(text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True) -def deep_linked_level_3(update: Update, context: CallbackContext) -> None: +def deep_linked_level_3(update: Update, _: CallbackContext) -> None: """Reached through the USING_ENTITIES payload""" update.message.reply_text( "It is also possible to make deep-linking using InlineKeyboardButtons.", @@ -104,7 +103,7 @@ def deep_linked_level_4(update: Update, context: CallbackContext) -> None: ) -def main(): +def main() -> None: """Start the bot.""" # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/examples/echobot.py b/examples/echobot.py index b2f81a2d1..ec4e7914a 100644 --- a/examples/echobot.py +++ b/examples/echobot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -31,22 +30,22 @@ 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: Update, context: CallbackContext) -> None: +def start(update: Update, _: CallbackContext) -> None: """Send a message when the command /start is issued.""" update.message.reply_text('Hi!') -def help_command(update: Update, context: CallbackContext) -> None: +def help_command(update: Update, _: CallbackContext) -> None: """Send a message when the command /help is issued.""" update.message.reply_text('Help!') -def echo(update: Update, context: CallbackContext) -> None: +def echo(update: Update, _: CallbackContext) -> None: """Echo the user message.""" update.message.reply_text(update.message.text) -def main(): +def main() -> None: """Start the bot.""" # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/examples/errorhandlerbot.py b/examples/errorhandlerbot.py index 51419e50f..f11cee118 100644 --- a/examples/errorhandlerbot.py +++ b/examples/errorhandlerbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -28,7 +27,7 @@ BOT_TOKEN = "TOKEN" DEVELOPER_CHAT_ID = 123456789 -def error_handler(update: Update, context: CallbackContext) -> None: +def error_handler(update: object, 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) @@ -40,9 +39,10 @@ def error_handler(update: Update, context: CallbackContext) -> None: # Build the message with some markup and additional information about what happened. # You might need to add some logic to deal with messages longer than the 4096 character limit. + update_str = update.to_dict() if isinstance(update, Update) else str(update) message = ( f'An exception was raised while handling an update\n' - f'
update = {html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False))}' + f'update = {html.escape(json.dumps(update_str, indent=2, ensure_ascii=False))}' '\n\n' f'context.chat_data = {html.escape(str(context.chat_data))}\n\n' f'context.user_data = {html.escape(str(context.user_data))}\n\n' @@ -53,19 +53,19 @@ def error_handler(update: Update, context: CallbackContext) -> None: context.bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML) -def bad_command(update: Update, context: CallbackContext) -> None: +def bad_command(_: Update, context: CallbackContext) -> None: """Raise an error to trigger the error handler.""" - context.bot.wrong_method_name() + context.bot.wrong_method_name() # type: ignore[attr-defined] -def start(update: Update, context: CallbackContext) -> None: +def start(update: Update, _: CallbackContext) -> None: update.effective_message.reply_html( 'Use /bad_command to cause an error.\n' f'Your chat id is{update.effective_chat.id}
.' ) -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater(BOT_TOKEN) diff --git a/examples/inlinebot.py b/examples/inlinebot.py index 16b194c9e..9971eedd3 100644 --- a/examples/inlinebot.py +++ b/examples/inlinebot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -30,32 +29,34 @@ 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: Update, context: CallbackContext) -> None: +def start(update: Update, _: CallbackContext) -> None: """Send a message when the command /start is issued.""" update.message.reply_text('Hi!') -def help_command(update: Update, context: CallbackContext) -> None: +def help_command(update: Update, _: CallbackContext) -> None: """Send a message when the command /help is issued.""" update.message.reply_text('Help!') -def inlinequery(update: Update, context: CallbackContext) -> None: +def inlinequery(update: Update, _: CallbackContext) -> None: """Handle the inline query.""" query = update.inline_query.query results = [ InlineQueryResultArticle( - id=uuid4(), title="Caps", input_message_content=InputTextMessageContent(query.upper()) + id=str(uuid4()), + title="Caps", + input_message_content=InputTextMessageContent(query.upper()), ), InlineQueryResultArticle( - id=uuid4(), + id=str(uuid4()), title="Bold", input_message_content=InputTextMessageContent( f"*{escape_markdown(query)}*", parse_mode=ParseMode.MARKDOWN ), ), InlineQueryResultArticle( - id=uuid4(), + id=str(uuid4()), title="Italic", input_message_content=InputTextMessageContent( f"_{escape_markdown(query)}_", parse_mode=ParseMode.MARKDOWN diff --git a/examples/inlinekeyboard.py b/examples/inlinekeyboard.py index 07784be6e..d5f88c27a 100644 --- a/examples/inlinekeyboard.py +++ b/examples/inlinekeyboard.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -17,7 +16,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def start(update: Update, context: CallbackContext) -> None: +def start(update: Update, _: CallbackContext) -> None: keyboard = [ [ InlineKeyboardButton("Option 1", callback_data='1'), @@ -31,7 +30,7 @@ def start(update: Update, context: CallbackContext) -> None: update.message.reply_text('Please choose:', reply_markup=reply_markup) -def button(update: Update, context: CallbackContext) -> None: +def button(update: Update, _: CallbackContext) -> None: query = update.callback_query # CallbackQueries need to be answered, even if no notification to the user is needed @@ -41,11 +40,11 @@ def button(update: Update, context: CallbackContext) -> None: query.edit_message_text(text=f"Selected option: {query.data}") -def help_command(update: Update, context: CallbackContext) -> None: +def help_command(update: Update, _: CallbackContext) -> None: update.message.reply_text("Use /start to test this bot.") -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/examples/inlinekeyboard2.py b/examples/inlinekeyboard2.py index 069cec58f..800f04c61 100644 --- a/examples/inlinekeyboard2.py +++ b/examples/inlinekeyboard2.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """Simple inline keyboard bot with multiple CallbackQueryHandlers. @@ -38,7 +37,7 @@ FIRST, SECOND = range(2) ONE, TWO, THREE, FOUR = range(4) -def start(update: Update, context: CallbackContext) -> None: +def start(update: Update, _: CallbackContext) -> int: """Send message on `/start`.""" # Get user that sent /start and log his name user = update.message.from_user @@ -60,7 +59,7 @@ def start(update: Update, context: CallbackContext) -> None: return FIRST -def start_over(update: Update, context: CallbackContext) -> None: +def start_over(update: Update, _: CallbackContext) -> int: """Prompt same text & keyboard as `start` does but not as new message""" # Get CallbackQuery from Update query = update.callback_query @@ -81,7 +80,7 @@ def start_over(update: Update, context: CallbackContext) -> None: return FIRST -def one(update: Update, context: CallbackContext) -> None: +def one(update: Update, _: CallbackContext) -> int: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -98,7 +97,7 @@ def one(update: Update, context: CallbackContext) -> None: return FIRST -def two(update: Update, context: CallbackContext) -> None: +def two(update: Update, _: CallbackContext) -> int: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -115,7 +114,7 @@ def two(update: Update, context: CallbackContext) -> None: return FIRST -def three(update: Update, context: CallbackContext) -> None: +def three(update: Update, _: CallbackContext) -> int: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -133,7 +132,7 @@ def three(update: Update, context: CallbackContext) -> None: return SECOND -def four(update: Update, context: CallbackContext) -> None: +def four(update: Update, _: CallbackContext) -> int: """Show new choice of buttons""" query = update.callback_query query.answer() @@ -150,7 +149,7 @@ def four(update: Update, context: CallbackContext) -> None: return FIRST -def end(update: Update, context: CallbackContext) -> None: +def end(update: Update, _: CallbackContext) -> int: """Returns `ConversationHandler.END`, which tells the ConversationHandler that the conversation is over""" query = update.callback_query @@ -159,7 +158,7 @@ def end(update: Update, context: CallbackContext) -> None: return ConversationHandler.END -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/examples/nestedconversationbot.py b/examples/nestedconversationbot.py index 2b725d856..6c7b88074 100644 --- a/examples/nestedconversationbot.py +++ b/examples/nestedconversationbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -16,6 +15,7 @@ bot. """ import logging +from typing import Tuple, Dict, Any from telegram import InlineKeyboardMarkup, InlineKeyboardButton, Update from telegram.ext import ( @@ -64,14 +64,14 @@ END = ConversationHandler.END # Helper -def _name_switcher(level): +def _name_switcher(level: str) -> Tuple[str, str]: if level == PARENTS: return 'Father', 'Mother' return 'Brother', 'Sister' # Top level conversation callbacks -def start(update: Update, context: CallbackContext) -> None: +def start(update: Update, context: CallbackContext) -> str: """Select an action: Adding parent/child or show data.""" text = ( 'You may add a familiy member, yourself show the gathered data or end the ' @@ -103,7 +103,7 @@ def start(update: Update, context: CallbackContext) -> None: return SELECTING_ACTION -def adding_self(update: Update, context: CallbackContext) -> None: +def adding_self(update: Update, context: CallbackContext) -> str: """Add information about youself.""" context.user_data[CURRENT_LEVEL] = SELF text = 'Okay, please tell me about yourself.' @@ -116,10 +116,10 @@ def adding_self(update: Update, context: CallbackContext) -> None: return DESCRIBING_SELF -def show_data(update: Update, context: CallbackContext) -> None: +def show_data(update: Update, context: CallbackContext) -> str: """Pretty print gathered data.""" - def prettyprint(user_data, level): + def prettyprint(user_data: Dict[str, Any], level: str) -> str: people = user_data.get(level) if not people: return '\nNo information yet.' @@ -151,14 +151,14 @@ def show_data(update: Update, context: CallbackContext) -> None: return SHOWING -def stop(update: Update, context: CallbackContext) -> None: +def stop(update: Update, _: CallbackContext) -> int: """End Conversation by command.""" update.message.reply_text('Okay, bye.') return END -def end(update: Update, context: CallbackContext) -> None: +def end(update: Update, _: CallbackContext) -> int: """End conversation from InlineKeyboardButton.""" update.callback_query.answer() @@ -169,7 +169,7 @@ def end(update: Update, context: CallbackContext) -> None: # Second level conversation callbacks -def select_level(update: Update, context: CallbackContext) -> None: +def select_level(update: Update, _: CallbackContext) -> str: """Choose to add a parent or a child.""" text = 'You may add a parent or a child. Also you can show the gathered data or go back.' buttons = [ @@ -190,7 +190,7 @@ def select_level(update: Update, context: CallbackContext) -> None: return SELECTING_LEVEL -def select_gender(update: Update, context: CallbackContext) -> None: +def select_gender(update: Update, context: CallbackContext) -> str: """Choose to add mother or father.""" level = update.callback_query.data context.user_data[CURRENT_LEVEL] = level @@ -217,7 +217,7 @@ def select_gender(update: Update, context: CallbackContext) -> None: return SELECTING_GENDER -def end_second_level(update: Update, context: CallbackContext) -> None: +def end_second_level(update: Update, context: CallbackContext) -> int: """Return to top level conversation.""" context.user_data[START_OVER] = True start(update, context) @@ -226,7 +226,7 @@ def end_second_level(update: Update, context: CallbackContext) -> None: # Third level callbacks -def select_feature(update: Update, context: CallbackContext) -> None: +def select_feature(update: Update, context: CallbackContext) -> str: """Select a feature to update for the person.""" buttons = [ [ @@ -253,7 +253,7 @@ def select_feature(update: Update, context: CallbackContext) -> None: return SELECTING_FEATURE -def ask_for_input(update: Update, context: CallbackContext) -> None: +def ask_for_input(update: Update, context: CallbackContext) -> str: """Prompt user to input data for selected feature.""" context.user_data[CURRENT_FEATURE] = update.callback_query.data text = 'Okay, tell me.' @@ -264,7 +264,7 @@ def ask_for_input(update: Update, context: CallbackContext) -> None: return TYPING -def save_input(update: Update, context: CallbackContext) -> None: +def save_input(update: Update, context: CallbackContext) -> str: """Save input for feature and return to feature selection.""" user_data = context.user_data user_data[FEATURES][user_data[CURRENT_FEATURE]] = update.message.text @@ -274,7 +274,7 @@ def save_input(update: Update, context: CallbackContext) -> None: return select_feature(update, context) -def end_describing(update: Update, context: CallbackContext) -> None: +def end_describing(update: Update, context: CallbackContext) -> int: """End gathering of features and return to parent conversation.""" user_data = context.user_data level = user_data[CURRENT_LEVEL] @@ -292,14 +292,14 @@ def end_describing(update: Update, context: CallbackContext) -> None: return END -def stop_nested(update: Update, context: CallbackContext) -> None: +def stop_nested(update: Update, _: CallbackContext) -> str: """Completely end conversation from within nested conversation.""" update.message.reply_text('Okay, bye.') return STOPPING -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/examples/passportbot.py b/examples/passportbot.py index 3721329af..195e1cdc4 100644 --- a/examples/passportbot.py +++ b/examples/passportbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -24,7 +23,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def msg(update: Update, context: CallbackContext) -> None: +def msg(update: Update, _: CallbackContext) -> None: # If we received any passport data passport_data = update.message.passport_data if passport_data: @@ -65,19 +64,19 @@ def msg(update: Update, context: CallbackContext) -> None: actual_file.download() if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'): if data.front_side: - file = data.front_side.get_file() - print(data.type, file) - file.download() + front_file = data.front_side.get_file() + print(data.type, front_file) + front_file.download() if data.type in ('driver_license' and 'identity_card'): if data.reverse_side: - file = data.reverse_side.get_file() - print(data.type, file) - file.download() + reverse_file = data.reverse_side.get_file() + print(data.type, reverse_file) + reverse_file.download() if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'): if data.selfie: - file = data.selfie.get_file() - print(data.type, file) - file.download() + selfie_file = data.selfie.get_file() + print(data.type, selfie_file) + selfie_file.download() if data.type in ( 'passport', 'driver_license', @@ -96,7 +95,7 @@ def msg(update: Update, context: CallbackContext) -> None: actual_file.download() -def main(): +def main() -> None: """Start the bot.""" # Create the Updater and pass it your token and private key updater = Updater("TOKEN", private_key=open('private.key', 'rb').read()) diff --git a/examples/paymentbot.py b/examples/paymentbot.py index 22dec78e7..34f93df93 100644 --- a/examples/paymentbot.py +++ b/examples/paymentbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -28,7 +27,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def start_callback(update: Update, context: CallbackContext) -> None: +def start_callback(update: Update, _: 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) @@ -91,7 +90,7 @@ def start_without_shipping_callback(update: Update, context: CallbackContext) -> ) -def shipping_callback(update: Update, context: CallbackContext) -> None: +def shipping_callback(update: Update, _: CallbackContext) -> None: query = update.shipping_query # check the payload, is this from your bot? if query.invoice_payload != 'Custom-Payload': @@ -109,7 +108,7 @@ def shipping_callback(update: Update, context: CallbackContext) -> None: # after (optional) shipping, it's the pre-checkout -def precheckout_callback(update: Update, context: CallbackContext) -> None: +def precheckout_callback(update: Update, _: CallbackContext) -> None: query = update.pre_checkout_query # check the payload, is this from your bot? if query.invoice_payload != 'Custom-Payload': @@ -120,12 +119,12 @@ def precheckout_callback(update: Update, context: CallbackContext) -> None: # finally, after contacting the payment provider... -def successful_payment_callback(update: Update, context: CallbackContext) -> None: +def successful_payment_callback(update: Update, _: CallbackContext) -> None: # do something after successfully receiving payment? update.message.reply_text("Thank you for your payment!") -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/examples/persistentconversationbot.py b/examples/persistentconversationbot.py index 1c9129af8..686f737f0 100644 --- a/examples/persistentconversationbot.py +++ b/examples/persistentconversationbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116, C0103 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -16,6 +15,7 @@ bot. """ import logging +from typing import Dict from telegram import ReplyKeyboardMarkup, Update from telegram.ext import ( @@ -46,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(): @@ -55,7 +55,7 @@ def facts_to_str(user_data): return "\n".join(facts).join(['\n', '\n']) -def start(update: Update, context: CallbackContext) -> None: +def start(update: Update, context: CallbackContext) -> int: reply_text = "Hi! My name is Doctor Botter." if context.user_data: reply_text += ( @@ -72,7 +72,7 @@ def start(update: Update, context: CallbackContext) -> None: return CHOOSING -def regular_choice(update: Update, context: CallbackContext) -> None: +def regular_choice(update: Update, context: CallbackContext) -> int: text = update.message.text.lower() context.user_data['choice'] = text if context.user_data.get(text): @@ -86,7 +86,7 @@ def regular_choice(update: Update, context: CallbackContext) -> None: return TYPING_REPLY -def custom_choice(update: Update, context: CallbackContext) -> None: +def custom_choice(update: Update, _: CallbackContext) -> int: update.message.reply_text( 'Alright, please send me the category first, ' 'for example "Most impressive skill"' ) @@ -94,7 +94,7 @@ def custom_choice(update: Update, context: CallbackContext) -> None: return TYPING_CHOICE -def received_information(update: Update, context: CallbackContext) -> None: +def received_information(update: Update, context: CallbackContext) -> int: text = update.message.text category = context.user_data['choice'] context.user_data[category] = text.lower() @@ -117,7 +117,7 @@ def show_data(update: Update, context: CallbackContext) -> None: ) -def done(update: Update, context: CallbackContext) -> None: +def done(update: Update, context: CallbackContext) -> int: if 'choice' in context.user_data: del context.user_data['choice'] @@ -127,13 +127,13 @@ def done(update: Update, context: CallbackContext) -> None: return ConversationHandler.END -def main(): +def main() -> None: # Create the Updater and pass it your bot's token. - pp = PicklePersistence(filename='conversationbot') - updater = Updater("TOKEN", persistence=pp) + persistence = PicklePersistence(filename='conversationbot') + updater = Updater("TOKEN", persistence=persistence) # 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( @@ -162,10 +162,10 @@ def main(): persistent=True, ) - dp.add_handler(conv_handler) + dispatcher.add_handler(conv_handler) show_data_handler = CommandHandler('show_data', show_data) - dp.add_handler(show_data_handler) + dispatcher.add_handler(show_data_handler) # Start the Bot updater.start_polling() diff --git a/examples/pollbot.py b/examples/pollbot.py index d2f27123d..8e4a6d13d 100644 --- a/examples/pollbot.py +++ b/examples/pollbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -35,7 +34,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) -def start(update: Update, context: CallbackContext) -> None: +def start(update: Update, _: 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' @@ -121,7 +120,7 @@ def receive_quiz_answer(update: Update, context: CallbackContext) -> None: context.bot.stop_poll(quiz_data["chat_id"], quiz_data["message_id"]) -def preview(update: Update, context: CallbackContext) -> None: +def preview(update: Update, _: 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())]] @@ -132,7 +131,7 @@ def preview(update: Update, context: CallbackContext) -> None: ) -def receive_poll(update: Update, context: CallbackContext) -> None: +def receive_poll(update: Update, _: 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 @@ -146,7 +145,7 @@ def receive_poll(update: Update, context: CallbackContext) -> None: ) -def help_handler(update: Update, context: CallbackContext) -> None: +def help_handler(update: Update, _: CallbackContext) -> None: """Display a help message""" update.message.reply_text("Use /quiz, /poll or /preview to test this bot.") diff --git a/examples/rawapibot.py b/examples/rawapibot.py index 4d927b4b0..5aac795ba 100644 --- a/examples/rawapibot.py +++ b/examples/rawapibot.py @@ -39,7 +39,7 @@ def main() -> NoReturn: sleep(1) except Unauthorized: # The user has removed or blocked the bot. - UPDATE_ID += 1 # type: ignore[operator] + UPDATE_ID += 1 def echo(bot: telegram.Bot) -> None: diff --git a/examples/timerbot.py b/examples/timerbot.py index c63bfe215..045e5ff45 100644 --- a/examples/timerbot.py +++ b/examples/timerbot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -# pylint: disable=W0613, C0116 -# type: ignore[union-attr] +# pylint: disable=C0116 # This program is dedicated to the public domain under the CC0 license. """ @@ -34,17 +33,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: Update, context: CallbackContext) -> None: +def start(update: Update, _: CallbackContext) -> None: update.message.reply_text('Hi! Use /setto set a timer') -def alarm(context): +def alarm(context: CallbackContext) -> None: """Send the alarm message.""" job = context.job context.bot.send_message(job.context, text='Beep!') -def remove_job_if_exists(name, context): +def remove_job_if_exists(name: str, context: CallbackContext) -> bool: """Remove job with given name. Returns whether job was removed.""" current_jobs = context.job_queue.get_jobs_by_name(name) if not current_jobs: @@ -84,7 +83,7 @@ def unset(update: Update, context: CallbackContext) -> None: update.message.reply_text(text) -def main(): +def main() -> None: """Run bot.""" # Create the Updater and pass it your bot's token. updater = Updater("TOKEN") diff --git a/requirements-dev.txt b/requirements-dev.txt index 90af297b3..28a12e942 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,11 +2,11 @@ cryptography!=3.4,!=3.4.1,!=3.4.2,!=3.4.3 pre-commit -# Make sure that the versions specified here match the pre-commit settings +# 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.800 +pylint==2.7.2 +mypy==0.812 pyupgrade==2.10.0 pytest==6.2.2 diff --git a/requirements.txt b/requirements.txt index 69828590f..451938a5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +# Make sure to install those as additional_dependencies in the +# pre-commit hooks for pylint & mypy certifi # only telegram.ext: # Keep this line here; used in setup(-raw).py tornado>=5.1 diff --git a/setup.cfg b/setup.cfg index 240159cc4..dd6d88012 100644 --- a/setup.cfg +++ b/setup.cfg @@ -61,6 +61,10 @@ ignore_errors = True [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 +# type hinting for asyncio in webhookhandler is a bit tricky because it depends on the OS +[mypy-telegram.ext.utils.webhookhandler] +warn_unused_ignores = False + [mypy-urllib3.*] ignore_missing_imports = True diff --git a/telegram/__init__.py b/telegram/__init__.py index f1dc551c4..19f6386f9 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -40,8 +40,8 @@ from .files.videonote import VideoNote from .chataction import ChatAction from .dice import Dice from .userprofilephotos import UserProfilePhotos -from .keyboardbutton import KeyboardButton from .keyboardbuttonpolltype import KeyboardButtonPollType +from .keyboardbutton import KeyboardButton from .replymarkup import ReplyMarkup from .replykeyboardmarkup import ReplyKeyboardMarkup from .replykeyboardremove import ReplyKeyboardRemove diff --git a/telegram/__main__.py b/telegram/__main__.py index dbf4f28e8..c4a91cc74 100644 --- a/telegram/__main__.py +++ b/telegram/__main__.py @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=E0401, C0114 +# pylint: disable=C0114 import subprocess import sys from typing import Optional diff --git a/telegram/bot.py b/telegram/bot.py index b76e1b172..2e2f673a3 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# pylint: disable=E0611,E0213,E1102,C0103,E1101,R0913,R0904 +# pylint: disable=E0611,E0213,E1102,E1101,R0913,R0904 # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2021 @@ -17,7 +17,6 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=E0401 """This module contains an object that represents a Telegram Bot.""" import functools @@ -35,6 +34,7 @@ from typing import ( no_type_check, Dict, cast, + Sequence, ) try: @@ -324,7 +324,7 @@ class Bot(TelegramObject): return self._bot @property - def id(self) -> int: + def id(self) -> int: # pylint: disable=C0103 """:obj:`int`: Unique identifier for this bot.""" return self.bot.id @@ -1445,12 +1445,12 @@ class Bot(TelegramObject): 'allow_sending_without_reply': allow_sending_without_reply, } - for m in data['media']: - if m.parse_mode == DEFAULT_NONE: + for med in data['media']: + if med.parse_mode == DEFAULT_NONE: if self.defaults: - m.parse_mode = DefaultValue.get_value(self.defaults.parse_mode) + med.parse_mode = DefaultValue.get_value(self.defaults.parse_mode) else: - m.parse_mode = None + med.parse_mode = None if reply_to_message_id: data['reply_to_message_id'] = reply_to_message_id @@ -1990,7 +1990,7 @@ class Bot(TelegramObject): self, inline_query_id: str, results: Union[ - List['InlineQueryResult'], Callable[[int], Optional[List['InlineQueryResult']]] + Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] ], cache_time: int = 300, is_personal: bool = None, @@ -2103,7 +2103,7 @@ class Bot(TelegramObject): if callable(results): callable_output = results(current_offset_int) if not callable_output: - effective_results = [] + effective_results: Sequence['InlineQueryResult'] = [] else: effective_results = callable_output next_offset = str(current_offset_int + 1) @@ -3388,7 +3388,7 @@ class Bot(TelegramObject): ) @log - def answer_shipping_query( + def answer_shipping_query( # pylint: disable=C0103 self, shipping_query_id: str, ok: bool, @@ -3453,7 +3453,7 @@ class Bot(TelegramObject): return result # type: ignore[return-value] @log - def answer_pre_checkout_query( + def answer_pre_checkout_query( # pylint: disable=C0103 self, pre_checkout_query_id: str, ok: bool, diff --git a/telegram/chat.py b/telegram/chat.py index aa9cbc31d..b14cc5f29 100644 --- a/telegram/chat.py +++ b/telegram/chat.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# pylint: disable=C0103,W0622 +# pylint: disable=W0622 # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2021 @@ -165,7 +165,7 @@ class Chat(TelegramObject): **_kwargs: Any, ): # Required - self.id = int(id) + self.id = int(id) # pylint: disable=C0103 self.type = type # Optionals self.title = title diff --git a/telegram/ext/defaults.py b/telegram/ext/defaults.py index bac294b35..7e8e9fcdd 100644 --- a/telegram/ext/defaults.py +++ b/telegram/ext/defaults.py @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=R0201, E0401 +# pylint: disable=R0201 """This module contains the class Defaults, which allows to pass default values to Updater.""" from typing import NoReturn, Optional, Dict, Any diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index eaf1f100e..6b6d1c06b 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -497,7 +497,7 @@ class Filters: def filter(self, message: Message) -> bool: return bool( message.entities - and any([e.type == MessageEntity.BOT_COMMAND for e in message.entities]) + and any(e.type == MessageEntity.BOT_COMMAND for e in message.entities) ) def __call__( # type: ignore[override] @@ -1896,7 +1896,7 @@ officedocument.wordprocessingml.document")``. """""" # remove method from docs return bool( message.from_user.language_code - and any([message.from_user.language_code.startswith(x) for x in self.lang]) + and any(message.from_user.language_code.startswith(x) for x in self.lang) ) class _UpdateType(UpdateFilter): diff --git a/telegram/ext/jobqueue.py b/telegram/ext/jobqueue.py index b637a76ea..240c45d4f 100644 --- a/telegram/ext/jobqueue.py +++ b/telegram/ext/jobqueue.py @@ -16,7 +16,6 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=E0401 """This module contains the classes JobQueue and Job.""" import datetime diff --git a/telegram/ext/utils/webhookhandler.py b/telegram/ext/utils/webhookhandler.py index 3c29317e6..8419e141f 100644 --- a/telegram/ext/utils/webhookhandler.py +++ b/telegram/ext/utils/webhookhandler.py @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=E0401, C0114 +# pylint: disable=C0114 import asyncio import logging @@ -95,7 +95,7 @@ class WebhookServer: not force_event_loop and os.name == 'nt' and sys.version_info >= (3, 8) - and isinstance(loop, asyncio.ProactorEventLoop) + and isinstance(loop, asyncio.ProactorEventLoop) # type: ignore[attr-defined] ): raise TypeError( '`ProactorEventLoop` is incompatible with ' @@ -123,7 +123,7 @@ class WebhookServer: and ( isinstance( asyncio.get_event_loop_policy(), - asyncio.WindowsProactorEventLoopPolicy, # pylint: disable=E1101 + asyncio.WindowsProactorEventLoopPolicy, # type: ignore # pylint: disable ) ) ): # pylint: disable=E1101 @@ -140,7 +140,7 @@ class WebhookAppClass(tornado.web.Application): def __init__(self, webhook_path: str, bot: 'Bot', update_queue: Queue): self.shared_objects = {"bot": bot, "update_queue": update_queue} handlers = [(rf"{webhook_path}/?", WebhookHandler, self.shared_objects)] # noqa - tornado.web.Application.__init__(self, handlers) + tornado.web.Application.__init__(self, handlers) # type: ignore def log_request(self, handler: tornado.web.RequestHandler) -> None: pass @@ -149,7 +149,7 @@ class WebhookAppClass(tornado.web.Application): # WebhookHandler, process webhook calls # pylint: disable=W0223 class WebhookHandler(tornado.web.RequestHandler): - SUPPORTED_METHODS = ["POST"] + SUPPORTED_METHODS = ["POST"] # type: ignore def __init__( self, diff --git a/telegram/inline/inlinequery.py b/telegram/inline/inlinequery.py index 84129949f..727fa5a49 100644 --- a/telegram/inline/inlinequery.py +++ b/telegram/inline/inlinequery.py @@ -19,7 +19,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram InlineQuery.""" -from typing import TYPE_CHECKING, Any, Optional, List, Union, Callable, ClassVar +from typing import TYPE_CHECKING, Any, Optional, Union, Callable, ClassVar, Sequence from telegram import Location, TelegramObject, User, constants from telegram.utils.helpers import DEFAULT_NONE @@ -97,7 +97,7 @@ class InlineQuery(TelegramObject): def answer( self, results: Union[ - List['InlineQueryResult'], Callable[[int], Optional[List['InlineQueryResult']]] + Sequence['InlineQueryResult'], Callable[[int], Optional[Sequence['InlineQueryResult']]] ], cache_time: int = 300, is_personal: bool = None, diff --git a/telegram/keyboardbutton.py b/telegram/keyboardbutton.py index a69328270..0632bb257 100644 --- a/telegram/keyboardbutton.py +++ b/telegram/keyboardbutton.py @@ -20,7 +20,7 @@ from typing import Any -from telegram import TelegramObject +from telegram import TelegramObject, KeyboardButtonPollType class KeyboardButton(TelegramObject): @@ -63,7 +63,7 @@ class KeyboardButton(TelegramObject): text: str, request_contact: bool = None, request_location: bool = None, - request_poll: bool = None, + request_poll: KeyboardButtonPollType = None, **_kwargs: Any, ): # Required diff --git a/telegram/passport/credentials.py b/telegram/passport/credentials.py index 8f7944270..bc5026c3a 100644 --- a/telegram/passport/credentials.py +++ b/telegram/passport/credentials.py @@ -16,7 +16,7 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -# pylint: disable=C0114, E0401, W0622 +# pylint: disable=C0114, W0622 try: import ujson as json except ImportError: diff --git a/telegram/replykeyboardmarkup.py b/telegram/replykeyboardmarkup.py index 1368fed09..95131d598 100644 --- a/telegram/replykeyboardmarkup.py +++ b/telegram/replykeyboardmarkup.py @@ -18,7 +18,7 @@ # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram ReplyKeyboardMarkup.""" -from typing import Any, List, Union +from typing import Any, List, Union, Sequence from telegram import KeyboardButton, ReplyMarkup from telegram.utils.types import JSONDict @@ -67,7 +67,7 @@ class ReplyKeyboardMarkup(ReplyMarkup): def __init__( self, - keyboard: List[List[Union[str, KeyboardButton]]], + keyboard: Sequence[Sequence[Union[str, KeyboardButton]]], resize_keyboard: bool = False, one_time_keyboard: bool = False, selective: bool = False, diff --git a/telegram/user.py b/telegram/user.py index 1db9d7a09..6faef9dce 100644 --- a/telegram/user.py +++ b/telegram/user.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# pylint: disable=C0103,W0622 +# pylint: disable=W0622 # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2021 @@ -111,7 +111,7 @@ class User(TelegramObject): **_kwargs: Any, ): # Required - self.id = int(id) + self.id = int(id) # pylint: disable=C0103 self.first_name = first_name self.is_bot = is_bot # Optionals diff --git a/telegram/utils/helpers.py b/telegram/utils/helpers.py index 6ee677946..19ee462f1 100644 --- a/telegram/utils/helpers.py +++ b/telegram/utils/helpers.py @@ -51,7 +51,7 @@ if TYPE_CHECKING: # in PTB-Raw we don't have pytz, so we make a little workaround here DTM_UTC = dtm.timezone.utc try: - import pytz # pylint: disable=E0401 + import pytz UTC = pytz.utc except ImportError: diff --git a/telegram/utils/request.py b/telegram/utils/request.py index cd1efcb06..9aea76ef3 100644 --- a/telegram/utils/request.py +++ b/telegram/utils/request.py @@ -30,7 +30,7 @@ except ImportError: from typing import Any, Union -import certifi # pylint: disable=E0401 +import certifi try: import telegram.vendor.ptb_urllib3.urllib3 as urllib3