From 29646f33db7288d3352884487735bef28fbc225d Mon Sep 17 00:00:00 2001 From: "Maarten A. Breddels" Date: Mon, 2 Nov 2020 13:00:36 +0100 Subject: [PATCH] feat: allow showing of stacktraces in server extension mode Closes #751 This is a followup of #630 --- tests/app/show_traceback_test.py | 23 +++++++++++++++++++++++ tests/app/syntax_error_test.py | 9 ++------- tests/conftest.py | 5 +++++ tests/server/show_traceback_test.py | 23 +++++++++++++++++++++++ voila/app.py | 12 ++++++------ voila/configuration.py | 4 ++++ voila/execute.py | 10 +++++++--- voila/handler.py | 3 ++- 8 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 tests/app/show_traceback_test.py create mode 100644 tests/server/show_traceback_test.py diff --git a/tests/app/show_traceback_test.py b/tests/app/show_traceback_test.py new file mode 100644 index 000000000..23d6290b0 --- /dev/null +++ b/tests/app/show_traceback_test.py @@ -0,0 +1,23 @@ +import pytest + + +@pytest.fixture(params=[True, False]) +def show_tracebacks(request): + return request.param + + +@pytest.fixture +def voila_args(notebook_directory, voila_args_extra, show_tracebacks): + return ['--VoilaTest.root_dir=%r' % notebook_directory, f'--VoilaConfiguration.show_tracebacks={show_tracebacks}'] + voila_args_extra + + +async def test_syntax_error(http_server_client, syntax_error_notebook_url, show_tracebacks): + response = await http_server_client.fetch(syntax_error_notebook_url) + assert response.code == 200 + output = response.body.decode('utf-8') + if show_tracebacks: + assert 'this is a syntax error' in output, 'should show the "code"' + assert 'SyntaxError' in output and 'invalid syntax' in output, "should show the error" + else: + assert 'There was an error when executing cell' in output + assert 'This should not be executed' not in output diff --git a/tests/app/syntax_error_test.py b/tests/app/syntax_error_test.py index 77da5a84b..dba15689b 100644 --- a/tests/app/syntax_error_test.py +++ b/tests/app/syntax_error_test.py @@ -1,18 +1,13 @@ import pytest -@pytest.fixture -def syntax_error_notebook(base_url): - return base_url + "voila/render/syntax_error.ipynb" - - @pytest.fixture def voila_args(notebook_directory, voila_args_extra): return ['--VoilaTest.root_dir=%r' % notebook_directory] + voila_args_extra -async def test_syntax_error(capsys, http_server_client, syntax_error_notebook): - response = await http_server_client.fetch(syntax_error_notebook) +async def test_syntax_error(capsys, http_server_client, syntax_error_notebook_url): + response = await http_server_client.fetch(syntax_error_notebook_url) assert response.code == 200 output = response.body.decode('utf-8') assert 'There was an error when executing cell' in output diff --git a/tests/conftest.py b/tests/conftest.py index d19e0e3ac..5b343e941 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,11 @@ def print_notebook_url(base_url): return base_url + "voila/render/print.ipynb" +@pytest.fixture +def syntax_error_notebook_url(base_url): + return base_url + "voila/render/syntax_error.ipynb" + + @pytest.fixture def voila_notebook(notebook_directory): return os.path.join(notebook_directory, 'print.ipynb') diff --git a/tests/server/show_traceback_test.py b/tests/server/show_traceback_test.py new file mode 100644 index 000000000..f63237ffb --- /dev/null +++ b/tests/server/show_traceback_test.py @@ -0,0 +1,23 @@ +import pytest + + +@pytest.fixture(params=[True, False]) +def show_tracebacks(request): + return request.param + + +@pytest.fixture +def jupyter_server_args_extra(show_tracebacks): + return [f'--VoilaConfiguration.show_tracebacks={show_tracebacks}'] + + +async def test_syntax_error(http_server_client, syntax_error_notebook_url, show_tracebacks): + response = await http_server_client.fetch(syntax_error_notebook_url) + assert response.code == 200 + output = response.body.decode('utf-8') + if show_tracebacks: + assert 'this is a syntax error' in output, 'should show the "code"' + assert 'SyntaxError' in output and 'invalid syntax' in output, "should show the error" + else: + assert 'There was an error when executing cell' in output + assert 'This should not be executed' not in output diff --git a/voila/app.py b/voila/app.py index a3bcb911f..a32983471 100644 --- a/voila/app.py +++ b/voila/app.py @@ -75,7 +75,10 @@ class Voila(Application): flags = { 'debug': ( - {'Voila': {'log_level': logging.DEBUG, 'show_tracebacks': True}}, + { + 'Voila': {'log_level': logging.DEBUG}, + 'VoilaConfiguration': {'show_tracebacks': True}, + }, _("Set the log level to logging.DEBUG, and show exception tracebacks in output.") ), 'no-browser': ({'Voila': {'open_browser': False}}, _('Don\'t open the notebook in a browser after startup.')) @@ -125,7 +128,8 @@ class Voila(Application): 'theme': 'VoilaConfiguration.theme', 'base_url': 'Voila.base_url', 'server_url': 'Voila.server_url', - 'enable_nbextensions': 'VoilaConfiguration.enable_nbextensions' + 'enable_nbextensions': 'VoilaConfiguration.enable_nbextensions', + 'show_tracebacks': 'VoilaConfiguration.show_tracebacks', } classes = [ VoilaConfiguration, @@ -187,10 +191,6 @@ class Voila(Application): ) ) - show_tracebacks = Bool(False, config=True, help=_( - 'Whether to send tracebacks to clients on exceptions.' - )) - port_retries = Integer(50, config=True, help=_("The number of additional ports to try if the specified port is not available.") ) diff --git a/voila/configuration.py b/voila/configuration.py index 2c65120b8..db4ccb9d5 100644 --- a/voila/configuration.py +++ b/voila/configuration.py @@ -81,3 +81,7 @@ class VoilaConfiguration(traitlets.config.Configurable): When a cell takes a long time to execute, the http connection can timeout (possibly because of a proxy). Voila sends a 'heartbeat' message after the timeout is passed to keep the http connection alive. """).tag(config=True) + + show_tracebacks = Bool(False, config=True, help=( + 'Whether to send tracebacks to clients on exceptions.' + )) diff --git a/voila/execute.py b/voila/execute.py index 5184c7869..eef4a00ab 100644 --- a/voila/execute.py +++ b/voila/execute.py @@ -11,7 +11,7 @@ from nbclient.exceptions import CellExecutionError from nbclient import NotebookClient -from traitlets import Unicode +from traitlets import Bool, Unicode def strip_code_cell_warnings(cell): @@ -32,7 +32,7 @@ def strip_code_cell_warnings(cell): class VoilaExecutor(NotebookClient): """Execute, but respect the output widget behaviour""" cell_error_instruction = Unicode( - 'Please run VoilĂ  with --debug to see the error message.', + 'Please run VoilĂ  with --show_tracebacks=True or --debug to see the error message, or configure VoilaConfigurion.show_tracebacks.', config=True, help=( 'instruction given to user to debug cell errors' @@ -47,6 +47,10 @@ class VoilaExecutor(NotebookClient): ) ) + show_tracebacks = Bool(False, config=True, help=( + 'Whether to send tracebacks to clients on exceptions.' + )) + def execute(self, nb, resources, km=None): try: result = super(VoilaExecutor, self).execute() @@ -77,7 +81,7 @@ async def execute_cell(self, cell, resources, cell_index, store_history=True): def should_strip_error(self): """Return True if errors should be stripped from the Notebook, False otherwise, depending on the current config.""" - return 'Voila' not in self.config or not self.config['Voila'].get('show_tracebacks', False) + return not self.show_tracebacks def strip_notebook_errors(self, nb): """Strip error messages and traceback from a Notebook.""" diff --git a/voila/handler.py b/voila/handler.py index 6179fedfc..97ad7bb40 100644 --- a/voila/handler.py +++ b/voila/handler.py @@ -170,7 +170,8 @@ async def _jinja_kernel_start(self, nb): )) km = self.kernel_manager.get_kernel(kernel_id) - self.executor = VoilaExecutor(nb, km=km, config=self.traitlet_config) + self.executor = VoilaExecutor(nb, km=km, config=self.traitlet_config, + show_tracebacks=self.voila_configuration.show_tracebacks) ### # start kernel client