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