##// END OF EJS Templates
Add option to interrupt kernel after execution timeout
Jessica B. Hamrick -
Show More
@@ -1,117 +1,141 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 from textwrap import dedent
7
8
8 try:
9 try:
9 from queue import Empty # Py 3
10 from queue import Empty # Py 3
10 except ImportError:
11 except ImportError:
11 from Queue import Empty # Py 2
12 from Queue import Empty # Py 2
12
13
13 from IPython.utils.traitlets import List, Unicode
14 from IPython.utils.traitlets import List, Unicode, Bool
14
15
15 from IPython.nbformat.v4 import output_from_msg
16 from IPython.nbformat.v4 import output_from_msg
16 from .base import Preprocessor
17 from .base import Preprocessor
17 from IPython.utils.traitlets import Integer
18 from IPython.utils.traitlets import Integer
18
19
19
20
20 class ExecutePreprocessor(Preprocessor):
21 class ExecutePreprocessor(Preprocessor):
21 """
22 """
22 Executes all the cells in a notebook
23 Executes all the cells in a notebook
23 """
24 """
24
25
25 timeout = Integer(30, config=True,
26 timeout = Integer(30, config=True,
26 help="The time to wait (in seconds) for output from executions."
27 help="The time to wait (in seconds) for output from executions."
27 )
28 )
29
30 interrupt_on_timeout = Bool(
31 False, config=True,
32 help=dedent(
33 """
34 If execution of a cell times out, interrupt the kernel and
35 continue executing other cells rather than throwing an error and
36 stopping.
37 """
38 )
39 )
28
40
29 extra_arguments = List(Unicode)
41 extra_arguments = List(Unicode)
30
42
31 def preprocess(self, nb, resources):
43 def preprocess(self, nb, resources):
32 path = resources.get('metadata', {}).get('path', '')
44 path = resources.get('metadata', {}).get('path', '')
33 if path == '':
45 if path == '':
34 path = None
46 path = None
35
47
36 from IPython.kernel import run_kernel
48 from IPython.kernel.manager import start_new_kernel
37 kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python')
49 kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python')
38 self.log.info("Executing notebook with kernel: %s" % kernel_name)
50 self.log.info("Executing notebook with kernel: %s" % kernel_name)
39 with run_kernel(kernel_name=kernel_name,
51 self.km, self.kc = start_new_kernel(
40 extra_arguments=self.extra_arguments,
52 kernel_name=kernel_name,
41 stderr=open(os.devnull, 'w'),
53 extra_arguments=self.extra_arguments,
42 cwd=path) as kc:
54 stderr=open(os.devnull, 'w'),
43 self.kc = kc
55 cwd=path)
44 self.kc.allow_stdin = False
56 self.kc.allow_stdin = False
57
58 try:
45 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
59 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
60 finally:
61 self.kc.stop_channels()
62 self.km.shutdown_kernel(now=True)
63
46 return nb, resources
64 return nb, resources
47
65
48 def preprocess_cell(self, cell, resources, cell_index):
66 def preprocess_cell(self, cell, resources, cell_index):
49 """
67 """
50 Apply a transformation on each code cell. See base.py for details.
68 Apply a transformation on each code cell. See base.py for details.
51 """
69 """
52 if cell.cell_type != 'code':
70 if cell.cell_type != 'code':
53 return cell, resources
71 return cell, resources
54 try:
72 try:
55 outputs = self.run_cell(cell)
73 outputs = self.run_cell(cell)
56 except Exception as e:
74 except Exception as e:
57 self.log.error("failed to run cell: " + repr(e))
75 self.log.error("failed to run cell: " + repr(e))
58 self.log.error(str(cell.source))
76 self.log.error(str(cell.source))
59 raise
77 raise
60 cell.outputs = outputs
78 cell.outputs = outputs
61 return cell, resources
79 return cell, resources
62
80
63 def run_cell(self, cell):
81 def run_cell(self, cell):
64 msg_id = self.kc.execute(cell.source)
82 msg_id = self.kc.execute(cell.source)
65 self.log.debug("Executing cell:\n%s", cell.source)
83 self.log.debug("Executing cell:\n%s", cell.source)
66 # wait for finish, with timeout
84 # wait for finish, with timeout
67 while True:
85 while True:
68 try:
86 try:
69 msg = self.kc.shell_channel.get_msg(timeout=self.timeout)
87 msg = self.kc.shell_channel.get_msg(timeout=self.timeout)
70 except Empty:
88 except Empty:
71 self.log.error("Timeout waiting for execute reply")
89 self.log.error("Timeout waiting for execute reply")
72 raise
90 if self.interrupt_on_timeout:
91 self.log.error("Interrupting kernel")
92 self.km.interrupt_kernel()
93 break
94 else:
95 raise
96
73 if msg['parent_header'].get('msg_id') == msg_id:
97 if msg['parent_header'].get('msg_id') == msg_id:
74 break
98 break
75 else:
99 else:
76 # not our reply
100 # not our reply
77 continue
101 continue
78
102
79 outs = []
103 outs = []
80
104
81 while True:
105 while True:
82 try:
106 try:
83 msg = self.kc.iopub_channel.get_msg(timeout=self.timeout)
107 msg = self.kc.iopub_channel.get_msg(timeout=self.timeout)
84 except Empty:
108 except Empty:
85 self.log.warn("Timeout waiting for IOPub output")
109 self.log.warn("Timeout waiting for IOPub output")
86 break
110 break
87 if msg['parent_header'].get('msg_id') != msg_id:
111 if msg['parent_header'].get('msg_id') != msg_id:
88 # not an output from our execution
112 # not an output from our execution
89 continue
113 continue
90
114
91 msg_type = msg['msg_type']
115 msg_type = msg['msg_type']
92 self.log.debug("output: %s", msg_type)
116 self.log.debug("output: %s", msg_type)
93 content = msg['content']
117 content = msg['content']
94
118
95 # set the prompt number for the input and the output
119 # set the prompt number for the input and the output
96 if 'execution_count' in content:
120 if 'execution_count' in content:
97 cell['execution_count'] = content['execution_count']
121 cell['execution_count'] = content['execution_count']
98
122
99 if msg_type == 'status':
123 if msg_type == 'status':
100 if content['execution_state'] == 'idle':
124 if content['execution_state'] == 'idle':
101 break
125 break
102 else:
126 else:
103 continue
127 continue
104 elif msg_type == 'execute_input':
128 elif msg_type == 'execute_input':
105 continue
129 continue
106 elif msg_type == 'clear_output':
130 elif msg_type == 'clear_output':
107 outs = []
131 outs = []
108 continue
132 continue
109
133
110 try:
134 try:
111 out = output_from_msg(msg)
135 out = output_from_msg(msg)
112 except ValueError:
136 except ValueError:
113 self.log.error("unhandled iopub msg: " + msg_type)
137 self.log.error("unhandled iopub msg: " + msg_type)
114 else:
138 else:
115 outs.append(out)
139 outs.append(out)
116
140
117 return outs
141 return outs
General Comments 0
You need to be logged in to leave comments. Login now