execute.py
142 lines
| 4.8 KiB
| text/x-python
|
PythonLexer
|
r17074 | """Module containing a preprocessor that removes the outputs from code cells""" | ||
# Copyright (c) IPython Development Team. | ||||
# Distributed under the terms of the Modified BSD License. | ||||
import os | ||||
import sys | ||||
|
r17087 | try: | ||
from queue import Empty # Py 3 | ||||
except ImportError: | ||||
from Queue import Empty # Py 2 | ||||
|
r17092 | from IPython.utils.traitlets import List, Unicode | ||
|
r17074 | |||
|
r17092 | from IPython.nbformat.current import reads, NotebookNode, writes | ||
|
r17074 | from .base import Preprocessor | ||
|
r17094 | from IPython.utils.traitlets import Integer | ||
|
r17074 | |||
class ExecutePreprocessor(Preprocessor): | ||||
""" | ||||
Executes all the cells in a notebook | ||||
""" | ||||
|
r17092 | |||
|
r17094 | timeout = Integer(30, config=True, | ||
help="The time to wait (in seconds) for output from executions." | ||||
) | ||||
|
r17092 | # FIXME: to be removed with nbformat v4 | ||
# map msg_type to v3 output_type | ||||
msg_type_map = { | ||||
"error" : "pyerr", | ||||
"execute_result" : "pyout", | ||||
} | ||||
# FIXME: to be removed with nbformat v4 | ||||
# map mime-type to v3 mime-type keys | ||||
mime_map = { | ||||
"text/plain" : "text", | ||||
"text/html" : "html", | ||||
"image/svg+xml" : "svg", | ||||
"image/png" : "png", | ||||
"image/jpeg" : "jpeg", | ||||
"text/latex" : "latex", | ||||
"application/json" : "json", | ||||
"application/javascript" : "javascript", | ||||
} | ||||
extra_arguments = List(Unicode) | ||||
|
r17079 | |||
|
r17080 | def preprocess(self, nb, resources): | ||
|
r17822 | from IPython.kernel import run_kernel | ||
kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python') | ||||
with run_kernel(kernel_name=kernel_name, | ||||
extra_arguments=self.extra_arguments, | ||||
stderr=open(os.devnull, 'w')) as kc: | ||||
self.kc = kc | ||||
nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources) | ||||
|
r17079 | return nb, resources | ||
|
r17074 | def preprocess_cell(self, cell, resources, cell_index): | ||
""" | ||||
Apply a transformation on each code cell. See base.py for details. | ||||
""" | ||||
if cell.cell_type != 'code': | ||||
return cell, resources | ||||
try: | ||||
|
r17822 | outputs = self.run_cell(self.kc.shell_channel, self.kc.iopub_channel, cell) | ||
|
r17074 | except Exception as e: | ||
|
r17078 | self.log.error("failed to run cell: " + repr(e)) | ||
self.log.error(str(cell.input)) | ||||
|
r17092 | raise | ||
|
r17074 | cell.outputs = outputs | ||
return cell, resources | ||||
|
r17092 | def run_cell(self, shell, iopub, cell): | ||
msg_id = shell.execute(cell.input) | ||||
self.log.debug("Executing cell:\n%s", cell.input) | ||||
# wait for finish, with timeout | ||||
while True: | ||||
try: | ||||
|
r17094 | msg = shell.get_msg(timeout=self.timeout) | ||
|
r17092 | except Empty: | ||
self.log.error("Timeout waiting for execute reply") | ||||
raise | ||||
if msg['parent_header'].get('msg_id') == msg_id: | ||||
break | ||||
else: | ||||
# not our reply | ||||
continue | ||||
|
r17074 | outs = [] | ||
|
r17077 | |||
|
r17074 | while True: | ||
try: | ||||
|
r17094 | msg = iopub.get_msg(timeout=self.timeout) | ||
|
r17074 | except Empty: | ||
|
r17092 | self.log.warn("Timeout waiting for IOPub output") | ||
|
r17074 | break | ||
|
r17092 | if msg['parent_header'].get('msg_id') != msg_id: | ||
# not an output from our execution | ||||
continue | ||||
|
r17090 | |||
|
r17074 | msg_type = msg['msg_type'] | ||
|
r17092 | self.log.debug("output: %s", msg_type) | ||
|
r17090 | content = msg['content'] | ||
|
r17824 | out = NotebookNode(output_type=self.msg_type_map.get(msg_type, msg_type)) | ||
# set the prompt number for the input and the output | ||||
if 'execution_count' in content: | ||||
cell['prompt_number'] = content['execution_count'] | ||||
out.prompt_number = content['execution_count'] | ||||
|
r17092 | if msg_type == 'status': | ||
if content['execution_state'] == 'idle': | ||||
break | ||||
else: | ||||
continue | ||||
elif msg_type in {'execute_input', 'pyin'}: | ||||
continue | ||||
elif msg_type == 'clear_output': | ||||
outs = [] | ||||
continue | ||||
|
r17074 | if msg_type == 'stream': | ||
out.stream = content['name'] | ||||
out.text = content['data'] | ||||
|
r17092 | elif msg_type in ('display_data', 'execute_result'): | ||
|
r17074 | out['metadata'] = content['metadata'] | ||
|
r17092 | for mime, data in content['data'].items(): | ||
# map mime-type keys to nbformat v3 keys | ||||
# this will be unnecessary in nbformat v4 | ||||
key = self.mime_map.get(mime, mime) | ||||
out[key] = data | ||||
elif msg_type == 'error': | ||||
|
r17074 | out.ename = content['ename'] | ||
out.evalue = content['evalue'] | ||||
out.traceback = content['traceback'] | ||||
else: | ||||
|
r17078 | self.log.error("unhandled iopub msg: " + msg_type) | ||
|
r17077 | |||
|
r17074 | outs.append(out) | ||
return outs | ||||