##// END OF EJS Templates
update execute preprocessor for msg spec 5
MinRK -
Show More
@@ -1,120 +1,162 b''
1 1 """Module containing a preprocessor that removes the outputs from code cells"""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 #-----------------------------------------------------------------------------
7 # Imports
8 #-----------------------------------------------------------------------------
9
10 6 import os
11 7 import sys
12 8
13 9 try:
14 10 from queue import Empty # Py 3
15 11 except ImportError:
16 12 from Queue import Empty # Py 2
17 13
18 from IPython.kernel import KernelManager
19 from IPython.nbformat.current import reads, NotebookNode, writes
14 from IPython.utils.traitlets import List, Unicode
20 15
16 from IPython.nbformat.current import reads, NotebookNode, writes
21 17 from .base import Preprocessor
22 18
19 # default timeout for reply and output: 10s
20 TIMEOUT = 10
23 21
24 #-----------------------------------------------------------------------------
25 # Classes
26 #-----------------------------------------------------------------------------
27 22 class ExecutePreprocessor(Preprocessor):
28 23 """
29 24 Executes all the cells in a notebook
30 25 """
31 def __init__(self, extra_arguments=[], **kwargs):
32 """
33 Start an kernel to run the Python code
34 """
35 super(ExecutePreprocessor, self).__init__(**kwargs)
36 self.extra_arguments = []
37
26
27 # FIXME: to be removed with nbformat v4
28 # map msg_type to v3 output_type
29 msg_type_map = {
30 "error" : "pyerr",
31 "execute_result" : "pyout",
32 }
33
34 # FIXME: to be removed with nbformat v4
35 # map mime-type to v3 mime-type keys
36 mime_map = {
37 "text/plain" : "text",
38 "text/html" : "html",
39 "image/svg+xml" : "svg",
40 "image/png" : "png",
41 "image/jpeg" : "jpeg",
42 "text/latex" : "latex",
43 "application/json" : "json",
44 "application/javascript" : "javascript",
45 }
46
47 extra_arguments = List(Unicode)
48
38 49 def _create_client(self):
50 from IPython.kernel import KernelManager
39 51 self.km = KernelManager()
40 52 self.km.start_kernel(extra_arguments=self.extra_arguments, stderr=open(os.devnull, 'w'))
41 53 self.kc = self.km.client()
42 54 self.kc.start_channels()
55 self.log.debug('kc.start_channels: %s', self.kc.session.session)
43 56 self.iopub = self.kc.iopub_channel
44 57 self.shell = self.kc.shell_channel
45 58 self.shell.kernel_info()
59 try:
60 self.shell.get_msg(timeout=TIMEOUT)
61 except Empty:
62 self.log.error("Timeout waiting for kernel_info reply")
63 raise
64 try:
65 self.iopub.get_msg(timeout=TIMEOUT)
66 except Empty:
67 self.log.warn("Timeout waiting for IOPub on startup")
46 68
47 69 def _shutdown_client(self):
48 70 self.kc.stop_channels()
49 71 self.km.shutdown_kernel()
50 72 del self.km
51 73
52 74 def preprocess(self, nb, resources):
53 75 self._create_client()
54 76 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
55 77 self._shutdown_client()
56 78 return nb, resources
57 79
58 80 def preprocess_cell(self, cell, resources, cell_index):
59 81 """
60 82 Apply a transformation on each code cell. See base.py for details.
61 83 """
62 84 if cell.cell_type != 'code':
63 85 return cell, resources
64 86 try:
65 87 outputs = self.run_cell(self.shell, self.iopub, cell)
66 88 except Exception as e:
67 89 self.log.error("failed to run cell: " + repr(e))
68 90 self.log.error(str(cell.input))
69 sys.exit(1)
91 raise
70 92 cell.outputs = outputs
71 93 return cell, resources
72 94
73 @staticmethod
74 def run_cell(shell, iopub, cell):
75 # print cell.input
76 shell.execute(cell.input)
77 # wait for finish, maximum 20s
78 shell.get_msg(timeout=20)
95 def run_cell(self, shell, iopub, cell):
96 msg_id = shell.execute(cell.input)
97 self.log.debug("Executing cell:\n%s", cell.input)
98 # wait for finish, with timeout
99 while True:
100 try:
101 msg = shell.get_msg(timeout=TIMEOUT)
102 except Empty:
103 self.log.error("Timeout waiting for execute reply")
104 raise
105 if msg['parent_header'].get('msg_id') == msg_id:
106 break
107 else:
108 # not our reply
109 continue
110
79 111 outs = []
80 112
81 113 while True:
82 114 try:
83 msg = iopub.get_msg(timeout=0.2)
115 msg = iopub.get_msg(timeout=TIMEOUT)
84 116 except Empty:
117 self.log.warn("Timeout waiting for IOPub output")
85 118 break
119 if msg['parent_header'].get('msg_id') != msg_id:
120 # not an output from our execution
121 continue
86 122
87 123 msg_type = msg['msg_type']
124 self.log.debug("output: %s", msg_type)
88 125 content = msg['content']
89 out = NotebookNode(output_type=msg_type)
126 if msg_type == 'status':
127 if content['execution_state'] == 'idle':
128 break
129 else:
130 continue
131 elif msg_type in {'execute_input', 'pyin'}:
132 continue
133 elif msg_type == 'clear_output':
134 outs = []
135 continue
136
137 out = NotebookNode(output_type=self.msg_type_map.get(msg_type, msg_type))
90 138
91 139 # set the prompt number for the input and the output
92 140 if 'execution_count' in content:
93 141 cell['prompt_number'] = content['execution_count']
94 142 out.prompt_number = content['execution_count']
95 143
96 if msg_type in ('status', 'pyin'):
97 continue
98 elif msg_type == 'clear_output':
99 outs = []
100 continue
101
102 144 if msg_type == 'stream':
103 145 out.stream = content['name']
104 146 out.text = content['data']
105 elif msg_type in ('display_data', 'pyout'):
147 elif msg_type in ('display_data', 'execute_result'):
106 148 out['metadata'] = content['metadata']
107 for mime, data in content['data'].iteritems():
108 attr = mime.split('/')[-1].lower()
109 # this gets most right, but fix svg+html, plain
110 attr = attr.replace('+xml', '').replace('plain', 'text')
111 setattr(out, attr, data)
112 elif msg_type == 'pyerr':
149 for mime, data in content['data'].items():
150 # map mime-type keys to nbformat v3 keys
151 # this will be unnecessary in nbformat v4
152 key = self.mime_map.get(mime, mime)
153 out[key] = data
154 elif msg_type == 'error':
113 155 out.ename = content['ename']
114 156 out.evalue = content['evalue']
115 157 out.traceback = content['traceback']
116 158 else:
117 159 self.log.error("unhandled iopub msg: " + msg_type)
118 160
119 161 outs.append(out)
120 162 return outs
@@ -1,87 +1,86 b''
1 1 """
2 2 Module with tests for the execute preprocessor.
3 3 """
4 4
5 5 # Copyright (c) IPython Development Team.
6 6 # Distributed under the terms of the Modified BSD License.
7 7
8 8 import copy
9 9 import os
10 10 import re
11 11
12 12 from IPython.nbformat import current as nbformat
13 13
14 14 from .base import PreprocessorTestsBase
15 15 from ..execute import ExecutePreprocessor
16 16
17 17 from IPython.nbconvert.filters import strip_ansi
18 18
19 19
20 20 addr_pat = re.compile(r'0x[0-9a-f]{7,9}')
21 21
22
23 22 class TestExecute(PreprocessorTestsBase):
24 23 """Contains test functions for execute.py"""
25 24
26 25 @staticmethod
27 def normalize_cell(cell):
26 def normalize_output(output):
28 27 """
29 Normalizes cells for comparison.
28 Normalizes outputs for comparison.
30 29 """
31 cell = dict(cell)
32 if 'metadata' in cell:
33 del cell['metadata']
34 if 'text' in cell:
35 cell['text'] = re.sub(addr_pat, '<HEXADDR>', cell['text'])
36 if 'svg' in cell:
37 del cell['text']
38 if 'traceback' in cell:
30 output = dict(output)
31 if 'metadata' in output:
32 del output['metadata']
33 if 'text' in output:
34 output['text'] = re.sub(addr_pat, '<HEXADDR>', output['text'])
35 if 'svg' in output:
36 del output['text']
37 if 'traceback' in output:
39 38 tb = []
40 for line in cell['traceback']:
39 for line in output['traceback']:
41 40 tb.append(strip_ansi(line))
42 cell['traceback'] = tb
41 output['traceback'] = tb
43 42
44 return cell
43 return output
45 44
46 45
47 46 def assert_notebooks_equal(self, expected, actual):
48 47 expected_cells = expected['worksheets'][0]['cells']
49 48 actual_cells = actual['worksheets'][0]['cells']
50 49 assert len(expected_cells) == len(actual_cells)
51 50
52 51 for expected_cell, actual_cell in zip(expected_cells, actual_cells):
53 52 expected_outputs = expected_cell.get('outputs', [])
54 53 actual_outputs = actual_cell.get('outputs', [])
55 normalized_expected_outputs = list(map(self.normalize_cell, expected_outputs))
56 normalized_actual_outputs = list(map(self.normalize_cell, actual_outputs))
54 normalized_expected_outputs = list(map(self.normalize_output, expected_outputs))
55 normalized_actual_outputs = list(map(self.normalize_output, actual_outputs))
57 56 assert normalized_expected_outputs == normalized_actual_outputs
58 57
59 58
60 59 def build_preprocessor(self):
61 60 """Make an instance of a preprocessor"""
62 61 preprocessor = ExecutePreprocessor()
63 62 preprocessor.enabled = True
64 63 return preprocessor
65 64
66 65
67 66 def test_constructor(self):
68 67 """Can a ExecutePreprocessor be constructed?"""
69 68 self.build_preprocessor()
70 69
71 70
72 71 def test_run_notebooks(self):
73 72 """Runs a series of test notebooks and compares them to their actual output"""
74 73 current_dir = os.path.dirname(__file__)
75 74 input_files = os.listdir(os.path.join(current_dir, 'input'))
76 75 for filename in input_files:
77 76 if not filename.endswith(".ipynb"):
78 77 continue
79 78 with open(os.path.join(current_dir, 'input', filename)) as f:
80 79 input_nb = nbformat.read(f, 'ipynb')
81 80 with open(os.path.join(current_dir, 'expected', filename)) as f:
82 81 expected_nb = nbformat.read(f, 'ipynb')
83 82 res = self.build_resources()
84 83 preprocessor = self.build_preprocessor()
85 84 output_nb, _ = preprocessor(input_nb, res)
86 85 self.assert_notebooks_equal(output_nb, expected_nb)
87 86
General Comments 0
You need to be logged in to leave comments. Login now