diff --git a/IPython/html/static/notebook/js/codecell.js b/IPython/html/static/notebook/js/codecell.js
index 1404956..527b9ca 100644
--- a/IPython/html/static/notebook/js/codecell.js
+++ b/IPython/html/static/notebook/js/codecell.js
@@ -403,14 +403,18 @@ define([
* Execute current code cell to the kernel
* @method execute
*/
- CodeCell.prototype.execute = function () {
+ CodeCell.prototype.execute = function (skip_exceptions) {
if (!this.kernel || !this.kernel.is_connected()) {
console.log("Can't execute, kernel is not connected.");
return;
}
this.active_output_area.clear_output(false, true);
-
+
+ if (skip_exceptions === undefined) {
+ skip_exceptions = false;
+ }
+
// Clear widget area
for (var i = 0; i < this.widget_views.length; i++) {
var view = this.widget_views[i];
@@ -434,7 +438,8 @@ define([
var callbacks = this.get_callbacks();
var old_msg_id = this.last_msg_id;
- this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
+ this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true,
+ skip_exceptions : skip_exceptions});
if (old_msg_id) {
delete CodeCell.msg_cells[old_msg_id];
}
diff --git a/IPython/html/tests/notebook/execute_code.js b/IPython/html/tests/notebook/execute_code.js
index 8374fc1..ca80b9f 100644
--- a/IPython/html/tests/notebook/execute_code.js
+++ b/IPython/html/tests/notebook/execute_code.js
@@ -75,4 +75,39 @@ casper.notebook_test(function () {
var result = this.get_output_cell(0);
this.test.assertEquals(result.text, '13\n', 'cell execute (using "play" toolbar button)')
});
+
+ // run code with skip_exception
+ this.thenEvaluate(function () {
+ var cell0 = IPython.notebook.get_cell(0);
+ cell0.set_text('raise IOError');
+ IPython.notebook.insert_cell_below('code',0);
+ var cell1 = IPython.notebook.get_cell(1);
+ cell1.set_text('a=14; print(a)');
+ cell0.execute(skip_exception=true);
+ cell1.execute();
+ });
+
+ this.wait_for_output(1);
+
+ this.then(function () {
+ var result = this.get_output_cell(1);
+ this.test.assertEquals(result.text, '14\n', 'cell execute, skip exceptions');
+ });
+
+ this.thenEvaluate(function () {
+ var cell0 = IPython.notebook.get_cell(0);
+ cell0.set_text('raise IOError');
+ IPython.notebook.insert_cell_below('code',0);
+ var cell1 = IPython.notebook.get_cell(1);
+ cell1.set_text('a=14; print(a)');
+ cell0.execute();
+ cell1.execute();
+ });
+
+ this.wait_for_output(1);
+
+ this.then(function () {
+ var result = this.get_output_cell(1);
+ this.test.assertNotEquals(result.text, '14\n', 'cell execute, skip exceptions');
+ });
});
diff --git a/IPython/kernel/client.py b/IPython/kernel/client.py
index 16d5e2f..5977f3f 100644
--- a/IPython/kernel/client.py
+++ b/IPython/kernel/client.py
@@ -196,7 +196,7 @@ class KernelClient(ConnectionFileMixin):
# Methods to send specific messages on channels
def execute(self, code, silent=False, store_history=True,
- user_expressions=None, allow_stdin=None):
+ user_expressions=None, allow_stdin=None, skip_exceptions=False):
"""Execute code in the kernel.
Parameters
@@ -224,6 +224,9 @@ class KernelClient(ConnectionFileMixin):
If raw_input is called from code executed from such a frontend, a
StdinNotImplementedError will be raised.
+ skip_exceptions: bool, optional (default False)
+ Flag whether to abort the execution queue, if an exception is encountered.
+
Returns
-------
The msg_id of the message sent.
@@ -243,7 +246,7 @@ class KernelClient(ConnectionFileMixin):
# not in Session.
content = dict(code=code, silent=silent, store_history=store_history,
user_expressions=user_expressions,
- allow_stdin=allow_stdin,
+ allow_stdin=allow_stdin, skip_exceptions=skip_exceptions
)
msg = self.session.msg('execute_request', content)
self.shell_channel.send(msg)
diff --git a/IPython/kernel/tests/test_message_spec.py b/IPython/kernel/tests/test_message_spec.py
index a2a6d3f..b3fad59 100644
--- a/IPython/kernel/tests/test_message_spec.py
+++ b/IPython/kernel/tests/test_message_spec.py
@@ -303,6 +303,24 @@ def test_execute_inc():
count_2 = reply['execution_count']
nt.assert_equal(count_2, count+1)
+def test_execute_skip_exceptions():
+ """execute request should not abort execution queue with skip_exceptions"""
+ flush_channels()
+
+ KC.execute(code='raise IOError')
+ msg_id = KC.execute(code='print("Hallo")')
+ KC.get_shell_msg(timeout=TIMEOUT)
+ reply = KC.get_shell_msg(timeout=TIMEOUT)
+ nt.assert_equal(reply['content']['status'], 'aborted')
+
+ flush_channels()
+
+ KC.execute(code='raise IOError', skip_exceptions=True)
+ msg_id = KC.execute(code='print("Hallo")')
+ KC.get_shell_msg(timeout=TIMEOUT)
+ reply = KC.get_shell_msg(timeout=TIMEOUT)
+ nt.assert_equal(reply['content']['status'], 'ok')
+
def test_user_expressions():
flush_channels()
diff --git a/IPython/kernel/zmq/kernelbase.py b/IPython/kernel/zmq/kernelbase.py
index 396d1d5..10e4d52 100755
--- a/IPython/kernel/zmq/kernelbase.py
+++ b/IPython/kernel/zmq/kernelbase.py
@@ -348,6 +348,11 @@ class Kernel(SingletonConfigurable):
self.log.error("%s", parent)
return
+ if u'skip_exceptions' in content and content[u'skip_exceptions'] is True:
+ skip_exceptions = True
+ else:
+ skip_exceptions = False
+
md = self._make_metadata(parent['metadata'])
# Re-broadcast our input for the benefit of listening clients, and
@@ -382,11 +387,11 @@ class Kernel(SingletonConfigurable):
self.log.debug("%s", reply_msg)
- if not silent and reply_msg['content']['status'] == u'error':
+ if not silent and reply_msg['content']['status'] == u'error' and not skip_exceptions:
self._abort_queues()
def do_execute(self, code, silent, store_history=True,
- user_experssions=None, allow_stdin=False):
+ user_expressions=None, allow_stdin=False):
"""Execute user code. Must be overridden by subclasses.
"""
raise NotImplementedError
diff --git a/docs/source/development/messaging.rst b/docs/source/development/messaging.rst
index 4c33ce4..4a50898 100644
--- a/docs/source/development/messaging.rst
+++ b/docs/source/development/messaging.rst
@@ -281,6 +281,10 @@ Message type: ``execute_request``::
# If raw_input is called from code executed from such a frontend,
# a StdinNotImplementedError will be raised.
'allow_stdin' : True,
+
+ # A boolean flag, which, if True, does not abort the execution queue, if an exception is encountered.
+ # This allows the queued execution of multiple execute_requests, even if they generate exceptions.
+ 'skip_exceptions' : True,
}
.. versionchanged:: 5.0