diff --git a/nbconvert/nbconvertapp.py b/nbconvert/nbconvertapp.py index 399f47989..166aad06f 100755 --- a/nbconvert/nbconvertapp.py +++ b/nbconvert/nbconvertapp.py @@ -72,6 +72,13 @@ def validate(self, obj, value): "(the default behaviour is to abort conversion). This flag " "is only relevant if '--execute' was specified, too.") ), + 'save-on-error' : ( + {'ExecutePreprocessor' : {'save_on_error' : True}}, + ("Saves the notebook even if one cell throws " + "an error. The error message is shown in the cell output." + "(the default behaviour is to not save the notebook on an error). This flag " + "is only relevant if '--execute' was specified, too.") + ), 'stdin' : ( {'NbConvertApp' : { 'from_stdin' : True, diff --git a/nbconvert/preprocessors/execute.py b/nbconvert/preprocessors/execute.py index 8768328c1..80ce72fc6 100644 --- a/nbconvert/preprocessors/execute.py +++ b/nbconvert/preprocessors/execute.py @@ -146,6 +146,15 @@ class ExecutePreprocessor(Preprocessor): ) ).tag(config=True) + save_on_error = Bool(False, + help=dedent( + """ + If `False` (default), when a cell raises an error no output is saved. + If `True`, the output of the notebook will be saved when an error occors. + """ + ) + ).tag(config=True) + extra_arguments = List(Unicode()) kernel_name = Unicode('', @@ -276,6 +285,12 @@ def start_new_kernel(startup_timeout=60, kernel_name='python', **kwargs): try: nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources) + except CellExecutionError: + if self.save_on_error: + pass + else: + raise + finally: self.kc.stop_channels() self.km.shutdown_kernel(now=self.shutdown_kernel == 'immediate') diff --git a/nbconvert/preprocessors/tests/test_execute.py b/nbconvert/preprocessors/tests/test_execute.py index f6657fff9..ca66ab293 100644 --- a/nbconvert/preprocessors/tests/test_execute.py +++ b/nbconvert/preprocessors/tests/test_execute.py @@ -211,6 +211,16 @@ def test_allow_errors(self): else: assert u"# üñîçø∂é".encode('utf8', 'replace') in str(exc.value) + def test_save_on_error(self): + """ + Check that conversion halts if ``allow_errors`` is False. + """ + current_dir = os.path.dirname(__file__) + filename = os.path.join(current_dir, 'files', 'Skip Exceptions.ipynb') + res = self.build_resources() + res['metadata']['path'] = os.path.dirname(filename) + self.run_notebook(filename, dict(save_on_error=True), res) + def test_force_raise_errors(self): """ Check that conversion halts if the ``force_raise_errors`` traitlet on