From 7152b5aaf96425007194d8104bc7ece797feafeb Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <6d0847b9@opayq.com> Date: Wed, 16 Oct 2019 21:50:38 +0300 Subject: [PATCH 01/16] Fix CONTRIBUTING.rst not to install requirements as root (#1558) --- .github/CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst index e6c748b84..05bd22e02 100644 --- a/.github/CONTRIBUTING.rst +++ b/.github/CONTRIBUTING.rst @@ -25,7 +25,7 @@ Setting things up .. code-block:: bash - $ sudo pip install -r requirements.txt -r requirements-dev.txt + $ pip install -r requirements.txt -r requirements-dev.txt 5. Install pre-commit hooks: From 3d8771bbdf1b98b192509cce26101e15e5627e40 Mon Sep 17 00:00:00 2001 From: Lorenzo Rossi Date: Thu, 17 Oct 2019 00:03:53 +0200 Subject: [PATCH 02/16] :twisted_rightwards_arrows: Add mutex protection on ConversationHandler (#1533) * Add mutex protection on ConversationHandler * Remove timeout job before child update * Make locks private * Add conversation timeout conflict test --- telegram/ext/conversationhandler.py | 73 +++++++++++++++++++---------- tests/test_conversationhandler.py | 47 +++++++++++++++++++ 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index a53f4e81c..67da65fe4 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -20,6 +20,7 @@ import logging import warnings +from threading import Lock from telegram import Update from telegram.ext import (Handler, CallbackQueryHandler, InlineQueryHandler, @@ -184,7 +185,9 @@ class ConversationHandler(Handler): self.map_to_parent = map_to_parent self.timeout_jobs = dict() + self._timeout_jobs_lock = Lock() self.conversations = dict() + self._conversations_lock = Lock() self.logger = logging.getLogger(__name__) @@ -262,7 +265,8 @@ class ConversationHandler(Handler): return None key = self._get_key(update) - state = self.conversations.get(key) + with self._conversations_lock: + state = self.conversations.get(key) # Resolve promises if isinstance(state, tuple) and len(state) == 2 and isinstance(state[1], Promise): @@ -281,7 +285,8 @@ class ConversationHandler(Handler): if res is None and old_state is None: res = self.END self.update_state(res, key) - state = self.conversations.get(key) + with self._conversations_lock: + state = self.conversations.get(key) else: handlers = self.states.get(self.WAITING, []) for handler in handlers: @@ -340,15 +345,22 @@ class ConversationHandler(Handler): """ conversation_key, handler, check_result = check_result - new_state = handler.handle_update(update, dispatcher, check_result, context) - timeout_job = self.timeout_jobs.pop(conversation_key, None) - if timeout_job is not None: - timeout_job.schedule_removal() - if self.conversation_timeout and new_state != self.END: - self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once( - self._trigger_timeout, self.conversation_timeout, - context=_ConversationTimeoutContext(conversation_key, update, dispatcher)) + with self._timeout_jobs_lock: + # Remove the old timeout job (if present) + timeout_job = self.timeout_jobs.pop(conversation_key, None) + + if timeout_job is not None: + timeout_job.schedule_removal() + + new_state = handler.handle_update(update, dispatcher, check_result, context) + + with self._timeout_jobs_lock: + if self.conversation_timeout and new_state != self.END: + # Add the new timeout job + self.timeout_jobs[conversation_key] = dispatcher.job_queue.run_once( + self._trigger_timeout, self.conversation_timeout, + context=_ConversationTimeoutContext(conversation_key, update, dispatcher)) if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent: self.update_state(self.END, conversation_key) @@ -358,33 +370,42 @@ class ConversationHandler(Handler): def update_state(self, new_state, key): if new_state == self.END: - if key in self.conversations: - # If there is no key in conversations, nothing is done. - del self.conversations[key] - if self.persistent: - self.persistence.update_conversation(self.name, key, None) + with self._conversations_lock: + if key in self.conversations: + # If there is no key in conversations, nothing is done. + del self.conversations[key] + if self.persistent: + self.persistence.update_conversation(self.name, key, None) elif isinstance(new_state, Promise): - self.conversations[key] = (self.conversations.get(key), new_state) - if self.persistent: - self.persistence.update_conversation(self.name, key, - (self.conversations.get(key), new_state)) + with self._conversations_lock: + self.conversations[key] = (self.conversations.get(key), new_state) + if self.persistent: + self.persistence.update_conversation(self.name, key, + (self.conversations.get(key), new_state)) elif new_state is not None: - self.conversations[key] = new_state - if self.persistent: - self.persistence.update_conversation(self.name, key, new_state) + with self._conversations_lock: + self.conversations[key] = new_state + if self.persistent: + self.persistence.update_conversation(self.name, key, new_state) def _trigger_timeout(self, context, job=None): self.logger.debug('conversation timeout was triggered!') # Backward compatibility with bots that do not use CallbackContext if isinstance(context, CallbackContext): - context = context.job.context - else: - context = job.context + job = context.job + + context = job.context + + with self._timeout_jobs_lock: + found_job = self.timeout_jobs[context.conversation_key] + if found_job is not job: + # The timeout has been canceled in handle_update + return + del self.timeout_jobs[context.conversation_key] - del self.timeout_jobs[context.conversation_key] handlers = self.states.get(self.TIMEOUT, []) for handler in handlers: check = handler.check_update(context.update) diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py index 269cb895d..d82d10cbe 100644 --- a/tests/test_conversationhandler.py +++ b/tests/test_conversationhandler.py @@ -613,6 +613,53 @@ class TestConversationHandler(object): assert handler.conversations.get((self.group.id, user1.id)) is None assert not self.is_timeout + def test_conversation_timeout_cancel_conflict(self, dp, bot, user1): + # Start state machine, wait half the timeout, + # then call a callback that takes more than the timeout + # t=0 /start (timeout=.5) + # t=.25 /slowbrew (sleep .5) + # | t=.5 original timeout (should not execute) + # | t=.75 /slowbrew returns (timeout=1.25) + # t=1.25 timeout + + def slowbrew(_bot, update): + sleep(0.25) + # Let's give to the original timeout a chance to execute + dp.job_queue.tick() + sleep(0.25) + # By returning None we do not override the conversation state so + # we can see if the timeout has been executed + + states = self.states + states[self.THIRSTY].append(CommandHandler('slowbrew', slowbrew)) + states.update({ConversationHandler.TIMEOUT: [ + MessageHandler(None, self.passout2) + ]}) + + handler = ConversationHandler(entry_points=self.entry_points, states=states, + fallbacks=self.fallbacks, conversation_timeout=0.5) + dp.add_handler(handler) + + # CommandHandler timeout + message = Message(0, user1, None, self.group, text='/start', + entities=[MessageEntity(type=MessageEntity.BOT_COMMAND, offset=0, + length=len('/start'))], + bot=bot) + dp.process_update(Update(update_id=0, message=message)) + sleep(0.25) + dp.job_queue.tick() + message.text = '/slowbrew' + message.entities[0].length = len('/slowbrew') + dp.process_update(Update(update_id=0, message=message)) + dp.job_queue.tick() + assert handler.conversations.get((self.group.id, user1.id)) is not None + assert not self.is_timeout + + sleep(0.5) + dp.job_queue.tick() + assert handler.conversations.get((self.group.id, user1.id)) is None + assert self.is_timeout + def test_per_message_warning_is_only_shown_once(self, recwarn): ConversationHandler( entry_points=self.entry_points, From 88eccc6608d141004d231b38df76492e1c5f5b93 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <6d0847b9@opayq.com> Date: Thu, 17 Oct 2019 23:24:44 +0300 Subject: [PATCH 03/16] Add MAX_PHOTOSIZE_UPLOAD size limit constant (#1560) * Add MAX_PHOTOSIZE_UPLOAD size limit constant * Add the other source of the constants' values --- telegram/constants.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/telegram/constants.py b/telegram/constants.py index 9bb20940e..ba5af0fc3 100644 --- a/telegram/constants.py +++ b/telegram/constants.py @@ -17,7 +17,8 @@ """Constants in the Telegram network. The following constants were extracted from the -`Telegram Bots FAQ `_. +`Telegram Bots FAQ `_ and +`Telegram Bots API `_. Attributes: MAX_MESSAGE_LENGTH (:obj:`int`): 4096 @@ -25,6 +26,7 @@ Attributes: SUPPORTED_WEBHOOK_PORTS (List[:obj:`int`]): [443, 80, 88, 8443] MAX_FILESIZE_DOWNLOAD (:obj:`int`): In bytes (20MB) MAX_FILESIZE_UPLOAD (:obj:`int`): In bytes (50MB) + MAX_PHOTOSIZE_UPLOAD (:obj:`int`): In bytes (10MB) MAX_MESSAGES_PER_SECOND_PER_CHAT (:obj:`int`): `1`. Telegram may allow short bursts that go over this limit, but eventually you'll begin receiving 429 errors. MAX_MESSAGES_PER_SECOND (:obj:`int`): 30 @@ -47,6 +49,7 @@ MAX_CAPTION_LENGTH = 1024 SUPPORTED_WEBHOOK_PORTS = [443, 80, 88, 8443] MAX_FILESIZE_DOWNLOAD = int(20E6) # (20MB) MAX_FILESIZE_UPLOAD = int(50E6) # (50MB) +MAX_PHOTOSIZE_UPLOAD = int(10E6) # (10MB) MAX_MESSAGES_PER_SECOND_PER_CHAT = 1 MAX_MESSAGES_PER_SECOND = 30 MAX_MESSAGES_PER_MINUTE_PER_GROUP = 20 From b0aef0c7189b44cde28e52d41cb469f71f4b077a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannes=20H=C3=B6ke?= Date: Mon, 21 Oct 2019 23:51:58 +0200 Subject: [PATCH 04/16] :twisted_rightwards_arrows: Update issue templates (#1569) * :pencil: Update issue templates * :ok_hand: Update question template * :fire: Delete old issue template --- .../bug_report.md} | 13 ++++++--- .github/ISSUE_TEMPLATE/feature_request.md | 24 +++++++++++++++++ .github/ISSUE_TEMPLATE/question.md | 27 +++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) rename .github/{ISSUE_TEMPLATE.md => ISSUE_TEMPLATE/bug_report.md} (71%) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug_report.md similarity index 71% rename from .github/ISSUE_TEMPLATE.md rename to .github/ISSUE_TEMPLATE/bug_report.md index 8945f449e..1b32e8050 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,9 +1,16 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: 'bug :bug:' +assignees: '' + +--- + + +### Issue I am facing +Please describe the issue here in as much detail as possible + +### Traceback to the issue +``` +put it here +``` + +### Related part of your code +```python +put it here +``` From 6fddb49af583d7e5886a9f89d2aee9953b98b69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannes=20H=C3=B6ke?= Date: Tue, 22 Oct 2019 00:04:31 +0200 Subject: [PATCH 05/16] :pencil: Update section "Getting help" --- README.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 812f3686a..4c6953bd6 100644 --- a/README.rst +++ b/README.rst @@ -192,11 +192,12 @@ You can get help in several ways: 1. We have a vibrant community of developers helping each other in our `Telegram group `_. Join us! -2. Our `Wiki pages `_ offer a growing amount of resources. +2. Report bugs, request new features or ask questions by `creating an issue `_. -3. You can ask for help on Stack Overflow using the `python-telegram-bot tag `_. +3. Our `Wiki pages `_ offer a growing amount of resources. + +4. You can even ask for help on Stack Overflow using the `python-telegram-bot tag `_. -4. As last resort, the developers are ready to help you with `serious issues `_. ============ From 8cdb20a85abb19ec97b432eb3ce6c1aee1036d34 Mon Sep 17 00:00:00 2001 From: Poolitzer <25934244+Poolitzer@users.noreply.github.com> Date: Sat, 26 Oct 2019 14:51:29 -0700 Subject: [PATCH 06/16] updating example to V12 (#1579) --- examples/passportbot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/passportbot.py b/examples/passportbot.py index ebcfc6154..5d57f731e 100644 --- a/examples/passportbot.py +++ b/examples/passportbot.py @@ -21,7 +21,7 @@ logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s logger = logging.getLogger(__name__) -def msg(bot, update): +def msg(update, context): # If we received any passport data passport_data = update.message.passport_data if passport_data: @@ -77,9 +77,9 @@ def msg(bot, update): actual_file.download() -def error(bot, update, error): +def error(update, context): """Log Errors caused by Updates.""" - logger.warning('Update "%s" caused error "%s"', update, error) + logger.warning('Update "%s" caused error "%s"', update, context.error) def main(): From 93449443b2293472c005675d22a95f695c19210a Mon Sep 17 00:00:00 2001 From: Bibo-Joshi Date: Sun, 27 Oct 2019 00:00:27 +0200 Subject: [PATCH 07/16] Add *args, **kwargs to Message.forward() (#1574) --- telegram/message.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/telegram/message.py b/telegram/message.py index 913705c74..9e4d2f8ed 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -742,13 +742,14 @@ class Message(TelegramObject): self._quote(kwargs) return self.bot.send_poll(self.chat_id, *args, **kwargs) - def forward(self, chat_id, disable_notification=False): + def forward(self, chat_id, *args, **kwargs): """Shortcut for:: bot.forward_message(chat_id=chat_id, from_chat_id=update.message.chat_id, - disable_notification=disable_notification, - message_id=update.message.message_id) + message_id=update.message.message_id, + *args, + **kwargs) Returns: :class:`telegram.Message`: On success, instance representing the message forwarded. @@ -757,8 +758,9 @@ class Message(TelegramObject): return self.bot.forward_message( chat_id=chat_id, from_chat_id=self.chat_id, - disable_notification=disable_notification, - message_id=self.message_id) + message_id=self.message_id, + *args, + **kwargs) def edit_text(self, *args, **kwargs): """Shortcut for:: From bbcff96804bc8dc40ad948dabbce264a7f83538c Mon Sep 17 00:00:00 2001 From: Bibo-Joshi Date: Sun, 27 Oct 2019 00:04:48 +0200 Subject: [PATCH 08/16] Doc fixes (#1572) Fixes #1576 --- telegram/ext/conversationhandler.py | 3 +-- telegram/ext/jobqueue.py | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/telegram/ext/conversationhandler.py b/telegram/ext/conversationhandler.py index 67da65fe4..a31809a70 100644 --- a/telegram/ext/conversationhandler.py +++ b/telegram/ext/conversationhandler.py @@ -38,8 +38,7 @@ class _ConversationTimeoutContext(object): class ConversationHandler(Handler): """ A handler to hold a conversation with a single user by managing four collections of other - handlers. Note that neither posts in Telegram Channels, nor group interactions with multiple - users are managed by instances of this class. + handlers. The first collection, a ``list`` named :attr:`entry_points`, is used to initiate the conversation, for example with a :class:`telegram.ext.CommandHandler` or diff --git a/telegram/ext/jobqueue.py b/telegram/ext/jobqueue.py index 1f513872b..d3e0edced 100644 --- a/telegram/ext/jobqueue.py +++ b/telegram/ext/jobqueue.py @@ -42,7 +42,8 @@ class JobQueue(object): Attributes: _queue (:obj:`PriorityQueue`): The queue that holds the Jobs. bot (:class:`telegram.Bot`): The bot instance that should be passed to the jobs. - DEPRECATED: Use set_dispatcher instead. + DEPRECATED: Use :attr:`set_dispatcher` instead. + """ def __init__(self, bot=None): @@ -68,6 +69,13 @@ class JobQueue(object): self._running = False def set_dispatcher(self, dispatcher): + """Set the dispatcher to be used by this JobQueue. Use this instead of passing a + :class:`telegram.Bot` to the JobQueue, which is deprecated. + + Args: + dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher. + + """ self._dispatcher = dispatcher def _put(self, job, next_t=None, last_t=None): From 34bdbc632ad9f2b6b9548288436151a0412b1164 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi Date: Sun, 27 Oct 2019 00:12:54 +0200 Subject: [PATCH 09/16] Add msg_in filter (new) (#1570) Closes #1144 --- AUTHORS.rst | 1 + telegram/ext/filters.py | 33 +++++++++++++++++++++++++++++++++ tests/test_filters.py | 10 ++++++++++ 3 files changed, 44 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 63cd5e0bd..a72ca99f9 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -26,6 +26,7 @@ The following wonderful people contributed directly or indirectly to this projec - `d-qoi `_ - `daimajia `_ - `Daniel Reed `_ +- `Dmitry Grigoryev `_ - `Ehsan Online `_ - `Eli Gao `_ - `Emilio Molinari `_ diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py index ae5df4e68..40c9abdf9 100644 --- a/telegram/ext/filters.py +++ b/telegram/ext/filters.py @@ -909,6 +909,39 @@ officedocument.wordprocessingml.document")``- return message.from_user.language_code and any( [message.from_user.language_code.startswith(x) for x in self.lang]) + class msg_in(BaseFilter): + """Filters messages to only allow those whose text/caption appears in a given list. + + Examples: + A simple usecase is to allow only messages that were send by a custom + :class:`telegram.ReplyKeyboardMarkup`:: + + buttons = ['Start', 'Settings', 'Back'] + markup = ReplyKeyboardMarkup.from_column(buttons) + ... + MessageHandler(Filters.msg_in(buttons), callback_method) + + Args: + list_ (List[:obj:`str`]): Which messages to allow through. Only exact matches + are allowed. + caption (:obj:`bool`): Optional. Whether the caption should be used instead of text. + Default is ``False``. + + """ + + def __init__(self, list_, caption=False): + self.list_ = list_ + self.caption = caption + self.name = 'Filters.msg_in({!r}, caption={!r})'.format(self.list_, self.caption) + + def filter(self, message): + if self.caption: + txt = message.caption + else: + txt = message.text + + return txt in self.list_ + class _UpdateType(BaseFilter): update_filter = True diff --git a/tests/test_filters.py b/tests/test_filters.py index a91b10f38..17f9e9714 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -604,6 +604,16 @@ class TestFilters(object): update.message.from_user.language_code = 'da' assert f(update) + def test_msg_in_filter(self, update): + update.message.text = 'test' + update.message.caption = 'caption' + + assert Filters.msg_in(['test'])(update) + assert Filters.msg_in(['caption'], caption=True)(update) + + assert not Filters.msg_in(['test'], caption=True)(update) + assert not Filters.msg_in(['caption'])(update) + def test_and_filters(self, update): update.message.text = 'test' update.message.forward_date = datetime.datetime.now() From ac64027580543468c4655873722c8a9e567d61d9 Mon Sep 17 00:00:00 2001 From: Julian Ste <31321934+julian-st@users.noreply.github.com> Date: Sun, 27 Oct 2019 00:15:09 +0200 Subject: [PATCH 10/16] Fixed comments in examples (#1566) --- examples/paymentbot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/paymentbot.py b/examples/paymentbot.py index 229f87e32..5c65a043c 100644 --- a/examples/paymentbot.py +++ b/examples/paymentbot.py @@ -42,7 +42,7 @@ def start_with_shipping_callback(update, context): currency = "USD" # price in dollars price = 1 - # price * 100 so as to include 2 d.p. + # price * 100 so as to include 2 decimal points # check https://core.telegram.org/bots/payments#supported-currencies for more details prices = [LabeledPrice("Test", price * 100)] @@ -66,7 +66,7 @@ def start_without_shipping_callback(update, context): currency = "USD" # price in dollars price = 1 - # price * 100 so as to include 2 d.p. + # price * 100 so as to include 2 decimal points prices = [LabeledPrice("Test", price * 100)] # optionally pass need_name=True, need_phone_number=True, From 264de2b7c1ea5dc4224892833f0b42b80000c61c Mon Sep 17 00:00:00 2001 From: Poolitzer <25934244+Poolitzer@users.noreply.github.com> Date: Sat, 26 Oct 2019 15:42:47 -0700 Subject: [PATCH 11/16] Github actions - notify maintainers about changed examples (#1555) --- .github/workflows/example_notifier.yml | 14 ++++++++++++++ examples/README.md | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/example_notifier.yml diff --git a/.github/workflows/example_notifier.yml b/.github/workflows/example_notifier.yml new file mode 100644 index 000000000..661f63431 --- /dev/null +++ b/.github/workflows/example_notifier.yml @@ -0,0 +1,14 @@ +name: Warning maintainers +on: + pull_request: + paths: examples/** +jobs: + job: + runs-on: ubuntu-latest + name: about example change + steps: + - name: running the check + uses: Poolitzer/notifier-action@master + with: + notify-message: Hey there. Relax, I am just a little warning for the maintainers to release directly after merging your PR, otherwise we have broken examples and people might get confused :) + repo-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 3adda0430..e6e2241d2 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,6 @@ # Examples -In this folder there are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule. +In this folder are small examples to show what a bot written with `python-telegram-bot` looks like. Some bots focus on one specific aspect of the Telegram Bot API while others focus on one of the mechanics of this library. Except for the [`echobot.py`](#pure-api) example, they all use the high-level framework this library provides with the [`telegram.ext`](https://python-telegram-bot.readthedocs.io/en/latest/telegram.ext.html) submodule. All examples are licensed under the [CC0 License](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/LICENSE.txt) and are therefore fully dedicated to the public domain. You can use them as the base for your own bots without worrying about copyrights. @@ -35,4 +35,4 @@ A basic example of a bot that can accept payments. Don't forget to enable and co A basic example of a bot store conversation state and user_data over multiple restarts. ## Pure API -The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper. +The [`echobot.py`](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/echobot.py) example uses only the pure, "bare-metal" API wrapper. From b294c92bad9db31229684dbe6245d730e8b78377 Mon Sep 17 00:00:00 2001 From: Bibo-Joshi Date: Sun, 27 Oct 2019 13:33:30 +0100 Subject: [PATCH 12/16] question template: Add info about users' group (#1577) --- .github/ISSUE_TEMPLATE/question.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 7dbbe4c21..08fb76138 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -11,6 +11,8 @@ assignees: '' Hey there, you have a question? We are happy to answer. Please make sure no similar question was opened already. The following template is a suggestion how you can report an issue you run into whilst using our library. If you just want to ask a question, feel free to delete everything; just make sure you have a describing title :) + +Please mind that there is also a users' Telegram group at https://t.me/pythontelegrambotgroup for questions about the library. Questions asked there might be answered quicker than here. In case you are unable to join our group due to Telegram restrictions, you can use our IRC channel at https://webchat.freenode.net/?channels=##python-telegram-bot to participate in the group. --> ### Issue I am facing From 8b4b22cc89c6c0b1b827f3738306913a5f4491e3 Mon Sep 17 00:00:00 2001 From: Jasmin Bom Date: Sun, 27 Oct 2019 14:28:33 +0100 Subject: [PATCH 13/16] Implement Github Actions CI (#1556) * Add test workflow * Attempt github grouping * Improve bot info fetching - Add support for b64+json encoded github secret with all the vars - Add bot_name and bot_username since it's needed for a proper get_me test * Improve test workflow a lot - Add coverage - Install ujson - test_official only run on in single job - Pass bot info to pytest * Improve github grouping by having shorter titles * Run pytest with coverage * Improve coverage report * Proper exitcode behaviour for pytest * Proper test official handling * Proper error handling * Skip jobqueue tests on windows * run coverage tests even if nocoverage ones fail * Skip messagequeue tests on windows * Clean up to satisfy flake8 * Run meta tests --- .github/workflows/test.yml | 73 ++++++++++++++++++++++++++++++++++ tests/bots.py | 41 +++++++++++-------- tests/conftest.py | 5 +++ tests/plugin_github_group.py | 77 ++++++++++++++++++++++++++++++++++++ tests/test_jobqueue.py | 2 + tests/test_messagequeue.py | 2 + tests/test_meta.py | 11 +----- 7 files changed, 186 insertions(+), 25 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 tests/plugin_github_group.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..afd3faa84 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,73 @@ +name: Testing your PR +on: + pull_request: + branches: + - master + +jobs: + pytest: + name: pytest + runs-on: ${{matrix.os}} + strategy: + matrix: + python-version: [2.7, 3.5, 3.6, 3.7] + os: [ubuntu-latest, windows-latest] + include: + - os: ubuntu-latest + python-version: 3.7 + test-official: True + test-build: True + test-pre-commit: True + - os: windows-latest + python-version: 3.7 + test-build: True + fail-fast: False + steps: + - uses: actions/checkout@v1 + - name: Initialize vendored libs + run: + git submodule update --init --recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -U codecov pytest-cov + pip install ujson + pip install -r requirements.txt + pip install -r requirements-dev.txt + + - name: Test with pytest + run: | + pytest -v -m nocoverage + nocov_exit=$? + pytest -v -m "not nocoverage" --cov + cov_exit=$? + global_exit=$(( nocov_exit > cov_exit ? nocov_exit : cov_exit )) + exit ${global_exit} + env: + JOB_INDEX: ${{ strategy.job-index }} + BOTS: ${{ secrets.BOTS }} + TEST_BUILD: ${{ matrix.test-build }} + TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }} + shell: bash --noprofile --norc {0} + + - name: Compare to official api + run: | + pytest -v tests/test_official.py + exit $? + env: + TEST_OFFICIAL: "true" + if: matrix.test-official + shell: bash --noprofile --norc {0} + + - name: Submit coverage + run: | + if [ "$CODECOV_TOKEN" != "" ]; then + codecov -F github -t $CODECOV_TOKEN --name "${{ matrix.os }}-${{ matrix.python-version }}" + fi + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + shell: bash diff --git a/tests/bots.py b/tests/bots.py index 3177cde26..a7cca964b 100644 --- a/tests/bots.py +++ b/tests/bots.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/]. """Provide a bot to tests""" +import json +import base64 import os import random -import sys - -from platform import python_implementation # Provide some public fallbacks so it's easy for contributors to run tests on their local machine # These bots are only able to talk in our test chats, so they are quite useless for other @@ -32,32 +31,42 @@ FALLBACKS = [ 'payment_provider_token': '284685063:TEST:NjQ0NjZlNzI5YjJi', 'chat_id': '675666224', 'super_group_id': '-1001493296829', - 'channel_id': '@pythontelegrambottests' + 'channel_id': '@pythontelegrambottests', + 'bot_name': 'PTB tests fallback 1', + 'bot_username': '@ptb_fallback_1_bot' }, { 'token': '558194066:AAEEylntuKSLXj9odiv3TnX7Z5KY2J3zY3M', 'payment_provider_token': '284685063:TEST:YjEwODQwMTFmNDcy', 'chat_id': '675666224', 'super_group_id': '-1001493296829', - 'channel_id': '@pythontelegrambottests' + 'channel_id': '@pythontelegrambottests', + 'bot_name': 'PTB tests fallback 2', + 'bot_username': '@ptb_fallback_2_bot' } ] +GITHUB_ACTION = os.getenv('GITHUB_ACTION', None) +BOTS = os.getenv('BOTS', None) +JOB_INDEX = os.getenv('JOB_INDEX', None) +if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None: + BOTS = json.loads(base64.b64decode(BOTS).decode('utf-8')) + JOB_INDEX = int(JOB_INDEX) + def get(name, fallback): - full_name = '{0}_{1}_{2[0]}{2[1]}'.format(name, python_implementation(), - sys.version_info).upper() - # First try full_names such as - # TOKEN_CPYTHON_33 - # CHAT_ID_PYPY_27 - val = os.getenv(full_name) - if val: - return val - # Then try short names - # TOKEN - # CHAT_ID + # If we have TOKEN, PAYMENT_PROVIDER_TOKEN, CHAT_ID, SUPER_GROUP_ID, + # CHANNEL_ID, BOT_NAME, or BOT_USERNAME in the environment, then use that val = os.getenv(name.upper()) if val: return val + + # If we're running as a github action then fetch bots from the repo secrets + if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None: + try: + return BOTS[JOB_INDEX][name] + except KeyError: + pass + # Otherwise go with the fallback return fallback diff --git a/tests/conftest.py b/tests/conftest.py index c702b0f63..9d6a808c2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -37,6 +37,11 @@ TRAVIS = os.getenv('TRAVIS', False) if TRAVIS: pytest_plugins = ['tests.travis_fold'] +GITHUB_ACTION = os.getenv('GITHUB_ACTION', False) + +if GITHUB_ACTION: + pytest_plugins = ['tests.plugin_github_group'] + # THIS KEY IS OBVIOUSLY COMPROMISED # DO NOT USE IN PRODUCTION! PRIVATE_KEY = b"-----BEGIN RSA PRIVATE KEY-----\r\nMIIEowIBAAKCAQEA0AvEbNaOnfIL3GjB8VI4M5IaWe+GcK8eSPHkLkXREIsaddum\r\nwPBm/+w8lFYdnY+O06OEJrsaDtwGdU//8cbGJ/H/9cJH3dh0tNbfszP7nTrQD+88\r\nydlcYHzClaG8G+oTe9uEZSVdDXj5IUqR0y6rDXXb9tC9l+oSz+ShYg6+C4grAb3E\r\nSTv5khZ9Zsi/JEPWStqNdpoNuRh7qEYc3t4B/a5BH7bsQENyJSc8AWrfv+drPAEe\r\njQ8xm1ygzWvJp8yZPwOIYuL+obtANcoVT2G2150Wy6qLC0bD88Bm40GqLbSazueC\r\nRHZRug0B9rMUKvKc4FhG4AlNzBCaKgIcCWEqKwIDAQABAoIBACcIjin9d3Sa3S7V\r\nWM32JyVF3DvTfN3XfU8iUzV7U+ZOswA53eeFM04A/Ly4C4ZsUNfUbg72O8Vd8rg/\r\n8j1ilfsYpHVvphwxaHQlfIMa1bKCPlc/A6C7b2GLBtccKTbzjARJA2YWxIaqk9Nz\r\nMjj1IJK98i80qt29xRnMQ5sqOO3gn2SxTErvNchtBiwOH8NirqERXig8VCY6fr3n\r\nz7ZImPU3G/4qpD0+9ULrt9x/VkjqVvNdK1l7CyAuve3D7ha3jPMfVHFtVH5gqbyp\r\nKotyIHAyD+Ex3FQ1JV+H7DkP0cPctQiss7OiO9Zd9C1G2OrfQz9el7ewAPqOmZtC\r\nKjB3hUECgYEA/4MfKa1cvaCqzd3yUprp1JhvssVkhM1HyucIxB5xmBcVLX2/Kdhn\r\nhiDApZXARK0O9IRpFF6QVeMEX7TzFwB6dfkyIePsGxputA5SPbtBlHOvjZa8omMl\r\nEYfNa8x/mJkvSEpzvkWPascuHJWv1cEypqphu/70DxubWB5UKo/8o6cCgYEA0HFy\r\ncgwPMB//nltHGrmaQZPFT7/Qgl9ErZT3G9S8teWY4o4CXnkdU75tBoKAaJnpSfX3\r\nq8VuRerF45AFhqCKhlG4l51oW7TUH50qE3GM+4ivaH5YZB3biwQ9Wqw+QyNLAh/Q\r\nnS4/Wwb8qC9QuyEgcCju5lsCaPEXZiZqtPVxZd0CgYEAshBG31yZjO0zG1TZUwfy\r\nfN3euc8mRgZpSdXIHiS5NSyg7Zr8ZcUSID8jAkJiQ3n3OiAsuq1MGQ6kNa582kLT\r\nFPQdI9Ea8ahyDbkNR0gAY9xbM2kg/Gnro1PorH9PTKE0ekSodKk1UUyNrg4DBAwn\r\nqE6E3ebHXt/2WmqIbUD653ECgYBQCC8EAQNX3AFegPd1GGxU33Lz4tchJ4kMCNU0\r\nN2NZh9VCr3nTYjdTbxsXU8YP44CCKFG2/zAO4kymyiaFAWEOn5P7irGF/JExrjt4\r\nibGy5lFLEq/HiPtBjhgsl1O0nXlwUFzd7OLghXc+8CPUJaz5w42unqT3PBJa40c3\r\nQcIPdQKBgBnSb7BcDAAQ/Qx9juo/RKpvhyeqlnp0GzPSQjvtWi9dQRIu9Pe7luHc\r\nm1Img1EO1OyE3dis/rLaDsAa2AKu1Yx6h85EmNjavBqP9wqmFa0NIQQH8fvzKY3/\r\nP8IHY6009aoamLqYaexvrkHVq7fFKiI6k8myMJ6qblVNFv14+KXU\r\n-----END RSA PRIVATE KEY-----" # noqa: E501 diff --git a/tests/plugin_github_group.py b/tests/plugin_github_group.py new file mode 100644 index 000000000..b7f4ced60 --- /dev/null +++ b/tests/plugin_github_group.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2018 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. +import _pytest.config +import pytest + +fold_plugins = {'_cov': 'Coverage report', 'flaky': 'Flaky report'} + + +def terminal_summary_wrapper(original, plugin_name): + text = fold_plugins[plugin_name] + + def pytest_terminal_summary(terminalreporter): + terminalreporter.write('##[group] {}\n'.format(text)) + original(terminalreporter) + terminalreporter.write('##[endgroup]') + + return pytest_terminal_summary + + +@pytest.mark.trylast +def pytest_configure(config): + for hookimpl in config.pluginmanager.hook.pytest_terminal_summary._nonwrappers: + if hookimpl.plugin_name in fold_plugins.keys(): + hookimpl.function = terminal_summary_wrapper(hookimpl.function, + hookimpl.plugin_name) + + +terminal = None +previous_name = None + + +def _get_name(location): + if location[0].startswith('tests/'): + return location[0][6:] + return location[0] + + +@pytest.mark.trylast +def pytest_itemcollected(item): + item._nodeid = item._nodeid.split('::', 1)[1] + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_protocol(item, nextitem): + # This is naughty but pytests' own plugins does something similar too, so who cares + global terminal + if terminal is None: + terminal = _pytest.config.create_terminal_writer(item.config) + + global previous_name + + name = _get_name(item.location) + + if previous_name is None or previous_name != name: + previous_name = name + terminal.write('\n##[group] {}'.format(name)) + + yield + + if nextitem is None or _get_name(nextitem.location) != name: + terminal.write('\n##[endgroup]') diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py index f0c7ac405..b4a4985bc 100644 --- a/tests/test_jobqueue.py +++ b/tests/test_jobqueue.py @@ -40,6 +40,8 @@ def job_queue(bot, _dp): @pytest.mark.skipif(os.getenv('APPVEYOR'), reason="On Appveyor precise timings are not accurate.") +@pytest.mark.skipif(os.getenv('GITHUB_ACTIONS', False) and os.name == 'nt', + reason="On windows precise timings are not accurate.") @flaky(10, 1) # Timings aren't quite perfect class TestJobQueue(object): result = 0 diff --git a/tests/test_messagequeue.py b/tests/test_messagequeue.py index 0ed7831a8..60260ffc2 100644 --- a/tests/test_messagequeue.py +++ b/tests/test_messagequeue.py @@ -26,6 +26,8 @@ import telegram.ext.messagequeue as mq @pytest.mark.skipif(os.getenv('APPVEYOR'), reason="On Appveyor precise timings are not accurate.") +@pytest.mark.skipif(os.getenv('GITHUB_ACTIONS', False) and os.name == 'nt', + reason="On windows precise timings are not accurate.") class TestDelayQueue(object): N = 128 burst_limit = 30 diff --git a/tests/test_meta.py b/tests/test_meta.py index 9c6abf13f..512541087 100644 --- a/tests/test_meta.py +++ b/tests/test_meta.py @@ -17,8 +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/]. import os -import sys -from platform import python_implementation import pytest @@ -30,17 +28,12 @@ def call_pre_commit_hook(hook_id): @pytest.mark.nocoverage @pytest.mark.parametrize('hook_id', argvalues=('yapf', 'flake8', 'pylint')) -@pytest.mark.skipif(not (os.getenv('TRAVIS') or os.getenv('APPVEYOR')), reason='Not running in CI') -@pytest.mark.skipif(not sys.version_info[:2] == (3, 6) or python_implementation() != 'CPython', - reason='Only running pre-commit-hooks on newest tested python version, ' - 'as they are slow and consistent across platforms.') +@pytest.mark.skipif(not os.getenv('TEST_PRE_COMMIT', False), reason='TEST_PRE_COMMIT not enabled') def test_pre_commit_hook(hook_id): assert call_pre_commit_hook(hook_id) == 0 # pragma: no cover @pytest.mark.nocoverage -@pytest.mark.skipif( - not sys.version_info[:2] in ((3, 6), (2, 7)) or python_implementation() != 'CPython', - reason='Only testing build on 2.7 and 3.6') +@pytest.mark.skipif(not os.getenv('TEST_BUILD', False), reason='TEST_BUILD not enabled') def test_build(): assert os.system('python setup.py bdist_dumb') == 0 # pragma: no cover From e9d9f01bd41819424fe7d8fc33dfa67f05046956 Mon Sep 17 00:00:00 2001 From: Noam Meltzer Date: Sat, 9 Nov 2019 20:33:51 +0200 Subject: [PATCH 14/16] Fix windows actions (#1605) * Stop testing with ujson * Fix timing issue with test_delete_message * ignore pip deprecation warning. hopefully it will fix windows+py2.7 * telegram replies to deleteing old messages are not consistent --- .github/workflows/test.yml | 9 ++++----- tests/test_bot.py | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index afd3faa84..29eb00022 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,11 +33,10 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -U codecov pytest-cov - pip install ujson - pip install -r requirements.txt - pip install -r requirements-dev.txt + python -W ignore -m pip install --upgrade pip + python -W ignore -m pip install -U codecov pytest-cov + python -W ignore -m pip install -r requirements.txt + python -W ignore -m pip install -r requirements-dev.txt - name: Test with pytest run: | diff --git a/tests/test_bot.py b/tests/test_bot.py index 5eb581360..684291d75 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -114,13 +114,14 @@ class TestBot(object): @pytest.mark.timeout(10) def test_delete_message(self, bot, chat_id): message = bot.send_message(chat_id, text='will be deleted') + time.sleep(2) assert bot.delete_message(chat_id=chat_id, message_id=message.message_id) is True @flaky(3, 1) @pytest.mark.timeout(10) def test_delete_message_old_message(self, bot, chat_id): - with pytest.raises(TelegramError, match='Message to delete not found'): + with pytest.raises(BadRequest): # Considering that the first message is old enough bot.delete_message(chat_id=chat_id, message_id=1) From 096a7c359395729d254d9b27afaec340697d481c Mon Sep 17 00:00:00 2001 From: Noam Meltzer Date: Sat, 9 Nov 2019 22:13:02 +0200 Subject: [PATCH 15/16] Allow PRs to test (#1606) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 29eb00022..91ccf40f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: exit ${global_exit} env: JOB_INDEX: ${{ strategy.job-index }} - BOTS: ${{ secrets.BOTS }} + BOTS: W3sidG9rZW4iOiAiNjk2MTg4NzMyOkFBR1Z3RUtmSEhsTmpzY3hFRE5LQXdraEdzdFpfa28xbUMwIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WldGaU1UUmxNbVF5TnpNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMi43IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzI3X2JvdCJ9LCB7InRva2VuIjogIjY3MTQ2ODg4NjpBQUdQR2ZjaVJJQlVORmU4MjR1SVZkcTdKZTNfWW5BVE5HdyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpHWXdPVGxrTXpNeE4yWTIiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNCIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zNF9ib3QifSwgeyJ0b2tlbiI6ICI2MjkzMjY1Mzg6QUFGUnJaSnJCN29CM211ekdzR0pYVXZHRTVDUXpNNUNVNG8iLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpNbU01WVdKaFl6a3hNMlUxIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgQ1B5dGhvbiAzLjUiLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX2NweXRob25fMzVfYm90In0sIHsidG9rZW4iOiAiNjQwMjA4OTQzOkFBRmhCalFwOXFtM1JUeFN6VXBZekJRakNsZS1Kano1aGNrIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WXpoa1pUZzFOamMxWXpWbCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIENQeXRob24gMy42IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19jcHl0aG9uXzM2X2JvdCJ9LCB7InRva2VuIjogIjY5NTEwNDA4ODpBQUhmenlsSU9qU0lJUy1lT25JMjB5MkUyMEhvZEhzZnotMCIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOk9HUTFNRGd3WmpJd1pqRmwiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIFRyYXZpcyB1c2luZyBDUHl0aG9uIDMuNyIsICJib3RfdXNlcm5hbWUiOiAiQHB0Yl90cmF2aXNfY3B5dGhvbl8zN19ib3QifSwgeyJ0b2tlbiI6ICI2OTE0MjM1NTQ6QUFGOFdrakNaYm5IcVBfaTZHaFRZaXJGRWxackdhWU9oWDAiLCAicGF5bWVudF9wcm92aWRlcl90b2tlbiI6ICIyODQ2ODUwNjM6VEVTVDpZamM1TlRoaU1tUXlNV1ZoIiwgImJvdF9uYW1lIjogIlBUQiB0ZXN0cyBvbiBUcmF2aXMgdXNpbmcgUHlQeSAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfdHJhdmlzX3B5cHlfMjdfYm90In0sIHsidG9rZW4iOiAiNjg0MzM5OTg0OkFBRk1nRUVqcDAxcjVyQjAwN3lDZFZOc2c4QWxOc2FVLWNjIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6TVRBek1UWTNNR1V5TmpnMCIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gVHJhdmlzIHVzaW5nIFB5UHkgMy41IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX3RyYXZpc19weXB5XzM1X2JvdCJ9LCB7InRva2VuIjogIjY5MDA5MTM0NzpBQUZMbVI1cEFCNVljcGVfbU9oN3pNNEpGQk9oMHozVDBUbyIsICJwYXltZW50X3Byb3ZpZGVyX3Rva2VuIjogIjI4NDY4NTA2MzpURVNUOlpEaGxOekU1TURrd1lXSmkiLCAiYm90X25hbWUiOiAiUFRCIHRlc3RzIG9uIEFwcFZleW9yIHVzaW5nIENQeXRob24gMy40IiwgImJvdF91c2VybmFtZSI6ICJAcHRiX2FwcHZleW9yX2NweXRob25fMzRfYm90In0sIHsidG9rZW4iOiAiNjk0MzA4MDUyOkFBRUIyX3NvbkNrNTVMWTlCRzlBTy1IOGp4aVBTNTVvb0JBIiwgInBheW1lbnRfcHJvdmlkZXJfdG9rZW4iOiAiMjg0Njg1MDYzOlRFU1Q6WW1aaVlXWm1NakpoWkdNeSIsICJib3RfbmFtZSI6ICJQVEIgdGVzdHMgb24gQXBwVmV5b3IgdXNpbmcgQ1B5dGhvbiAyLjciLCAiYm90X3VzZXJuYW1lIjogIkBwdGJfYXBwdmV5b3JfY3B5dGhvbl8yN19ib3QifV0= TEST_BUILD: ${{ matrix.test-build }} TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }} shell: bash --noprofile --norc {0} From 10c9ec231305a023984b8f822b7674bdfe0aca04 Mon Sep 17 00:00:00 2001 From: Noam Meltzer Date: Sat, 9 Nov 2019 22:38:23 +0200 Subject: [PATCH 16/16] workflow: run test-official in its own job (#1607) --- .github/workflows/test.yml | 40 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 91ccf40f3..0d182b081 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,6 @@ jobs: include: - os: ubuntu-latest python-version: 3.7 - test-official: True test-build: True test-pre-commit: True - os: windows-latest @@ -53,15 +52,6 @@ jobs: TEST_PRE_COMMIT: ${{ matrix.test-pre-commit }} shell: bash --noprofile --norc {0} - - name: Compare to official api - run: | - pytest -v tests/test_official.py - exit $? - env: - TEST_OFFICIAL: "true" - if: matrix.test-official - shell: bash --noprofile --norc {0} - - name: Submit coverage run: | if [ "$CODECOV_TOKEN" != "" ]; then @@ -70,3 +60,33 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} shell: bash + test_official: + name: test-official + runs-on: ${{matrix.os}} + strategy: + matrix: + python-version: [3.7] + os: [ubuntu-latest] + fail-fast: False + steps: + - uses: actions/checkout@v1 + - name: Initialize vendored libs + run: + git submodule update --init --recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -W ignore -m pip install --upgrade pip + python -W ignore -m pip install -r requirements.txt + python -W ignore -m pip install -r requirements-dev.txt + - name: Compare to official api + run: | + pytest -v tests/test_official.py + exit $? + env: + TEST_OFFICIAL: "true" + shell: bash --noprofile --norc {0} +