def coalesce_streams(cell: NotebookNode, resources: dict, index: int) -> Tuple[NotebookNode, dict]: """Merge all stream outputs with shared names into single streams. This ensure deterministic outputs. Adapted from: https://github.com/computationalmodelling/nbval/blob/master/nbval/plugin.py. """ if "outputs" not in cell: return cell, resources new_outputs = [] streams = {} for output in cell.outputs: if output.output_type == "stream": if output.name in streams: streams[output.name].text += output.text else: new_outputs.append(output) streams[output.name] = output else: new_outputs.append(output) # process \r and \b characters for output in streams.values(): old = output.text while len(output.text) < len(old): old = output.text # Cancel out anything-but-newline followed by backspace output.text = RGX_BACKSPACE.sub("", output.text) # Replace all carriage returns not followed by newline output.text = RGX_CARRIAGERETURN.sub("", output.text) # We also want to ensure stdout and stderr are always in the same consecutive order, # because they are asynchronous, so order isn't guaranteed. for i, output in enumerate(new_outputs): if output.output_type == "stream" and output.name == "stderr": if (len(new_outputs) >= i + 2 and new_outputs[i + 1].output_type == "stream" and new_outputs[i + 1].name == "stdout"): stdout = new_outputs.pop(i + 1) new_outputs.insert(i, stdout) cell.outputs = new_outputs return cell, resources
def extract_glue_data_cell( cell: NotebookNode) -> list[tuple[str, NotebookNode]]: """Extract glue data from a single cell.""" outputs = [] data = [] for output in cell.get("outputs", []): meta = output.get("metadata", {}) if "scrapbook" not in meta: outputs.append(output) continue key = meta["scrapbook"]["name"] mime_prefix = len(meta["scrapbook"].get("mime_prefix", "")) output["data"] = { k[mime_prefix:]: v for k, v in output["data"].items() } data.append((key, output)) if not mime_prefix: # assume that the output is a displayable object outputs.append(output) cell.outputs = outputs return data
async def async_execute_cell( self, cell: nbformat.NotebookNode, cell_index: int, execution_count: t.Optional[int] = None, store_history: bool = True) -> nbformat.NotebookNode: """ Executes a single code cell. To execute all cells see :meth:`execute`. Parameters ---------- cell : nbformat.NotebookNode The cell which is currently being processed. cell_index : int The position of the cell within the notebook object. execution_count : int The execution count assigned to the cell (default: Use kernel response) store_history : bool Determines if history should be stored in the kernel (default: False). Specific to ipython kernels, which can store command histories. Raises ------ CellExecutionError If execution failed and should raise an exception, this will be raised with defaults about the failure. Returns ------- cell : NotebookNode The cell which was just processed. License ------- This project is licensed under the terms of the Modified BSD License (also known as New or Revised or 3-Clause BSD), as follows: - Copyright (c) 2020-, Jupyter Development Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Jupyter Development Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ assert self.kc is not None if cell.cell_type != 'code' or not cell.source.strip(): self.log.debug("Skipping non-executing cell %s", cell_index) return cell if self.record_timing and 'execution' not in cell['metadata']: cell['metadata']['execution'] = {} self.log.debug("Executing cell:\n%s", cell.source) parent_msg_id = await ensure_async( self.kc.execute(cell.source, store_history=store_history, stop_on_error=not self.allow_errors)) # We launched a code cell to execute self.code_cells_executed += 1 exec_timeout = self._get_timeout(cell) cell.outputs = [] self.clear_before_next_output = False task_poll_kernel_alive = asyncio.ensure_future( self._async_poll_kernel_alive()) task_poll_output_msg = asyncio.ensure_future( self._async_poll_output_msg(parent_msg_id, cell, cell_index)) self.task_poll_for_reply = asyncio.ensure_future( self._async_poll_for_reply( parent_msg_id, cell, exec_timeout, task_poll_output_msg, task_poll_kernel_alive, )) try: exec_reply = await self.task_poll_for_reply except asyncio.CancelledError: # can only be cancelled by task_poll_kernel_alive when the kernel is dead task_poll_output_msg.cancel() raise DeadKernelError("Kernel died") except Exception as e: # Best effort to cancel request if it hasn't been resolved try: # Check if the task_poll_output is doing the raising for us if not isinstance(e, CellControlSignal): task_poll_output_msg.cancel() finally: raise if execution_count: cell['execution_count'] = execution_count # -- NMA-specific code here -- # self._check_raise_for_error_nma(cell, exec_reply) self.nb['cells'][cell_index] = cell return cell
async def async_execute_cell( self, cell: NotebookNode, cell_index: int, execution_count: t.Optional[int] = None, store_history: bool = True, ) -> NotebookNode: """ Executes a single code cell. To execute all cells see :meth:`execute`. Parameters ---------- cell : nbformat.NotebookNode The cell which is currently being processed. cell_index : int The position of the cell within the notebook object. execution_count : int The execution count to be assigned to the cell (default: Use kernel response) store_history : bool Determines if history should be stored in the kernel (default: False). Specific to ipython kernels, which can store command histories. Returns ------- output : dict The execution output payload (or None for no output). Raises ------ CellExecutionError If execution failed and should raise an exception, this will be raised with defaults about the failure. Returns ------- cell : NotebookNode The cell which was just processed. """ assert self.kc is not None if cell.cell_type != 'code' or not cell.source.strip(): self.log.debug("Skipping non-executing cell %s", cell_index) return cell if self.skip_cells_with_tag in cell.metadata.get("tags", []): self.log.debug("Skipping tagged cell %s", cell_index) return cell if self.record_timing and 'execution' not in cell['metadata']: cell['metadata']['execution'] = {} self.log.debug("Executing cell:\n%s", cell.source) cell_allows_errors = (not self.force_raise_errors) and ( self.allow_errors or "raises-exception" in cell.metadata.get("tags", [])) parent_msg_id = await ensure_async( self.kc.execute(cell.source, store_history=store_history, stop_on_error=not cell_allows_errors)) # We launched a code cell to execute self.code_cells_executed += 1 exec_timeout = self._get_timeout(cell) cell.outputs = [] self.clear_before_next_output = False task_poll_kernel_alive = asyncio.ensure_future( self._async_poll_kernel_alive()) task_poll_output_msg = asyncio.ensure_future( self._async_poll_output_msg(parent_msg_id, cell, cell_index)) self.task_poll_for_reply = asyncio.ensure_future( self._async_poll_for_reply(parent_msg_id, cell, exec_timeout, task_poll_output_msg, task_poll_kernel_alive)) try: exec_reply = await self.task_poll_for_reply except asyncio.CancelledError: # can only be cancelled by task_poll_kernel_alive when the kernel is dead task_poll_output_msg.cancel() raise DeadKernelError("Kernel died") except Exception as e: # Best effort to cancel request if it hasn't been resolved try: # Check if the task_poll_output is doing the raising for us if not isinstance(e, CellControlSignal): task_poll_output_msg.cancel() finally: raise if execution_count: cell['execution_count'] = execution_count self._check_raise_for_error(cell, exec_reply) self.nb['cells'][cell_index] = cell return cell