From 3e8ccc3772d76ee261f21bab7b2a6557a68ffde4 Mon Sep 17 00:00:00 2001 From: David Brochart Date: Tue, 25 Feb 2020 12:49:24 +0100 Subject: [PATCH 1/4] Show timeout error in cell output --- voila/execute.py | 24 ++++++++++++++++++++++++ voila/handler.py | 14 +++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/voila/execute.py b/voila/execute.py index b26a68f93..bc52badf0 100644 --- a/voila/execute.py +++ b/voila/execute.py @@ -131,6 +131,14 @@ class VoilaExecutePreprocessor(ExecutePreprocessor): ) ) + cell_timeout_instruction = Unicode( + 'Please run Voila with --VoilaExecutePreprocessor.interrupt_on_timeout=True to continue executing the rest of the notebook.', + config=True, + help=( + 'instruction given to user to continue execution on timeout' + ) + ) + def __init__(self, **kwargs): super(VoilaExecutePreprocessor, self).__init__(**kwargs) self.output_hook_stack = collections.defaultdict(list) # maps to list of hooks, where the last is used @@ -154,6 +162,10 @@ def preprocess_cell(self, cell, resources, cell_index, store_history=True): # TODO: pass store_history as a 5th argument when we can require nbconver >=5.6.1 # result = super(VoilaExecutePreprocessor, self).preprocess_cell(cell, resources, cell_index, store_history) result = super(VoilaExecutePreprocessor, self).preprocess_cell(cell, resources, cell_index) + except TimeoutError as e: + self.log.error(e) + self.show_code_cell_timeout(cell) + raise e except CellExecutionError as e: self.log.error(e) result = (cell, resources) @@ -321,6 +333,18 @@ def run_cell(self, cell, cell_index=0, store_history=False): return execute_reply, cell.outputs + def show_code_cell_timeout(self, cell): + """Show a timeout error output in a code cell.""" + + timeout_message = 'Cell execution timed out, aborting notebook execution. {}'.format(self.cell_timeout_instruction) + + output = {'output_type': 'error', + 'ename': 'TimeoutError', + 'evalue': 'Timeout error', + 'traceback': [timeout_message]} + + cell['outputs'] = [output] + def executenb(nb, cwd=None, km=None, **kwargs): resources = {} diff --git a/voila/handler.py b/voila/handler.py index ecf50f3e3..2a68f7085 100644 --- a/voila/handler.py +++ b/voila/handler.py @@ -21,6 +21,11 @@ from .execute import executenb, VoilaExecutePreprocessor from .exporter import VoilaExporter +try: + TimeoutError # Py 3 +except NameError: + TimeoutError = RuntimeError # Py 2 + class VoilaHandler(JupyterHandler): @@ -140,9 +145,16 @@ def _jinja_cell_generator(self, nb, kernel_id): nb, resources = ClearOutputPreprocessor().preprocess(nb, {'metadata': {'path': self.cwd}}) ep = VoilaExecutePreprocessor(config=self.traitlet_config) + stop_execution = False with ep.setup_preprocessor(nb, resources, km=km): for cell_idx, cell in enumerate(nb.cells): - res = ep.preprocess_cell(cell, resources, cell_idx, store_history=False) + if stop_execution: + break + try: + res = ep.preprocess_cell(cell, resources, cell_idx, store_history=False) + except TimeoutError: + res = (cell, resources) + stop_execution = True yield res[0] From 1dcfa993d6c65261e7cf5b4e44a06465d320c3ec Mon Sep 17 00:00:00 2001 From: David Brochart Date: Tue, 10 Mar 2020 13:46:16 +0100 Subject: [PATCH 2/4] Add test --- tests/app/timeout_test.py | 20 ++++++++++++++++++++ tests/notebooks/sleep.ipynb | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/app/timeout_test.py create mode 100644 tests/notebooks/sleep.ipynb diff --git a/tests/app/timeout_test.py b/tests/app/timeout_test.py new file mode 100644 index 000000000..9da18761d --- /dev/null +++ b/tests/app/timeout_test.py @@ -0,0 +1,20 @@ +import pytest + +import os + + +@pytest.fixture +def voila_notebook(notebook_directory): + return os.path.join(notebook_directory, 'sleep.ipynb') + + +@pytest.fixture +def voila_args_extra(): + return ['--VoilaExecutePreprocessor.timeout=1'] + + +@pytest.mark.gen_test +def test_timeout(http_client, base_url): + response = yield http_client.fetch(base_url) + html_text = response.body.decode('utf-8') + assert 'Cell execution timed out' in html_text diff --git a/tests/notebooks/sleep.ipynb b/tests/notebooks/sleep.ipynb new file mode 100644 index 000000000..31a1d77dc --- /dev/null +++ b/tests/notebooks/sleep.ipynb @@ -0,0 +1,35 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "time.sleep(10)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 56cdf95fdf12637cc0f352cd449b9fe07396ed99 Mon Sep 17 00:00:00 2001 From: David Brochart Date: Wed, 11 Mar 2020 18:24:41 +0100 Subject: [PATCH 3/4] Remove python2 code --- voila/execute.py | 10 +--------- voila/handler.py | 5 ----- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/voila/execute.py b/voila/execute.py index bc52badf0..c4aeaaa5a 100644 --- a/voila/execute.py +++ b/voila/execute.py @@ -8,10 +8,7 @@ ############################################################################# import collections import logging -try: - from time import monotonic # Py 3 -except ImportError: - from time import time as monotonic # Py 2 +from time import monotonic from nbconvert.preprocessors import ClearOutputPreprocessor from nbconvert.preprocessors.execute import CellExecutionError, ExecutePreprocessor @@ -21,11 +18,6 @@ from traitlets import Unicode from ipykernel.jsonutil import json_clean -try: - TimeoutError # Py 3 -except NameError: - TimeoutError = RuntimeError # Py 2 - def strip_code_cell_warnings(cell): """Strip any warning outputs and traceback from a code cell.""" diff --git a/voila/handler.py b/voila/handler.py index 2a68f7085..78ff2c95f 100644 --- a/voila/handler.py +++ b/voila/handler.py @@ -21,11 +21,6 @@ from .execute import executenb, VoilaExecutePreprocessor from .exporter import VoilaExporter -try: - TimeoutError # Py 3 -except NameError: - TimeoutError = RuntimeError # Py 2 - class VoilaHandler(JupyterHandler): From 310338511edd26d0ac1374654db8e84966f29f1f Mon Sep 17 00:00:00 2001 From: David Brochart Date: Mon, 23 Mar 2020 11:10:52 +0100 Subject: [PATCH 4/4] Don't wait for kernel shutdown --- .github/workflows/main.yml | 10 ++++------ tests/app/timeout_test.py | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 030f923b0..70aca8172 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -26,14 +26,12 @@ jobs: - name: Add conda to $PATH run: echo ::add-path::$CONDA/condabin - - name: Update conda on Mac + - name: Fix permissions on Mac if: matrix.os == 'macos-latest' run: | - # sudo required? - sudo conda update -y -n base conda setuptools - - - name: Update conda on Linux - if: matrix.os == 'ubuntu-latest' + sudo chmod -R a+rw /usr/local/miniconda + + - name: Update conda run: | conda update -y -n base conda setuptools diff --git a/tests/app/timeout_test.py b/tests/app/timeout_test.py index 9da18761d..df3c37fed 100644 --- a/tests/app/timeout_test.py +++ b/tests/app/timeout_test.py @@ -10,7 +10,7 @@ def voila_notebook(notebook_directory): @pytest.fixture def voila_args_extra(): - return ['--VoilaExecutePreprocessor.timeout=1'] + return ['--VoilaExecutePreprocessor.timeout=1', '--KernelManager.shutdown_wait_time=0.1'] @pytest.mark.gen_test