##// END OF EJS Templates
make execute preprocessor timeout configurable
MinRK -
Show More
@@ -1,162 +1,165 b''
1 """Module containing a preprocessor that removes the outputs from code cells"""
1 """Module containing a preprocessor that removes the outputs from code cells"""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import os
6 import os
7 import sys
7 import sys
8
8
9 try:
9 try:
10 from queue import Empty # Py 3
10 from queue import Empty # Py 3
11 except ImportError:
11 except ImportError:
12 from Queue import Empty # Py 2
12 from Queue import Empty # Py 2
13
13
14 from IPython.utils.traitlets import List, Unicode
14 from IPython.utils.traitlets import List, Unicode
15
15
16 from IPython.nbformat.current import reads, NotebookNode, writes
16 from IPython.nbformat.current import reads, NotebookNode, writes
17 from .base import Preprocessor
17 from .base import Preprocessor
18
18 from IPython.utils.traitlets import Integer
19 # default timeout for reply and output: 10s
20 TIMEOUT = 10
21
19
22 class ExecutePreprocessor(Preprocessor):
20 class ExecutePreprocessor(Preprocessor):
23 """
21 """
24 Executes all the cells in a notebook
22 Executes all the cells in a notebook
25 """
23 """
26
24
25 timeout = Integer(30, config=True,
26 help="The time to wait (in seconds) for output from executions."
27 )
27 # FIXME: to be removed with nbformat v4
28 # FIXME: to be removed with nbformat v4
28 # map msg_type to v3 output_type
29 # map msg_type to v3 output_type
29 msg_type_map = {
30 msg_type_map = {
30 "error" : "pyerr",
31 "error" : "pyerr",
31 "execute_result" : "pyout",
32 "execute_result" : "pyout",
32 }
33 }
33
34
34 # FIXME: to be removed with nbformat v4
35 # FIXME: to be removed with nbformat v4
35 # map mime-type to v3 mime-type keys
36 # map mime-type to v3 mime-type keys
36 mime_map = {
37 mime_map = {
37 "text/plain" : "text",
38 "text/plain" : "text",
38 "text/html" : "html",
39 "text/html" : "html",
39 "image/svg+xml" : "svg",
40 "image/svg+xml" : "svg",
40 "image/png" : "png",
41 "image/png" : "png",
41 "image/jpeg" : "jpeg",
42 "image/jpeg" : "jpeg",
42 "text/latex" : "latex",
43 "text/latex" : "latex",
43 "application/json" : "json",
44 "application/json" : "json",
44 "application/javascript" : "javascript",
45 "application/javascript" : "javascript",
45 }
46 }
46
47
47 extra_arguments = List(Unicode)
48 extra_arguments = List(Unicode)
48
49
49 def _create_client(self):
50 def _create_client(self):
50 from IPython.kernel import KernelManager
51 from IPython.kernel import KernelManager
51 self.km = KernelManager()
52 self.km = KernelManager()
52 self.km.start_kernel(extra_arguments=self.extra_arguments, stderr=open(os.devnull, 'w'))
53 self.km.write_connection_file()
53 self.kc = self.km.client()
54 self.kc = self.km.client()
54 self.kc.start_channels()
55 self.kc.start_channels()
55 self.log.debug('kc.start_channels: %s', self.kc.session.session)
56 self.km.start_kernel(extra_arguments=self.extra_arguments, stderr=open(os.devnull, 'w'))
56 self.iopub = self.kc.iopub_channel
57 self.iopub = self.kc.iopub_channel
57 self.shell = self.kc.shell_channel
58 self.shell = self.kc.shell_channel
58 self.shell.kernel_info()
59 self.shell.kernel_info()
59 try:
60 try:
60 self.shell.get_msg(timeout=TIMEOUT)
61 self.shell.get_msg(timeout=self.timeout)
61 except Empty:
62 except Empty:
62 self.log.error("Timeout waiting for kernel_info reply")
63 self.log.error("Timeout waiting for kernel_info reply")
63 raise
64 raise
64 try:
65 # flush IOPub
65 self.iopub.get_msg(timeout=TIMEOUT)
66 while True:
66 except Empty:
67 try:
67 self.log.warn("Timeout waiting for IOPub on startup")
68 self.iopub.get_msg(block=True, timeout=0.25)
69 except Empty:
70 break
68
71
69 def _shutdown_client(self):
72 def _shutdown_client(self):
70 self.kc.stop_channels()
73 self.kc.stop_channels()
71 self.km.shutdown_kernel()
74 self.km.shutdown_kernel()
72 del self.km
75 del self.km
73
76
74 def preprocess(self, nb, resources):
77 def preprocess(self, nb, resources):
75 self._create_client()
78 self._create_client()
76 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
79 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
77 self._shutdown_client()
80 self._shutdown_client()
78 return nb, resources
81 return nb, resources
79
82
80 def preprocess_cell(self, cell, resources, cell_index):
83 def preprocess_cell(self, cell, resources, cell_index):
81 """
84 """
82 Apply a transformation on each code cell. See base.py for details.
85 Apply a transformation on each code cell. See base.py for details.
83 """
86 """
84 if cell.cell_type != 'code':
87 if cell.cell_type != 'code':
85 return cell, resources
88 return cell, resources
86 try:
89 try:
87 outputs = self.run_cell(self.shell, self.iopub, cell)
90 outputs = self.run_cell(self.shell, self.iopub, cell)
88 except Exception as e:
91 except Exception as e:
89 self.log.error("failed to run cell: " + repr(e))
92 self.log.error("failed to run cell: " + repr(e))
90 self.log.error(str(cell.input))
93 self.log.error(str(cell.input))
91 raise
94 raise
92 cell.outputs = outputs
95 cell.outputs = outputs
93 return cell, resources
96 return cell, resources
94
97
95 def run_cell(self, shell, iopub, cell):
98 def run_cell(self, shell, iopub, cell):
96 msg_id = shell.execute(cell.input)
99 msg_id = shell.execute(cell.input)
97 self.log.debug("Executing cell:\n%s", cell.input)
100 self.log.debug("Executing cell:\n%s", cell.input)
98 # wait for finish, with timeout
101 # wait for finish, with timeout
99 while True:
102 while True:
100 try:
103 try:
101 msg = shell.get_msg(timeout=TIMEOUT)
104 msg = shell.get_msg(timeout=self.timeout)
102 except Empty:
105 except Empty:
103 self.log.error("Timeout waiting for execute reply")
106 self.log.error("Timeout waiting for execute reply")
104 raise
107 raise
105 if msg['parent_header'].get('msg_id') == msg_id:
108 if msg['parent_header'].get('msg_id') == msg_id:
106 break
109 break
107 else:
110 else:
108 # not our reply
111 # not our reply
109 continue
112 continue
110
113
111 outs = []
114 outs = []
112
115
113 while True:
116 while True:
114 try:
117 try:
115 msg = iopub.get_msg(timeout=TIMEOUT)
118 msg = iopub.get_msg(timeout=self.timeout)
116 except Empty:
119 except Empty:
117 self.log.warn("Timeout waiting for IOPub output")
120 self.log.warn("Timeout waiting for IOPub output")
118 break
121 break
119 if msg['parent_header'].get('msg_id') != msg_id:
122 if msg['parent_header'].get('msg_id') != msg_id:
120 # not an output from our execution
123 # not an output from our execution
121 continue
124 continue
122
125
123 msg_type = msg['msg_type']
126 msg_type = msg['msg_type']
124 self.log.debug("output: %s", msg_type)
127 self.log.debug("output: %s", msg_type)
125 content = msg['content']
128 content = msg['content']
126 if msg_type == 'status':
129 if msg_type == 'status':
127 if content['execution_state'] == 'idle':
130 if content['execution_state'] == 'idle':
128 break
131 break
129 else:
132 else:
130 continue
133 continue
131 elif msg_type in {'execute_input', 'pyin'}:
134 elif msg_type in {'execute_input', 'pyin'}:
132 continue
135 continue
133 elif msg_type == 'clear_output':
136 elif msg_type == 'clear_output':
134 outs = []
137 outs = []
135 continue
138 continue
136
139
137 out = NotebookNode(output_type=self.msg_type_map.get(msg_type, msg_type))
140 out = NotebookNode(output_type=self.msg_type_map.get(msg_type, msg_type))
138
141
139 # set the prompt number for the input and the output
142 # set the prompt number for the input and the output
140 if 'execution_count' in content:
143 if 'execution_count' in content:
141 cell['prompt_number'] = content['execution_count']
144 cell['prompt_number'] = content['execution_count']
142 out.prompt_number = content['execution_count']
145 out.prompt_number = content['execution_count']
143
146
144 if msg_type == 'stream':
147 if msg_type == 'stream':
145 out.stream = content['name']
148 out.stream = content['name']
146 out.text = content['data']
149 out.text = content['data']
147 elif msg_type in ('display_data', 'execute_result'):
150 elif msg_type in ('display_data', 'execute_result'):
148 out['metadata'] = content['metadata']
151 out['metadata'] = content['metadata']
149 for mime, data in content['data'].items():
152 for mime, data in content['data'].items():
150 # map mime-type keys to nbformat v3 keys
153 # map mime-type keys to nbformat v3 keys
151 # this will be unnecessary in nbformat v4
154 # this will be unnecessary in nbformat v4
152 key = self.mime_map.get(mime, mime)
155 key = self.mime_map.get(mime, mime)
153 out[key] = data
156 out[key] = data
154 elif msg_type == 'error':
157 elif msg_type == 'error':
155 out.ename = content['ename']
158 out.ename = content['ename']
156 out.evalue = content['evalue']
159 out.evalue = content['evalue']
157 out.traceback = content['traceback']
160 out.traceback = content['traceback']
158 else:
161 else:
159 self.log.error("unhandled iopub msg: " + msg_type)
162 self.log.error("unhandled iopub msg: " + msg_type)
160
163
161 outs.append(out)
164 outs.append(out)
162 return outs
165 return outs
General Comments 0
You need to be logged in to leave comments. Login now