Skip to content

Commit 8d5fc8c

Browse files
authored
Combined run_cell and execute_cell (#12)
1 parent 8fb970c commit 8d5fc8c

File tree

3 files changed

+112
-70
lines changed

3 files changed

+112
-70
lines changed

nbclient/client.py

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ def reset_execution_trackers(self):
263263
"""Resets any per-execution trackers.
264264
"""
265265
self.kc = None
266+
self.code_cells_executed = 0
266267
self._display_id_map = {}
267268
self.widget_state = {}
268269
self.widget_buffers = {}
@@ -342,7 +343,6 @@ def setup_kernel(self, **kwargs):
342343
self.kc.stop_channels()
343344
self.kc = None
344345

345-
# TODO: Remove non-kwarg arguments
346346
def execute(self, **kwargs):
347347
"""
348348
Executes each code cell.
@@ -357,7 +357,9 @@ def execute(self, **kwargs):
357357
with self.setup_kernel(**kwargs):
358358
self.log.info("Executing notebook with kernel: %s" % self.kernel_name)
359359
for index, cell in enumerate(self.nb.cells):
360-
self.execute_cell(cell, index)
360+
# Ignore `'execution_count' in content` as it's always 1
361+
# when store_history is False
362+
self.execute_cell(cell, index, execution_count=self.code_cells_executed + 1)
361363
info_msg = self._wait_for_reply(self.kc.kernel_info())
362364
self.nb.metadata['language_info'] = info_msg['content']['language_info']
363365
self.set_widgets_metadata()
@@ -384,30 +386,6 @@ def set_widgets_metadata(self):
384386
if buffers:
385387
widget['buffers'] = buffers
386388

387-
def execute_cell(self, cell, cell_index, store_history=True):
388-
"""
389-
Executes a single code cell.
390-
391-
To execute all cells see :meth:`execute`.
392-
"""
393-
if cell.cell_type != 'code' or not cell.source.strip():
394-
return cell
395-
396-
reply, outputs = self.run_cell(cell, cell_index, store_history)
397-
# Backwards compatibility for processes that wrap run_cell
398-
cell.outputs = outputs
399-
400-
cell_allows_errors = self.allow_errors or "raises-exception" in cell.metadata.get(
401-
"tags", []
402-
)
403-
404-
if self.force_raise_errors or not cell_allows_errors:
405-
if (reply is not None) and reply['content']['status'] == 'error':
406-
raise CellExecutionError.from_cell_and_msg(cell, reply['content'])
407-
408-
self.nb['cells'][cell_index] = cell
409-
return cell
410-
411389
def _update_display_id(self, display_id, msg):
412390
"""Update outputs with a given display_id"""
413391
if display_id not in self._display_id_map:
@@ -499,11 +477,59 @@ def _passed_deadline(self, deadline):
499477
return True
500478
return False
501479

502-
def run_cell(self, cell, cell_index=0, store_history=False):
480+
def _check_raise_for_error(self, cell, exec_reply):
481+
cell_allows_errors = self.allow_errors or "raises-exception" in cell.metadata.get(
482+
"tags", []
483+
)
484+
485+
if self.force_raise_errors or not cell_allows_errors:
486+
if (exec_reply is not None) and exec_reply['content']['status'] == 'error':
487+
raise CellExecutionError.from_cell_and_msg(cell, exec_reply['content'])
488+
489+
def execute_cell(self, cell, cell_index, execution_count=None, store_history=True):
490+
"""
491+
Executes a single code cell.
492+
493+
To execute all cells see :meth:`execute`.
494+
495+
Parameters
496+
----------
497+
cell : nbformat.NotebookNode
498+
The cell which is currently being processed.
499+
cell_index : int
500+
The position of the cell within the notebook object.
501+
execution_count : int
502+
The execution count to be assigned to the cell (default: Use kernel response)
503+
store_history : bool
504+
Determines if history should be stored in the kernel (default: False).
505+
Specific to ipython kernels, which can store command histories.
506+
507+
Returns
508+
-------
509+
output : dict
510+
The execution output payload (or None for no output).
511+
512+
Raises
513+
------
514+
CellExecutionError
515+
If execution failed and should raise an exception, this will be raised
516+
with defaults about the failure.
517+
518+
Returns
519+
-------
520+
cell : NotebookNode
521+
The cell which was just processed.
522+
"""
523+
if cell.cell_type != 'code' or not cell.source.strip():
524+
self.log.debug("Skipping non-executing cell %s", cell_index)
525+
return cell
526+
527+
self.log.debug("Executing cell:\n%s", cell.source)
503528
parent_msg_id = self.kc.execute(
504529
cell.source, store_history=store_history, stop_on_error=not self.allow_errors
505530
)
506-
self.log.debug("Executing cell:\n%s", cell.source)
531+
# We launched a code cell to execute
532+
self.code_cells_executed += 1
507533
exec_timeout = self._get_timeout(cell)
508534
deadline = None
509535
if exec_timeout is not None:
@@ -570,8 +596,11 @@ def run_cell(self, cell, cell_index=0, store_history=False):
570596
except CellExecutionComplete:
571597
more_output = False
572598

573-
# Return cell.outputs still for backwards compatibility
574-
return exec_reply, cell.outputs
599+
if execution_count:
600+
cell['execution_count'] = execution_count
601+
self._check_raise_for_error(cell, exec_reply)
602+
self.nb['cells'][cell_index] = cell
603+
return cell
575604

576605
def process_message(self, msg, cell, cell_index):
577606
"""

nbclient/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class DeadKernelError(RuntimeError):
2323

2424
class CellExecutionComplete(Exception):
2525
"""
26-
Used as a control signal for cell execution across run_cell and
26+
Used as a control signal for cell execution across execute_cell and
2727
process_message function calls. Raised when all execution requests
2828
are completed and no further messages are expected from the kernel
2929
over zeromq channels.

0 commit comments

Comments
 (0)