From d79030ac895ed76ac5f01a675f0e9271728faca2 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Wed, 27 Jan 2021 14:31:20 -0800 Subject: [PATCH 1/8] Add execution_strict_mode configuration --- myst_nb/__init__.py | 1 + myst_nb/execution.py | 15 +++++++++++---- tests/test_execute.py | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/myst_nb/__init__.py b/myst_nb/__init__.py index 50f34c2e..a26eec60 100644 --- a/myst_nb/__init__.py +++ b/myst_nb/__init__.py @@ -129,6 +129,7 @@ def visit_element_html(self, node): # show traceback in stdout (in addition to writing to file) # this is useful in e.g. RTD where one cannot inspect a file app.add_config_value("execution_show_tb", False, "") + app.add_config_value("execution_strict_mode", False, "env") app.add_config_value("nb_custom_formats", {}, "env") # render config diff --git a/myst_nb/execution.py b/myst_nb/execution.py index f5107e27..5ac14736 100644 --- a/myst_nb/execution.py +++ b/myst_nb/execution.py @@ -151,7 +151,7 @@ def generate_notebook_outputs( show_traceback, "Execution Failed with traceback saved in {}", ) - LOGGER.error(message) + handle_execution_failure(env, message) ntbk = result.nb @@ -289,8 +289,8 @@ def _stage_and_execute( # Normally we want to keep the stage records available, so that we can retrieve # execution tracebacks at the `generate_notebook_outputs` stage, # but we need to flush if it becomes 'corrupted' - LOGGER.error( - "Execution failed in an unexpected way, clearing staged notebooks: %s", err + handle_execution_failure(env, + "Execution failed in an unexpected way, clearing staged notebooks: {err}" ) for record in cache_base.list_staged_records(): cache_base.discard_staged_notebook(record.pk) @@ -308,7 +308,7 @@ def execute_staged_nb( try: executor = load_executor("basic", cache_base, logger=LOGGER) except ImportError as error: - LOGGER.error(str(error)) + handle_execution_failure(env, str(error)) return 1 def _converter(path): @@ -339,3 +339,10 @@ def nb_has_all_output(source_path: str, nb_extensions: List[str] = (".ipynb",)) if cell["cell_type"] == "code" ) return has_outputs + + +def handle_execution_failure(env, message): + if env.config["execution_strict_mode"]: + raise ValueError(f"Execution failed: {message}") + else: + LOGGER.error(message) diff --git a/tests/test_execute.py b/tests/test_execute.py index 42216438..7d00f05e 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -300,3 +300,22 @@ def test_custom_convert_cache(sphinx_run, file_regression, check_nbs): assert "custom-formats" in sphinx_run.env.nb_execution_data assert sphinx_run.env.nb_execution_data["custom-formats"]["method"] == "cache" assert sphinx_run.env.nb_execution_data["custom-formats"]["succeeded"] is True + + +@pytest.mark.sphinx_params( + "basic_failing.ipynb", + conf={"execution_allow_errors": False, "execution_strict_mode": True}, +) +def test_execution_strict_mode_true(sphinx_run, file_regression, check_nbs): + with pytest.raises(ValueError) as excinfo: + sphinx_run.build() + assert str(excinfo.value).startswith("Execution failed:") + + +@pytest.mark.sphinx_params( + "basic_failing.ipynb", + conf={"execution_allow_errors": False, "execution_strict_mode": False}, +) +def test_execution_strict_mode_false(sphinx_run, file_regression, check_nbs): + sphinx_run.build() + assert "Execution Failed" in sphinx_run.warnings() From 7ab81fb12140e4e7eea5d465778f17f86fbfcdf1 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Wed, 27 Jan 2021 14:42:35 -0800 Subject: [PATCH 2/8] fix flakes & code formatting --- myst_nb/execution.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/myst_nb/execution.py b/myst_nb/execution.py index 5ac14736..19080a30 100644 --- a/myst_nb/execution.py +++ b/myst_nb/execution.py @@ -289,8 +289,9 @@ def _stage_and_execute( # Normally we want to keep the stage records available, so that we can retrieve # execution tracebacks at the `generate_notebook_outputs` stage, # but we need to flush if it becomes 'corrupted' - handle_execution_failure(env, - "Execution failed in an unexpected way, clearing staged notebooks: {err}" + handle_execution_failure( + env, + f"Execution failed in an unexpected way, clearing staged notebooks: {err}", ) for record in cache_base.list_staged_records(): cache_base.discard_staged_notebook(record.pk) From 81c64b76db1054b4f59ae5ebd0b9023a2704a006 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Thu, 28 Jan 2021 11:08:00 -0800 Subject: [PATCH 3/8] Change execution_strict_mode to execution_fail_on_error --- myst_nb/__init__.py | 2 +- myst_nb/execution.py | 48 ++++++++++++++++++++++++++++--------------- tests/test_execute.py | 23 +++++++++++++-------- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/myst_nb/__init__.py b/myst_nb/__init__.py index a26eec60..ccbd4ecd 100644 --- a/myst_nb/__init__.py +++ b/myst_nb/__init__.py @@ -125,11 +125,11 @@ def visit_element_html(self, node): app.add_config_value("jupyter_execute_notebooks", "auto", "env") app.add_config_value("execution_timeout", 30, "env") app.add_config_value("execution_allow_errors", False, "env") + app.add_config_value("execution_fail_on_error", False, "env") app.add_config_value("execution_in_temp", False, "env") # show traceback in stdout (in addition to writing to file) # this is useful in e.g. RTD where one cannot inspect a file app.add_config_value("execution_show_tb", False, "") - app.add_config_value("execution_strict_mode", False, "env") app.add_config_value("nb_custom_formats", {}, "env") # render config diff --git a/myst_nb/execution.py b/myst_nb/execution.py index 19080a30..0f652624 100644 --- a/myst_nb/execution.py +++ b/myst_nb/execution.py @@ -31,6 +31,10 @@ LOGGER = logging.getLogger(__name__) +class ExecutionError(Exception): + pass + + def update_execution_cache( app: Sphinx, builder: Builder, added: Set[str], changed: Set[str], removed: Set[str] ): @@ -80,6 +84,7 @@ def update_execution_cache( path_to_cache=app.env.nb_path_to_cache, timeout=app.config["execution_timeout"], allow_errors=app.config["execution_allow_errors"], + fail_on_error=app.config["execution_fail_on_error"], exec_in_temp=app.config["execution_in_temp"], ) @@ -107,6 +112,9 @@ def generate_notebook_outputs( # If we have a jupyter_cache, see if there's a cache for this notebook file_path = file_path or env.doc2path(env.docname) + fail_on_error = env.config["execution_fail_on_error"] + allow_errors = env.config["execution_allow_errors"] and not fail_on_error + execution_method = env.config["jupyter_execute_notebooks"] # type: str path_to_cache = env.nb_path_to_cache if "cache" in execution_method else None @@ -130,7 +138,7 @@ def generate_notebook_outputs( ntbk, cwd=tmpdirname, timeout=env.config["execution_timeout"], - allow_errors=env.config["execution_allow_errors"], + allow_errors=allow_errors, ) else: cwd = Path(file_path).parent @@ -139,7 +147,7 @@ def generate_notebook_outputs( ntbk, cwd=cwd, timeout=env.config["execution_timeout"], - allow_errors=env.config["execution_allow_errors"], + allow_errors=allow_errors, ) report_path = None @@ -151,7 +159,10 @@ def generate_notebook_outputs( show_traceback, "Execution Failed with traceback saved in {}", ) - handle_execution_failure(env, message) + if fail_on_error: + raise ExecutionError(message) + else: + LOGGER.error(message) ntbk = result.nb @@ -197,7 +208,10 @@ def generate_notebook_outputs( ) message += suffix - LOGGER.error(message) + if fail_on_error: + raise ExecutionError(message) + else: + LOGGER.error(message) else: LOGGER.verbose("Merged cached outputs into %s", str(r_file_path)) @@ -257,6 +271,7 @@ def _stage_and_execute( path_to_cache: str, timeout: Optional[int], allow_errors: bool, + fail_on_error: bool, exec_in_temp: bool, ): pk_list = [] @@ -280,6 +295,7 @@ def _stage_and_execute( timeout=timeout, exec_in_temp=exec_in_temp, allow_errors=allow_errors, + fail_on_error=fail_on_error, env=env, ) except OSError as err: @@ -289,10 +305,13 @@ def _stage_and_execute( # Normally we want to keep the stage records available, so that we can retrieve # execution tracebacks at the `generate_notebook_outputs` stage, # but we need to flush if it becomes 'corrupted' - handle_execution_failure( - env, - f"Execution failed in an unexpected way, clearing staged notebooks: {err}", + message = ( + f"Execution failed in an unexpected way, clearing staged notebooks: {err}" ) + if fail_on_error: + raise ExecutionError(message) + else: + LOGGER.error(message) for record in cache_base.list_staged_records(): cache_base.discard_staged_notebook(record.pk) @@ -303,13 +322,17 @@ def execute_staged_nb( timeout: Optional[int], exec_in_temp: bool, allow_errors: bool, + fail_on_error: bool, env: BuildEnvironment, ): """Executing the staged notebook.""" try: executor = load_executor("basic", cache_base, logger=LOGGER) except ImportError as error: - handle_execution_failure(env, str(error)) + if fail_on_error: + raise ExecutionError(str(error)) + else: + LOGGER.error(str(error)) return 1 def _converter(path): @@ -320,7 +343,7 @@ def _converter(path): filter_pks=pk_list or None, converter=_converter, timeout=timeout, - allow_errors=allow_errors, + allow_errors=allow_errors and not fail_on_error, run_in_temp=exec_in_temp, ) return result @@ -340,10 +363,3 @@ def nb_has_all_output(source_path: str, nb_extensions: List[str] = (".ipynb",)) if cell["cell_type"] == "code" ) return has_outputs - - -def handle_execution_failure(env, message): - if env.config["execution_strict_mode"]: - raise ValueError(f"Execution failed: {message}") - else: - LOGGER.error(message) diff --git a/tests/test_execute.py b/tests/test_execute.py index 7d00f05e..d3570006 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -1,6 +1,8 @@ import os import pytest +from myst_nb.execution import ExecutionError + def regress_nb_doc(file_regression, sphinx_run, check_nbs): file_regression.check( @@ -304,18 +306,23 @@ def test_custom_convert_cache(sphinx_run, file_regression, check_nbs): @pytest.mark.sphinx_params( "basic_failing.ipynb", - conf={"execution_allow_errors": False, "execution_strict_mode": True}, + conf={"execution_allow_errors": False, "execution_fail_on_error": True}, ) -def test_execution_strict_mode_true(sphinx_run, file_regression, check_nbs): - with pytest.raises(ValueError) as excinfo: +def test_execution_fail_on_error(sphinx_run, file_regression, check_nbs): + with pytest.raises(ExecutionError) as excinfo: sphinx_run.build() - assert str(excinfo.value).startswith("Execution failed:") + assert str(excinfo.value).startswith("Execution Failed") + cell_contents = "------------------\nraise Exception('oopsie!')\n------------------" + assert cell_contents in str(excinfo.value) @pytest.mark.sphinx_params( "basic_failing.ipynb", - conf={"execution_allow_errors": False, "execution_strict_mode": False}, + conf={"execution_allow_errors": True, "execution_fail_on_error": True}, ) -def test_execution_strict_mode_false(sphinx_run, file_regression, check_nbs): - sphinx_run.build() - assert "Execution Failed" in sphinx_run.warnings() +def test_execution_fail_on_error_allow_errors(sphinx_run, file_regression, check_nbs): + with pytest.raises(ExecutionError) as excinfo: + sphinx_run.build() + assert str(excinfo.value).startswith("Execution Failed") + cell_contents = "------------------\nraise Exception('oopsie!')\n------------------" + assert cell_contents in str(excinfo.value) From 33b69cc2aa99827d15af08b354e4cdd3d3f34135 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Thu, 28 Jan 2021 11:37:45 -0800 Subject: [PATCH 4/8] Report cell contents on failure --- myst_nb/execution.py | 16 ++++++++-------- tests/test_execute.py | 9 +++++++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/myst_nb/execution.py b/myst_nb/execution.py index 0f652624..16c4d8ba 100644 --- a/myst_nb/execution.py +++ b/myst_nb/execution.py @@ -152,16 +152,16 @@ def generate_notebook_outputs( report_path = None if result.err: - report_path, message = _report_exec_fail( - env, - Path(file_path).name, - result.exc_string, - show_traceback, - "Execution Failed with traceback saved in {}", - ) if fail_on_error: - raise ExecutionError(message) + raise ExecutionError(str(result.err)) else: + report_path, message = _report_exec_fail( + env, + Path(file_path).name, + result.exc_string, + show_traceback, + "Execution Failed with traceback saved in {}", + ) LOGGER.error(message) ntbk = result.nb diff --git a/tests/test_execute.py b/tests/test_execute.py index d3570006..f08f8e2f 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -311,7 +311,9 @@ def test_custom_convert_cache(sphinx_run, file_regression, check_nbs): def test_execution_fail_on_error(sphinx_run, file_regression, check_nbs): with pytest.raises(ExecutionError) as excinfo: sphinx_run.build() - assert str(excinfo.value).startswith("Execution Failed") + assert str(excinfo.value).startswith( + "An error occurred while executing the following cell:" + ) cell_contents = "------------------\nraise Exception('oopsie!')\n------------------" assert cell_contents in str(excinfo.value) @@ -323,6 +325,9 @@ def test_execution_fail_on_error(sphinx_run, file_regression, check_nbs): def test_execution_fail_on_error_allow_errors(sphinx_run, file_regression, check_nbs): with pytest.raises(ExecutionError) as excinfo: sphinx_run.build() - assert str(excinfo.value).startswith("Execution Failed") + + assert str(excinfo.value).startswith( + "An error occurred while executing the following cell:" + ) cell_contents = "------------------\nraise Exception('oopsie!')\n------------------" assert cell_contents in str(excinfo.value) From c5c82e9ee9d786a92804011338a6a32681a62af8 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas Date: Thu, 28 Jan 2021 12:40:01 -0800 Subject: [PATCH 5/8] Fix interaction between allow_errors and fail_on_error --- myst_nb/execution.py | 13 ++--- tests/test_execute.py | 11 +--- ...execution_fail_on_error_allow_errors.ipynb | 56 +++++++++++++++++++ ...t_execution_fail_on_error_allow_errors.xml | 12 ++++ 4 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb create mode 100644 tests/test_execute/test_execution_fail_on_error_allow_errors.xml diff --git a/myst_nb/execution.py b/myst_nb/execution.py index 16c4d8ba..994d7bc1 100644 --- a/myst_nb/execution.py +++ b/myst_nb/execution.py @@ -112,9 +112,6 @@ def generate_notebook_outputs( # If we have a jupyter_cache, see if there's a cache for this notebook file_path = file_path or env.doc2path(env.docname) - fail_on_error = env.config["execution_fail_on_error"] - allow_errors = env.config["execution_allow_errors"] and not fail_on_error - execution_method = env.config["jupyter_execute_notebooks"] # type: str path_to_cache = env.nb_path_to_cache if "cache" in execution_method else None @@ -138,7 +135,7 @@ def generate_notebook_outputs( ntbk, cwd=tmpdirname, timeout=env.config["execution_timeout"], - allow_errors=allow_errors, + allow_errors=env.config["execution_allow_errors"], ) else: cwd = Path(file_path).parent @@ -147,12 +144,12 @@ def generate_notebook_outputs( ntbk, cwd=cwd, timeout=env.config["execution_timeout"], - allow_errors=allow_errors, + allow_errors=env.config["execution_allow_errors"], ) report_path = None if result.err: - if fail_on_error: + if env.config["execution_fail_on_error"]: raise ExecutionError(str(result.err)) else: report_path, message = _report_exec_fail( @@ -208,7 +205,7 @@ def generate_notebook_outputs( ) message += suffix - if fail_on_error: + if env.config["execution_fail_on_error"]: raise ExecutionError(message) else: LOGGER.error(message) @@ -343,7 +340,7 @@ def _converter(path): filter_pks=pk_list or None, converter=_converter, timeout=timeout, - allow_errors=allow_errors and not fail_on_error, + allow_errors=allow_errors, run_in_temp=exec_in_temp, ) return result diff --git a/tests/test_execute.py b/tests/test_execute.py index f08f8e2f..51ae64be 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -323,11 +323,6 @@ def test_execution_fail_on_error(sphinx_run, file_regression, check_nbs): conf={"execution_allow_errors": True, "execution_fail_on_error": True}, ) def test_execution_fail_on_error_allow_errors(sphinx_run, file_regression, check_nbs): - with pytest.raises(ExecutionError) as excinfo: - sphinx_run.build() - - assert str(excinfo.value).startswith( - "An error occurred while executing the following cell:" - ) - cell_contents = "------------------\nraise Exception('oopsie!')\n------------------" - assert cell_contents in str(excinfo.value) + sphinx_run.build() + assert not sphinx_run.warnings() + regress_nb_doc(file_regression, sphinx_run, check_nbs) diff --git a/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb b/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb new file mode 100644 index 00000000..7c20d877 --- /dev/null +++ b/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb @@ -0,0 +1,56 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# a title\n", + "\n", + "some text\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "ename": "Exception", + "evalue": "oopsie!", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'oopsie!'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mException\u001b[0m: oopsie!" + ] + } + ], + "source": [ + "raise Exception('oopsie!')" + ] + } + ], + "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" + }, + "test_name": "notebook1" + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/tests/test_execute/test_execution_fail_on_error_allow_errors.xml b/tests/test_execute/test_execution_fail_on_error_allow_errors.xml new file mode 100644 index 00000000..9dd3813e --- /dev/null +++ b/tests/test_execute/test_execution_fail_on_error_allow_errors.xml @@ -0,0 +1,12 @@ + +
+ + a title + <paragraph> + some text + <CellNode cell_type="code" classes="cell"> + <CellInputNode classes="cell_input"> + <literal_block language="ipython3" xml:space="preserve"> + raise Exception('oopsie!') + <CellOutputNode classes="cell_output"> + <CellOutputBundleNode output_count="1"> From 55378687999727787640699e2d839741ec40ffa4 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas <jakevdp@google.com> Date: Thu, 28 Jan 2021 13:15:25 -0800 Subject: [PATCH 6/8] Add docs --- docs/use/execute.md | 6 ++++++ docs/use/start.md | 4 ++++ .../test_execution_fail_on_error_allow_errors.ipynb | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/use/execute.md b/docs/use/execute.md index 3c23493c..6cdecdce 100644 --- a/docs/use/execute.md +++ b/docs/use/execute.md @@ -161,6 +161,12 @@ tags: [raises-exception] print(thisvariabledoesntexist) ``` +(execute/fail_on_error)= +### Error Reporting: Warning vs. Failure +When an error occurs in a context where `allow_errors=False`, the default behavoir is for this to be reported as a warning. This warning will simply be logged and not cause the build to fail unless `sphinx-build` is run with the [`-W` option](https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-W). + +If you would like unexpected execution errors to cause a build failure rather than a warning regardless of the `-W` option, you can achieve this by setting `execution_fail_on_error=True` in your `conf.py`. + (execute/statistics)= ## Execution statistics diff --git a/docs/use/start.md b/docs/use/start.md index f4745fcb..01250dd8 100644 --- a/docs/use/start.md +++ b/docs/use/start.md @@ -77,6 +77,10 @@ Firstly for execution: - `False` - If `False`, when a code cell raises an error the execution is stopped, if `True` then all cells are always run. This can also be overridden by metadata in a notebook, [see here](execute/allow_errors) for details. +* - `execution_fail_on_error` + - `False` + - If `False`, disallowed errors will result in a sphinx warnings. + If `True`, then disallowed errors result in a failed build. [See here](execute/fail_on_error) for details. * - `execution_timeout` - 30 - The maximum time (in seconds) each notebook cell is allowed to run. diff --git a/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb b/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb index 7c20d877..2278726f 100644 --- a/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb +++ b/tests/test_execute/test_execution_fail_on_error_allow_errors.ipynb @@ -53,4 +53,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} \ No newline at end of file +} From 443ebc524f7ee0309b427f2871b84a791294b7ca Mon Sep 17 00:00:00 2001 From: Jake VanderPlas <jakevdp@google.com> Date: Thu, 28 Jan 2021 14:20:11 -0800 Subject: [PATCH 7/8] Include filepath in execution error message --- myst_nb/execution.py | 4 +++- tests/test_execute.py | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/myst_nb/execution.py b/myst_nb/execution.py index 994d7bc1..23ec5cb7 100644 --- a/myst_nb/execution.py +++ b/myst_nb/execution.py @@ -150,7 +150,9 @@ def generate_notebook_outputs( report_path = None if result.err: if env.config["execution_fail_on_error"]: - raise ExecutionError(str(result.err)) + raise ExecutionError( + f"Execution failed for file: {file_path}\n{str(result.err)}" + ) else: report_path, message = _report_exec_fail( env, diff --git a/tests/test_execute.py b/tests/test_execute.py index 51ae64be..341a323e 100644 --- a/tests/test_execute.py +++ b/tests/test_execute.py @@ -311,11 +311,11 @@ def test_custom_convert_cache(sphinx_run, file_regression, check_nbs): def test_execution_fail_on_error(sphinx_run, file_regression, check_nbs): with pytest.raises(ExecutionError) as excinfo: sphinx_run.build() - assert str(excinfo.value).startswith( - "An error occurred while executing the following cell:" - ) - cell_contents = "------------------\nraise Exception('oopsie!')\n------------------" - assert cell_contents in str(excinfo.value) + assert str(excinfo.value).startswith("Execution failed for file:") + # Ensure filename is reported: + assert "basic_failing.ipynb" in str(excinfo.value) + # Ensure failing code is reported: + assert "raise Exception('oopsie!')" in str(excinfo.value) @pytest.mark.sphinx_params( From 9607206002ab492c2bac0a52efb0d3e4a4805ea5 Mon Sep 17 00:00:00 2001 From: Jake VanderPlas <jakevdp@google.com> Date: Fri, 29 Jan 2021 11:38:22 -0800 Subject: [PATCH 8/8] fix typo --- docs/use/execute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/use/execute.md b/docs/use/execute.md index 6cdecdce..c18cdc70 100644 --- a/docs/use/execute.md +++ b/docs/use/execute.md @@ -163,7 +163,7 @@ print(thisvariabledoesntexist) (execute/fail_on_error)= ### Error Reporting: Warning vs. Failure -When an error occurs in a context where `allow_errors=False`, the default behavoir is for this to be reported as a warning. This warning will simply be logged and not cause the build to fail unless `sphinx-build` is run with the [`-W` option](https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-W). +When an error occurs in a context where `allow_errors=False`, the default behaviour is for this to be reported as a warning. This warning will simply be logged and not cause the build to fail unless `sphinx-build` is run with the [`-W` option](https://www.sphinx-doc.org/en/master/man/sphinx-build.html#cmdoption-sphinx-build-W). If you would like unexpected execution errors to cause a build failure rather than a warning regardless of the `-W` option, you can achieve this by setting `execution_fail_on_error=True` in your `conf.py`.