##// END OF EJS Templates
Made is_alive a method of KernelManager and MultiKernelManager....
Brian E. Granger -
Show More
@@ -1,466 +1,466 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Frontend of ipython working with python-zmq
2 """Frontend of ipython working with python-zmq
3
3
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
5
5
6 For more details, see the ipython-zmq design
6 For more details, see the ipython-zmq design
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 from __future__ import print_function
18 from __future__ import print_function
19
19
20 import bdb
20 import bdb
21 import signal
21 import signal
22 import os
22 import os
23 import sys
23 import sys
24 import time
24 import time
25 import subprocess
25 import subprocess
26 from io import BytesIO
26 from io import BytesIO
27 import base64
27 import base64
28
28
29 from Queue import Empty
29 from Queue import Empty
30
30
31 try:
31 try:
32 from contextlib import nested
32 from contextlib import nested
33 except:
33 except:
34 from IPython.utils.nested_context import nested
34 from IPython.utils.nested_context import nested
35
35
36 from IPython.core.alias import AliasManager, AliasError
36 from IPython.core.alias import AliasManager, AliasError
37 from IPython.core import page
37 from IPython.core import page
38 from IPython.utils.warn import warn, error, fatal
38 from IPython.utils.warn import warn, error, fatal
39 from IPython.utils import io
39 from IPython.utils import io
40 from IPython.utils.traitlets import List, Enum, Any
40 from IPython.utils.traitlets import List, Enum, Any
41 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
41 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
42
42
43 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
43 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
44 from IPython.frontend.terminal.console.completer import ZMQCompleter
44 from IPython.frontend.terminal.console.completer import ZMQCompleter
45
45
46
46
47 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
47 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
48 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
48 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
49 _executing = False
49 _executing = False
50
50
51 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
51 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
52 config=True, help=
52 config=True, help=
53 """
53 """
54 Handler for image type output. This is useful, for example,
54 Handler for image type output. This is useful, for example,
55 when connecting to the kernel in which pylab inline backend is
55 when connecting to the kernel in which pylab inline backend is
56 activated. There are four handlers defined. 'PIL': Use
56 activated. There are four handlers defined. 'PIL': Use
57 Python Imaging Library to popup image; 'stream': Use an
57 Python Imaging Library to popup image; 'stream': Use an
58 external program to show the image. Image will be fed into
58 external program to show the image. Image will be fed into
59 the STDIN of the program. You will need to configure
59 the STDIN of the program. You will need to configure
60 `stream_image_handler`; 'tempfile': Use an external program to
60 `stream_image_handler`; 'tempfile': Use an external program to
61 show the image. Image will be saved in a temporally file and
61 show the image. Image will be saved in a temporally file and
62 the program is called with the temporally file. You will need
62 the program is called with the temporally file. You will need
63 to configure `tempfile_image_handler`; 'callable': You can set
63 to configure `tempfile_image_handler`; 'callable': You can set
64 any Python callable which is called with the image data. You
64 any Python callable which is called with the image data. You
65 will need to configure `callable_image_handler`.
65 will need to configure `callable_image_handler`.
66 """
66 """
67 )
67 )
68
68
69 stream_image_handler = List(config=True, help=
69 stream_image_handler = List(config=True, help=
70 """
70 """
71 Command to invoke an image viewer program when you are using
71 Command to invoke an image viewer program when you are using
72 'stream' image handler. This option is a list of string where
72 'stream' image handler. This option is a list of string where
73 the first element is the command itself and reminders are the
73 the first element is the command itself and reminders are the
74 options for the command. Raw image data is given as STDIN to
74 options for the command. Raw image data is given as STDIN to
75 the program.
75 the program.
76 """
76 """
77 )
77 )
78
78
79 tempfile_image_handler = List(config=True, help=
79 tempfile_image_handler = List(config=True, help=
80 """
80 """
81 Command to invoke an image viewer program when you are using
81 Command to invoke an image viewer program when you are using
82 'tempfile' image handler. This option is a list of string
82 'tempfile' image handler. This option is a list of string
83 where the first element is the command itself and reminders
83 where the first element is the command itself and reminders
84 are the options for the command. You can use {file} and
84 are the options for the command. You can use {file} and
85 {format} in the string to represent the location of the
85 {format} in the string to represent the location of the
86 generated image file and image format.
86 generated image file and image format.
87 """
87 """
88 )
88 )
89
89
90 callable_image_handler = Any(config=True, help=
90 callable_image_handler = Any(config=True, help=
91 """
91 """
92 Callable object called via 'callable' image handler with one
92 Callable object called via 'callable' image handler with one
93 argument, `data`, which is `msg["content"]["data"]` where
93 argument, `data`, which is `msg["content"]["data"]` where
94 `msg` is the message from iopub channel. For exmaple, you can
94 `msg` is the message from iopub channel. For exmaple, you can
95 find base64 encoded PNG data as `data['image/png']`.
95 find base64 encoded PNG data as `data['image/png']`.
96 """
96 """
97 )
97 )
98
98
99 mime_preference = List(
99 mime_preference = List(
100 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
100 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
101 config=True, allow_none=False, help=
101 config=True, allow_none=False, help=
102 """
102 """
103 Preferred object representation MIME type in order. First
103 Preferred object representation MIME type in order. First
104 matched MIME type will be used.
104 matched MIME type will be used.
105 """
105 """
106 )
106 )
107
107
108 def __init__(self, *args, **kwargs):
108 def __init__(self, *args, **kwargs):
109 self.km = kwargs.pop('kernel_manager')
109 self.km = kwargs.pop('kernel_manager')
110 self.session_id = self.km.session.session
110 self.session_id = self.km.session.session
111 super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
111 super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
112
112
113 def init_completer(self):
113 def init_completer(self):
114 """Initialize the completion machinery.
114 """Initialize the completion machinery.
115
115
116 This creates completion machinery that can be used by client code,
116 This creates completion machinery that can be used by client code,
117 either interactively in-process (typically triggered by the readline
117 either interactively in-process (typically triggered by the readline
118 library), programatically (such as in test suites) or out-of-prcess
118 library), programatically (such as in test suites) or out-of-prcess
119 (typically over the network by remote frontends).
119 (typically over the network by remote frontends).
120 """
120 """
121 from IPython.core.completerlib import (module_completer,
121 from IPython.core.completerlib import (module_completer,
122 magic_run_completer, cd_completer)
122 magic_run_completer, cd_completer)
123
123
124 self.Completer = ZMQCompleter(self, self.km)
124 self.Completer = ZMQCompleter(self, self.km)
125
125
126
126
127 self.set_hook('complete_command', module_completer, str_key = 'import')
127 self.set_hook('complete_command', module_completer, str_key = 'import')
128 self.set_hook('complete_command', module_completer, str_key = 'from')
128 self.set_hook('complete_command', module_completer, str_key = 'from')
129 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
129 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
130 self.set_hook('complete_command', cd_completer, str_key = '%cd')
130 self.set_hook('complete_command', cd_completer, str_key = '%cd')
131
131
132 # Only configure readline if we truly are using readline. IPython can
132 # Only configure readline if we truly are using readline. IPython can
133 # do tab-completion over the network, in GUIs, etc, where readline
133 # do tab-completion over the network, in GUIs, etc, where readline
134 # itself may be absent
134 # itself may be absent
135 if self.has_readline:
135 if self.has_readline:
136 self.set_readline_completer()
136 self.set_readline_completer()
137
137
138 def run_cell(self, cell, store_history=True):
138 def run_cell(self, cell, store_history=True):
139 """Run a complete IPython cell.
139 """Run a complete IPython cell.
140
140
141 Parameters
141 Parameters
142 ----------
142 ----------
143 cell : str
143 cell : str
144 The code (including IPython code such as %magic functions) to run.
144 The code (including IPython code such as %magic functions) to run.
145 store_history : bool
145 store_history : bool
146 If True, the raw and translated cell will be stored in IPython's
146 If True, the raw and translated cell will be stored in IPython's
147 history. For user code calling back into IPython's machinery, this
147 history. For user code calling back into IPython's machinery, this
148 should be set to False.
148 should be set to False.
149 """
149 """
150 if (not cell) or cell.isspace():
150 if (not cell) or cell.isspace():
151 return
151 return
152
152
153 if cell.strip() == 'exit':
153 if cell.strip() == 'exit':
154 # explicitly handle 'exit' command
154 # explicitly handle 'exit' command
155 return self.ask_exit()
155 return self.ask_exit()
156
156
157 self._executing = True
157 self._executing = True
158 # flush stale replies, which could have been ignored, due to missed heartbeats
158 # flush stale replies, which could have been ignored, due to missed heartbeats
159 while self.km.shell_channel.msg_ready():
159 while self.km.shell_channel.msg_ready():
160 self.km.shell_channel.get_msg()
160 self.km.shell_channel.get_msg()
161 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
161 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
162 msg_id = self.km.shell_channel.execute(cell, not store_history)
162 msg_id = self.km.shell_channel.execute(cell, not store_history)
163 while not self.km.shell_channel.msg_ready() and self.km.is_alive:
163 while not self.km.shell_channel.msg_ready() and self.km.is_alive():
164 try:
164 try:
165 self.handle_stdin_request(timeout=0.05)
165 self.handle_stdin_request(timeout=0.05)
166 except Empty:
166 except Empty:
167 # display intermediate print statements, etc.
167 # display intermediate print statements, etc.
168 self.handle_iopub()
168 self.handle_iopub()
169 pass
169 pass
170 if self.km.shell_channel.msg_ready():
170 if self.km.shell_channel.msg_ready():
171 self.handle_execute_reply(msg_id)
171 self.handle_execute_reply(msg_id)
172 self._executing = False
172 self._executing = False
173
173
174 #-----------------
174 #-----------------
175 # message handlers
175 # message handlers
176 #-----------------
176 #-----------------
177
177
178 def handle_execute_reply(self, msg_id):
178 def handle_execute_reply(self, msg_id):
179 msg = self.km.shell_channel.get_msg()
179 msg = self.km.shell_channel.get_msg()
180 if msg["parent_header"].get("msg_id", None) == msg_id:
180 if msg["parent_header"].get("msg_id", None) == msg_id:
181
181
182 self.handle_iopub()
182 self.handle_iopub()
183
183
184 content = msg["content"]
184 content = msg["content"]
185 status = content['status']
185 status = content['status']
186
186
187 if status == 'aborted':
187 if status == 'aborted':
188 self.write('Aborted\n')
188 self.write('Aborted\n')
189 return
189 return
190 elif status == 'ok':
190 elif status == 'ok':
191 # print execution payloads as well:
191 # print execution payloads as well:
192 for item in content["payload"]:
192 for item in content["payload"]:
193 text = item.get('text', None)
193 text = item.get('text', None)
194 if text:
194 if text:
195 page.page(text)
195 page.page(text)
196
196
197 elif status == 'error':
197 elif status == 'error':
198 for frame in content["traceback"]:
198 for frame in content["traceback"]:
199 print(frame, file=io.stderr)
199 print(frame, file=io.stderr)
200
200
201 self.execution_count = int(content["execution_count"] + 1)
201 self.execution_count = int(content["execution_count"] + 1)
202
202
203
203
204 def handle_iopub(self):
204 def handle_iopub(self):
205 """ Method to procces subscribe channel's messages
205 """ Method to procces subscribe channel's messages
206
206
207 This method reads a message and processes the content in different
207 This method reads a message and processes the content in different
208 outputs like stdout, stderr, pyout and status
208 outputs like stdout, stderr, pyout and status
209
209
210 Arguments:
210 Arguments:
211 sub_msg: message receive from kernel in the sub socket channel
211 sub_msg: message receive from kernel in the sub socket channel
212 capture by kernel manager.
212 capture by kernel manager.
213 """
213 """
214 while self.km.iopub_channel.msg_ready():
214 while self.km.iopub_channel.msg_ready():
215 sub_msg = self.km.iopub_channel.get_msg()
215 sub_msg = self.km.iopub_channel.get_msg()
216 msg_type = sub_msg['header']['msg_type']
216 msg_type = sub_msg['header']['msg_type']
217 parent = sub_msg["parent_header"]
217 parent = sub_msg["parent_header"]
218 if (not parent) or self.session_id == parent['session']:
218 if (not parent) or self.session_id == parent['session']:
219 if msg_type == 'status' :
219 if msg_type == 'status' :
220 if sub_msg["content"]["execution_state"] == "busy" :
220 if sub_msg["content"]["execution_state"] == "busy" :
221 pass
221 pass
222
222
223 elif msg_type == 'stream' :
223 elif msg_type == 'stream' :
224 if sub_msg["content"]["name"] == "stdout":
224 if sub_msg["content"]["name"] == "stdout":
225 print(sub_msg["content"]["data"], file=io.stdout, end="")
225 print(sub_msg["content"]["data"], file=io.stdout, end="")
226 io.stdout.flush()
226 io.stdout.flush()
227 elif sub_msg["content"]["name"] == "stderr" :
227 elif sub_msg["content"]["name"] == "stderr" :
228 print(sub_msg["content"]["data"], file=io.stderr, end="")
228 print(sub_msg["content"]["data"], file=io.stderr, end="")
229 io.stderr.flush()
229 io.stderr.flush()
230
230
231 elif msg_type == 'pyout':
231 elif msg_type == 'pyout':
232 self.execution_count = int(sub_msg["content"]["execution_count"])
232 self.execution_count = int(sub_msg["content"]["execution_count"])
233 format_dict = sub_msg["content"]["data"]
233 format_dict = sub_msg["content"]["data"]
234 self.handle_rich_data(format_dict)
234 self.handle_rich_data(format_dict)
235 # taken from DisplayHook.__call__:
235 # taken from DisplayHook.__call__:
236 hook = self.displayhook
236 hook = self.displayhook
237 hook.start_displayhook()
237 hook.start_displayhook()
238 hook.write_output_prompt()
238 hook.write_output_prompt()
239 hook.write_format_data(format_dict)
239 hook.write_format_data(format_dict)
240 hook.log_output(format_dict)
240 hook.log_output(format_dict)
241 hook.finish_displayhook()
241 hook.finish_displayhook()
242
242
243 elif msg_type == 'display_data':
243 elif msg_type == 'display_data':
244 self.handle_rich_data(sub_msg["content"]["data"])
244 self.handle_rich_data(sub_msg["content"]["data"])
245
245
246 _imagemime = {
246 _imagemime = {
247 'image/png': 'png',
247 'image/png': 'png',
248 'image/jpeg': 'jpeg',
248 'image/jpeg': 'jpeg',
249 'image/svg+xml': 'svg',
249 'image/svg+xml': 'svg',
250 }
250 }
251
251
252 def handle_rich_data(self, data):
252 def handle_rich_data(self, data):
253 for mime in self.mime_preference:
253 for mime in self.mime_preference:
254 if mime in data and mime in self._imagemime:
254 if mime in data and mime in self._imagemime:
255 self.handle_image(data, mime)
255 self.handle_image(data, mime)
256 return
256 return
257
257
258 def handle_image(self, data, mime):
258 def handle_image(self, data, mime):
259 handler = getattr(
259 handler = getattr(
260 self, 'handle_image_{0}'.format(self.image_handler), None)
260 self, 'handle_image_{0}'.format(self.image_handler), None)
261 if handler:
261 if handler:
262 handler(data, mime)
262 handler(data, mime)
263
263
264 def handle_image_PIL(self, data, mime):
264 def handle_image_PIL(self, data, mime):
265 if mime not in ('image/png', 'image/jpeg'):
265 if mime not in ('image/png', 'image/jpeg'):
266 return
266 return
267 import PIL.Image
267 import PIL.Image
268 raw = base64.decodestring(data[mime].encode('ascii'))
268 raw = base64.decodestring(data[mime].encode('ascii'))
269 img = PIL.Image.open(BytesIO(raw))
269 img = PIL.Image.open(BytesIO(raw))
270 img.show()
270 img.show()
271
271
272 def handle_image_stream(self, data, mime):
272 def handle_image_stream(self, data, mime):
273 raw = base64.decodestring(data[mime].encode('ascii'))
273 raw = base64.decodestring(data[mime].encode('ascii'))
274 imageformat = self._imagemime[mime]
274 imageformat = self._imagemime[mime]
275 fmt = dict(format=imageformat)
275 fmt = dict(format=imageformat)
276 args = [s.format(**fmt) for s in self.stream_image_handler]
276 args = [s.format(**fmt) for s in self.stream_image_handler]
277 with open(os.devnull, 'w') as devnull:
277 with open(os.devnull, 'w') as devnull:
278 proc = subprocess.Popen(
278 proc = subprocess.Popen(
279 args, stdin=subprocess.PIPE,
279 args, stdin=subprocess.PIPE,
280 stdout=devnull, stderr=devnull)
280 stdout=devnull, stderr=devnull)
281 proc.communicate(raw)
281 proc.communicate(raw)
282
282
283 def handle_image_tempfile(self, data, mime):
283 def handle_image_tempfile(self, data, mime):
284 raw = base64.decodestring(data[mime].encode('ascii'))
284 raw = base64.decodestring(data[mime].encode('ascii'))
285 imageformat = self._imagemime[mime]
285 imageformat = self._imagemime[mime]
286 filename = 'tmp.{0}'.format(imageformat)
286 filename = 'tmp.{0}'.format(imageformat)
287 with nested(NamedFileInTemporaryDirectory(filename),
287 with nested(NamedFileInTemporaryDirectory(filename),
288 open(os.devnull, 'w')) as (f, devnull):
288 open(os.devnull, 'w')) as (f, devnull):
289 f.write(raw)
289 f.write(raw)
290 f.flush()
290 f.flush()
291 fmt = dict(file=f.name, format=imageformat)
291 fmt = dict(file=f.name, format=imageformat)
292 args = [s.format(**fmt) for s in self.tempfile_image_handler]
292 args = [s.format(**fmt) for s in self.tempfile_image_handler]
293 subprocess.call(args, stdout=devnull, stderr=devnull)
293 subprocess.call(args, stdout=devnull, stderr=devnull)
294
294
295 def handle_image_callable(self, data, mime):
295 def handle_image_callable(self, data, mime):
296 self.callable_image_handler(data)
296 self.callable_image_handler(data)
297
297
298 def handle_stdin_request(self, timeout=0.1):
298 def handle_stdin_request(self, timeout=0.1):
299 """ Method to capture raw_input
299 """ Method to capture raw_input
300 """
300 """
301 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
301 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
302 # in case any iopub came while we were waiting:
302 # in case any iopub came while we were waiting:
303 self.handle_iopub()
303 self.handle_iopub()
304 if self.session_id == msg_rep["parent_header"].get("session"):
304 if self.session_id == msg_rep["parent_header"].get("session"):
305 # wrap SIGINT handler
305 # wrap SIGINT handler
306 real_handler = signal.getsignal(signal.SIGINT)
306 real_handler = signal.getsignal(signal.SIGINT)
307 def double_int(sig,frame):
307 def double_int(sig,frame):
308 # call real handler (forwards sigint to kernel),
308 # call real handler (forwards sigint to kernel),
309 # then raise local interrupt, stopping local raw_input
309 # then raise local interrupt, stopping local raw_input
310 real_handler(sig,frame)
310 real_handler(sig,frame)
311 raise KeyboardInterrupt
311 raise KeyboardInterrupt
312 signal.signal(signal.SIGINT, double_int)
312 signal.signal(signal.SIGINT, double_int)
313
313
314 try:
314 try:
315 raw_data = raw_input(msg_rep["content"]["prompt"])
315 raw_data = raw_input(msg_rep["content"]["prompt"])
316 except EOFError:
316 except EOFError:
317 # turn EOFError into EOF character
317 # turn EOFError into EOF character
318 raw_data = '\x04'
318 raw_data = '\x04'
319 except KeyboardInterrupt:
319 except KeyboardInterrupt:
320 sys.stdout.write('\n')
320 sys.stdout.write('\n')
321 return
321 return
322 finally:
322 finally:
323 # restore SIGINT handler
323 # restore SIGINT handler
324 signal.signal(signal.SIGINT, real_handler)
324 signal.signal(signal.SIGINT, real_handler)
325
325
326 # only send stdin reply if there *was not* another request
326 # only send stdin reply if there *was not* another request
327 # or execution finished while we were reading.
327 # or execution finished while we were reading.
328 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
328 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
329 self.km.stdin_channel.input(raw_data)
329 self.km.stdin_channel.input(raw_data)
330
330
331 def mainloop(self, display_banner=False):
331 def mainloop(self, display_banner=False):
332 while True:
332 while True:
333 try:
333 try:
334 self.interact(display_banner=display_banner)
334 self.interact(display_banner=display_banner)
335 #self.interact_with_readline()
335 #self.interact_with_readline()
336 # XXX for testing of a readline-decoupled repl loop, call
336 # XXX for testing of a readline-decoupled repl loop, call
337 # interact_with_readline above
337 # interact_with_readline above
338 break
338 break
339 except KeyboardInterrupt:
339 except KeyboardInterrupt:
340 # this should not be necessary, but KeyboardInterrupt
340 # this should not be necessary, but KeyboardInterrupt
341 # handling seems rather unpredictable...
341 # handling seems rather unpredictable...
342 self.write("\nKeyboardInterrupt in interact()\n")
342 self.write("\nKeyboardInterrupt in interact()\n")
343
343
344 def wait_for_kernel(self, timeout=None):
344 def wait_for_kernel(self, timeout=None):
345 """method to wait for a kernel to be ready"""
345 """method to wait for a kernel to be ready"""
346 tic = time.time()
346 tic = time.time()
347 self.km.hb_channel.unpause()
347 self.km.hb_channel.unpause()
348 while True:
348 while True:
349 self.run_cell('1', False)
349 self.run_cell('1', False)
350 if self.km.hb_channel.is_beating():
350 if self.km.hb_channel.is_beating():
351 # heart failure was not the reason this returned
351 # heart failure was not the reason this returned
352 break
352 break
353 else:
353 else:
354 # heart failed
354 # heart failed
355 if timeout is not None and (time.time() - tic) > timeout:
355 if timeout is not None and (time.time() - tic) > timeout:
356 return False
356 return False
357 return True
357 return True
358
358
359 def interact(self, display_banner=None):
359 def interact(self, display_banner=None):
360 """Closely emulate the interactive Python console."""
360 """Closely emulate the interactive Python console."""
361
361
362 # batch run -> do not interact
362 # batch run -> do not interact
363 if self.exit_now:
363 if self.exit_now:
364 return
364 return
365
365
366 if display_banner is None:
366 if display_banner is None:
367 display_banner = self.display_banner
367 display_banner = self.display_banner
368
368
369 if isinstance(display_banner, basestring):
369 if isinstance(display_banner, basestring):
370 self.show_banner(display_banner)
370 self.show_banner(display_banner)
371 elif display_banner:
371 elif display_banner:
372 self.show_banner()
372 self.show_banner()
373
373
374 more = False
374 more = False
375
375
376 # run a non-empty no-op, so that we don't get a prompt until
376 # run a non-empty no-op, so that we don't get a prompt until
377 # we know the kernel is ready. This keeps the connection
377 # we know the kernel is ready. This keeps the connection
378 # message above the first prompt.
378 # message above the first prompt.
379 if not self.wait_for_kernel(3):
379 if not self.wait_for_kernel(3):
380 error("Kernel did not respond\n")
380 error("Kernel did not respond\n")
381 return
381 return
382
382
383 if self.has_readline:
383 if self.has_readline:
384 self.readline_startup_hook(self.pre_readline)
384 self.readline_startup_hook(self.pre_readline)
385 hlen_b4_cell = self.readline.get_current_history_length()
385 hlen_b4_cell = self.readline.get_current_history_length()
386 else:
386 else:
387 hlen_b4_cell = 0
387 hlen_b4_cell = 0
388 # exit_now is set by a call to %Exit or %Quit, through the
388 # exit_now is set by a call to %Exit or %Quit, through the
389 # ask_exit callback.
389 # ask_exit callback.
390
390
391 while not self.exit_now:
391 while not self.exit_now:
392 if not self.km.is_alive:
392 if not self.km.is_alive():
393 # kernel died, prompt for action or exit
393 # kernel died, prompt for action or exit
394 action = "restart" if self.km.has_kernel else "wait for restart"
394 action = "restart" if self.km.has_kernel else "wait for restart"
395 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
395 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
396 if ans:
396 if ans:
397 if self.km.has_kernel:
397 if self.km.has_kernel:
398 self.km.restart_kernel(True)
398 self.km.restart_kernel(True)
399 self.wait_for_kernel(3)
399 self.wait_for_kernel(3)
400 else:
400 else:
401 self.exit_now = True
401 self.exit_now = True
402 continue
402 continue
403 try:
403 try:
404 # protect prompt block from KeyboardInterrupt
404 # protect prompt block from KeyboardInterrupt
405 # when sitting on ctrl-C
405 # when sitting on ctrl-C
406 self.hooks.pre_prompt_hook()
406 self.hooks.pre_prompt_hook()
407 if more:
407 if more:
408 try:
408 try:
409 prompt = self.prompt_manager.render('in2')
409 prompt = self.prompt_manager.render('in2')
410 except Exception:
410 except Exception:
411 self.showtraceback()
411 self.showtraceback()
412 if self.autoindent:
412 if self.autoindent:
413 self.rl_do_indent = True
413 self.rl_do_indent = True
414
414
415 else:
415 else:
416 try:
416 try:
417 prompt = self.separate_in + self.prompt_manager.render('in')
417 prompt = self.separate_in + self.prompt_manager.render('in')
418 except Exception:
418 except Exception:
419 self.showtraceback()
419 self.showtraceback()
420
420
421 line = self.raw_input(prompt)
421 line = self.raw_input(prompt)
422 if self.exit_now:
422 if self.exit_now:
423 # quick exit on sys.std[in|out] close
423 # quick exit on sys.std[in|out] close
424 break
424 break
425 if self.autoindent:
425 if self.autoindent:
426 self.rl_do_indent = False
426 self.rl_do_indent = False
427
427
428 except KeyboardInterrupt:
428 except KeyboardInterrupt:
429 #double-guard against keyboardinterrupts during kbdint handling
429 #double-guard against keyboardinterrupts during kbdint handling
430 try:
430 try:
431 self.write('\nKeyboardInterrupt\n')
431 self.write('\nKeyboardInterrupt\n')
432 source_raw = self.input_splitter.source_raw_reset()[1]
432 source_raw = self.input_splitter.source_raw_reset()[1]
433 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
433 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
434 more = False
434 more = False
435 except KeyboardInterrupt:
435 except KeyboardInterrupt:
436 pass
436 pass
437 except EOFError:
437 except EOFError:
438 if self.autoindent:
438 if self.autoindent:
439 self.rl_do_indent = False
439 self.rl_do_indent = False
440 if self.has_readline:
440 if self.has_readline:
441 self.readline_startup_hook(None)
441 self.readline_startup_hook(None)
442 self.write('\n')
442 self.write('\n')
443 self.exit()
443 self.exit()
444 except bdb.BdbQuit:
444 except bdb.BdbQuit:
445 warn('The Python debugger has exited with a BdbQuit exception.\n'
445 warn('The Python debugger has exited with a BdbQuit exception.\n'
446 'Because of how pdb handles the stack, it is impossible\n'
446 'Because of how pdb handles the stack, it is impossible\n'
447 'for IPython to properly format this particular exception.\n'
447 'for IPython to properly format this particular exception.\n'
448 'IPython will resume normal operation.')
448 'IPython will resume normal operation.')
449 except:
449 except:
450 # exceptions here are VERY RARE, but they can be triggered
450 # exceptions here are VERY RARE, but they can be triggered
451 # asynchronously by signal handlers, for example.
451 # asynchronously by signal handlers, for example.
452 self.showtraceback()
452 self.showtraceback()
453 else:
453 else:
454 self.input_splitter.push(line)
454 self.input_splitter.push(line)
455 more = self.input_splitter.push_accepts_more()
455 more = self.input_splitter.push_accepts_more()
456 if (self.SyntaxTB.last_syntax_error and
456 if (self.SyntaxTB.last_syntax_error and
457 self.autoedit_syntax):
457 self.autoedit_syntax):
458 self.edit_syntax_error()
458 self.edit_syntax_error()
459 if not more:
459 if not more:
460 source_raw = self.input_splitter.source_raw_reset()[1]
460 source_raw = self.input_splitter.source_raw_reset()[1]
461 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
461 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
462 self.run_cell(source_raw)
462 self.run_cell(source_raw)
463
463
464
464
465 # Turn off the exit flag, so the mainloop can be restarted if desired
465 # Turn off the exit flag, so the mainloop can be restarted if desired
466 self.exit_now = False
466 self.exit_now = False
@@ -1,314 +1,313 b''
1 """ A kernel manager for in-process kernels. """
1 """ A kernel manager for in-process kernels. """
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2012 The IPython Development Team
4 # Copyright (C) 2012 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 # Local imports.
14 # Local imports.
15 from IPython.config.configurable import Configurable
15 from IPython.config.configurable import Configurable
16 from IPython.utils.traitlets import Any, Instance, Type
16 from IPython.utils.traitlets import Any, Instance, Type
17 from IPython.kernel.kernelmanagerabc import (
17 from IPython.kernel.kernelmanagerabc import (
18 ShellChannelABC, IOPubChannelABC,
18 ShellChannelABC, IOPubChannelABC,
19 HBChannelABC, StdInChannelABC,
19 HBChannelABC, StdInChannelABC,
20 KernelManagerABC
20 KernelManagerABC
21 )
21 )
22
22
23 from .socket import DummySocket
23 from .socket import DummySocket
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Channel classes
26 # Channel classes
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 class InProcessChannel(object):
29 class InProcessChannel(object):
30 """Base class for in-process channels."""
30 """Base class for in-process channels."""
31
31
32 def __init__(self, manager):
32 def __init__(self, manager):
33 super(InProcessChannel, self).__init__()
33 super(InProcessChannel, self).__init__()
34 self.manager = manager
34 self.manager = manager
35 self._is_alive = False
35 self._is_alive = False
36
36
37 #--------------------------------------------------------------------------
37 #--------------------------------------------------------------------------
38 # Channel interface
38 # Channel interface
39 #--------------------------------------------------------------------------
39 #--------------------------------------------------------------------------
40
40
41 def is_alive(self):
41 def is_alive(self):
42 return self._is_alive
42 return self._is_alive
43
43
44 def start(self):
44 def start(self):
45 self._is_alive = True
45 self._is_alive = True
46
46
47 def stop(self):
47 def stop(self):
48 self._is_alive = False
48 self._is_alive = False
49
49
50 def call_handlers(self, msg):
50 def call_handlers(self, msg):
51 """ This method is called in the main thread when a message arrives.
51 """ This method is called in the main thread when a message arrives.
52
52
53 Subclasses should override this method to handle incoming messages.
53 Subclasses should override this method to handle incoming messages.
54 """
54 """
55 raise NotImplementedError('call_handlers must be defined in a subclass.')
55 raise NotImplementedError('call_handlers must be defined in a subclass.')
56
56
57 #--------------------------------------------------------------------------
57 #--------------------------------------------------------------------------
58 # InProcessChannel interface
58 # InProcessChannel interface
59 #--------------------------------------------------------------------------
59 #--------------------------------------------------------------------------
60
60
61 def call_handlers_later(self, *args, **kwds):
61 def call_handlers_later(self, *args, **kwds):
62 """ Call the message handlers later.
62 """ Call the message handlers later.
63
63
64 The default implementation just calls the handlers immediately, but this
64 The default implementation just calls the handlers immediately, but this
65 method exists so that GUI toolkits can defer calling the handlers until
65 method exists so that GUI toolkits can defer calling the handlers until
66 after the event loop has run, as expected by GUI frontends.
66 after the event loop has run, as expected by GUI frontends.
67 """
67 """
68 self.call_handlers(*args, **kwds)
68 self.call_handlers(*args, **kwds)
69
69
70 def process_events(self):
70 def process_events(self):
71 """ Process any pending GUI events.
71 """ Process any pending GUI events.
72
72
73 This method will be never be called from a frontend without an event
73 This method will be never be called from a frontend without an event
74 loop (e.g., a terminal frontend).
74 loop (e.g., a terminal frontend).
75 """
75 """
76 raise NotImplementedError
76 raise NotImplementedError
77
77
78
78
79 class InProcessShellChannel(InProcessChannel):
79 class InProcessShellChannel(InProcessChannel):
80 """See `IPython.kernel.kernelmanager.ShellChannel` for docstrings."""
80 """See `IPython.kernel.kernelmanager.ShellChannel` for docstrings."""
81
81
82 # flag for whether execute requests should be allowed to call raw_input
82 # flag for whether execute requests should be allowed to call raw_input
83 allow_stdin = True
83 allow_stdin = True
84
84
85 #--------------------------------------------------------------------------
85 #--------------------------------------------------------------------------
86 # ShellChannel interface
86 # ShellChannel interface
87 #--------------------------------------------------------------------------
87 #--------------------------------------------------------------------------
88
88
89 def execute(self, code, silent=False, store_history=True,
89 def execute(self, code, silent=False, store_history=True,
90 user_variables=[], user_expressions={}, allow_stdin=None):
90 user_variables=[], user_expressions={}, allow_stdin=None):
91 if allow_stdin is None:
91 if allow_stdin is None:
92 allow_stdin = self.allow_stdin
92 allow_stdin = self.allow_stdin
93 content = dict(code=code, silent=silent, store_history=store_history,
93 content = dict(code=code, silent=silent, store_history=store_history,
94 user_variables=user_variables,
94 user_variables=user_variables,
95 user_expressions=user_expressions,
95 user_expressions=user_expressions,
96 allow_stdin=allow_stdin)
96 allow_stdin=allow_stdin)
97 msg = self.manager.session.msg('execute_request', content)
97 msg = self.manager.session.msg('execute_request', content)
98 self._dispatch_to_kernel(msg)
98 self._dispatch_to_kernel(msg)
99 return msg['header']['msg_id']
99 return msg['header']['msg_id']
100
100
101 def complete(self, text, line, cursor_pos, block=None):
101 def complete(self, text, line, cursor_pos, block=None):
102 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
102 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
103 msg = self.manager.session.msg('complete_request', content)
103 msg = self.manager.session.msg('complete_request', content)
104 self._dispatch_to_kernel(msg)
104 self._dispatch_to_kernel(msg)
105 return msg['header']['msg_id']
105 return msg['header']['msg_id']
106
106
107 def object_info(self, oname, detail_level=0):
107 def object_info(self, oname, detail_level=0):
108 content = dict(oname=oname, detail_level=detail_level)
108 content = dict(oname=oname, detail_level=detail_level)
109 msg = self.manager.session.msg('object_info_request', content)
109 msg = self.manager.session.msg('object_info_request', content)
110 self._dispatch_to_kernel(msg)
110 self._dispatch_to_kernel(msg)
111 return msg['header']['msg_id']
111 return msg['header']['msg_id']
112
112
113 def history(self, raw=True, output=False, hist_access_type='range', **kwds):
113 def history(self, raw=True, output=False, hist_access_type='range', **kwds):
114 content = dict(raw=raw, output=output,
114 content = dict(raw=raw, output=output,
115 hist_access_type=hist_access_type, **kwds)
115 hist_access_type=hist_access_type, **kwds)
116 msg = self.manager.session.msg('history_request', content)
116 msg = self.manager.session.msg('history_request', content)
117 self._dispatch_to_kernel(msg)
117 self._dispatch_to_kernel(msg)
118 return msg['header']['msg_id']
118 return msg['header']['msg_id']
119
119
120 def shutdown(self, restart=False):
120 def shutdown(self, restart=False):
121 # FIXME: What to do here?
121 # FIXME: What to do here?
122 raise NotImplementedError('Cannot shutdown in-process kernel')
122 raise NotImplementedError('Cannot shutdown in-process kernel')
123
123
124 #--------------------------------------------------------------------------
124 #--------------------------------------------------------------------------
125 # Protected interface
125 # Protected interface
126 #--------------------------------------------------------------------------
126 #--------------------------------------------------------------------------
127
127
128 def _dispatch_to_kernel(self, msg):
128 def _dispatch_to_kernel(self, msg):
129 """ Send a message to the kernel and handle a reply.
129 """ Send a message to the kernel and handle a reply.
130 """
130 """
131 kernel = self.manager.kernel
131 kernel = self.manager.kernel
132 if kernel is None:
132 if kernel is None:
133 raise RuntimeError('Cannot send request. No kernel exists.')
133 raise RuntimeError('Cannot send request. No kernel exists.')
134
134
135 stream = DummySocket()
135 stream = DummySocket()
136 self.manager.session.send(stream, msg)
136 self.manager.session.send(stream, msg)
137 msg_parts = stream.recv_multipart()
137 msg_parts = stream.recv_multipart()
138 kernel.dispatch_shell(stream, msg_parts)
138 kernel.dispatch_shell(stream, msg_parts)
139
139
140 idents, reply_msg = self.manager.session.recv(stream, copy=False)
140 idents, reply_msg = self.manager.session.recv(stream, copy=False)
141 self.call_handlers_later(reply_msg)
141 self.call_handlers_later(reply_msg)
142
142
143
143
144 class InProcessIOPubChannel(InProcessChannel):
144 class InProcessIOPubChannel(InProcessChannel):
145 """See `IPython.kernel.kernelmanager.IOPubChannel` for docstrings."""
145 """See `IPython.kernel.kernelmanager.IOPubChannel` for docstrings."""
146
146
147 def flush(self, timeout=1.0):
147 def flush(self, timeout=1.0):
148 pass
148 pass
149
149
150
150
151 class InProcessStdInChannel(InProcessChannel):
151 class InProcessStdInChannel(InProcessChannel):
152 """See `IPython.kernel.kernelmanager.StdInChannel` for docstrings."""
152 """See `IPython.kernel.kernelmanager.StdInChannel` for docstrings."""
153
153
154 def input(self, string):
154 def input(self, string):
155 kernel = self.manager.kernel
155 kernel = self.manager.kernel
156 if kernel is None:
156 if kernel is None:
157 raise RuntimeError('Cannot send input reply. No kernel exists.')
157 raise RuntimeError('Cannot send input reply. No kernel exists.')
158 kernel.raw_input_str = string
158 kernel.raw_input_str = string
159
159
160
160
161 class InProcessHBChannel(InProcessChannel):
161 class InProcessHBChannel(InProcessChannel):
162 """See `IPython.kernel.kernelmanager.HBChannel` for docstrings."""
162 """See `IPython.kernel.kernelmanager.HBChannel` for docstrings."""
163
163
164 time_to_dead = 3.0
164 time_to_dead = 3.0
165
165
166 def __init__(self, *args, **kwds):
166 def __init__(self, *args, **kwds):
167 super(InProcessHBChannel, self).__init__(*args, **kwds)
167 super(InProcessHBChannel, self).__init__(*args, **kwds)
168 self._pause = True
168 self._pause = True
169
169
170 def pause(self):
170 def pause(self):
171 self._pause = True
171 self._pause = True
172
172
173 def unpause(self):
173 def unpause(self):
174 self._pause = False
174 self._pause = False
175
175
176 def is_beating(self):
176 def is_beating(self):
177 return not self._pause
177 return not self._pause
178
178
179
179
180 #-----------------------------------------------------------------------------
180 #-----------------------------------------------------------------------------
181 # Main kernel manager class
181 # Main kernel manager class
182 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
183
183
184 class InProcessKernelManager(Configurable):
184 class InProcessKernelManager(Configurable):
185 """A manager for an in-process kernel.
185 """A manager for an in-process kernel.
186
186
187 This class implements the interface of
187 This class implements the interface of
188 `IPython.kernel.kernelmanagerabc.KernelManagerABC` and allows
188 `IPython.kernel.kernelmanagerabc.KernelManagerABC` and allows
189 (asynchronous) frontends to be used seamlessly with an in-process kernel.
189 (asynchronous) frontends to be used seamlessly with an in-process kernel.
190
190
191 See `IPython.kernel.kernelmanager.KernelManager` for docstrings.
191 See `IPython.kernel.kernelmanager.KernelManager` for docstrings.
192 """
192 """
193
193
194 # The Session to use for building messages.
194 # The Session to use for building messages.
195 session = Instance('IPython.kernel.zmq.session.Session')
195 session = Instance('IPython.kernel.zmq.session.Session')
196 def _session_default(self):
196 def _session_default(self):
197 from IPython.kernel.zmq.session import Session
197 from IPython.kernel.zmq.session import Session
198 return Session(config=self.config)
198 return Session(config=self.config)
199
199
200 # The kernel process with which the KernelManager is communicating.
200 # The kernel process with which the KernelManager is communicating.
201 kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel')
201 kernel = Instance('IPython.kernel.inprocess.ipkernel.InProcessKernel')
202
202
203 # The classes to use for the various channels.
203 # The classes to use for the various channels.
204 shell_channel_class = Type(InProcessShellChannel)
204 shell_channel_class = Type(InProcessShellChannel)
205 iopub_channel_class = Type(InProcessIOPubChannel)
205 iopub_channel_class = Type(InProcessIOPubChannel)
206 stdin_channel_class = Type(InProcessStdInChannel)
206 stdin_channel_class = Type(InProcessStdInChannel)
207 hb_channel_class = Type(InProcessHBChannel)
207 hb_channel_class = Type(InProcessHBChannel)
208
208
209 # Protected traits.
209 # Protected traits.
210 _shell_channel = Any
210 _shell_channel = Any
211 _iopub_channel = Any
211 _iopub_channel = Any
212 _stdin_channel = Any
212 _stdin_channel = Any
213 _hb_channel = Any
213 _hb_channel = Any
214
214
215 #--------------------------------------------------------------------------
215 #--------------------------------------------------------------------------
216 # Channel management methods.
216 # Channel management methods.
217 #--------------------------------------------------------------------------
217 #--------------------------------------------------------------------------
218
218
219 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
219 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
220 if shell:
220 if shell:
221 self.shell_channel.start()
221 self.shell_channel.start()
222 if iopub:
222 if iopub:
223 self.iopub_channel.start()
223 self.iopub_channel.start()
224 if stdin:
224 if stdin:
225 self.stdin_channel.start()
225 self.stdin_channel.start()
226 self.shell_channel.allow_stdin = True
226 self.shell_channel.allow_stdin = True
227 else:
227 else:
228 self.shell_channel.allow_stdin = False
228 self.shell_channel.allow_stdin = False
229 if hb:
229 if hb:
230 self.hb_channel.start()
230 self.hb_channel.start()
231
231
232 def stop_channels(self):
232 def stop_channels(self):
233 if self.shell_channel.is_alive():
233 if self.shell_channel.is_alive():
234 self.shell_channel.stop()
234 self.shell_channel.stop()
235 if self.iopub_channel.is_alive():
235 if self.iopub_channel.is_alive():
236 self.iopub_channel.stop()
236 self.iopub_channel.stop()
237 if self.stdin_channel.is_alive():
237 if self.stdin_channel.is_alive():
238 self.stdin_channel.stop()
238 self.stdin_channel.stop()
239 if self.hb_channel.is_alive():
239 if self.hb_channel.is_alive():
240 self.hb_channel.stop()
240 self.hb_channel.stop()
241
241
242 @property
242 @property
243 def channels_running(self):
243 def channels_running(self):
244 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
244 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
245 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
245 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
246
246
247 @property
247 @property
248 def shell_channel(self):
248 def shell_channel(self):
249 if self._shell_channel is None:
249 if self._shell_channel is None:
250 self._shell_channel = self.shell_channel_class(self)
250 self._shell_channel = self.shell_channel_class(self)
251 return self._shell_channel
251 return self._shell_channel
252
252
253 @property
253 @property
254 def iopub_channel(self):
254 def iopub_channel(self):
255 if self._iopub_channel is None:
255 if self._iopub_channel is None:
256 self._iopub_channel = self.iopub_channel_class(self)
256 self._iopub_channel = self.iopub_channel_class(self)
257 return self._iopub_channel
257 return self._iopub_channel
258
258
259 @property
259 @property
260 def stdin_channel(self):
260 def stdin_channel(self):
261 if self._stdin_channel is None:
261 if self._stdin_channel is None:
262 self._stdin_channel = self.stdin_channel_class(self)
262 self._stdin_channel = self.stdin_channel_class(self)
263 return self._stdin_channel
263 return self._stdin_channel
264
264
265 @property
265 @property
266 def hb_channel(self):
266 def hb_channel(self):
267 if self._hb_channel is None:
267 if self._hb_channel is None:
268 self._hb_channel = self.hb_channel_class(self)
268 self._hb_channel = self.hb_channel_class(self)
269 return self._hb_channel
269 return self._hb_channel
270
270
271 #--------------------------------------------------------------------------
271 #--------------------------------------------------------------------------
272 # Kernel management methods:
272 # Kernel management methods:
273 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
274
274
275 def start_kernel(self, **kwds):
275 def start_kernel(self, **kwds):
276 from IPython.kernel.inprocess.ipkernel import InProcessKernel
276 from IPython.kernel.inprocess.ipkernel import InProcessKernel
277 self.kernel = InProcessKernel()
277 self.kernel = InProcessKernel()
278 self.kernel.frontends.append(self)
278 self.kernel.frontends.append(self)
279
279
280 def shutdown_kernel(self):
280 def shutdown_kernel(self):
281 self._kill_kernel()
281 self._kill_kernel()
282
282
283 def restart_kernel(self, now=False, **kwds):
283 def restart_kernel(self, now=False, **kwds):
284 self.shutdown_kernel()
284 self.shutdown_kernel()
285 self.start_kernel(**kwds)
285 self.start_kernel(**kwds)
286
286
287 @property
287 @property
288 def has_kernel(self):
288 def has_kernel(self):
289 return self.kernel is not None
289 return self.kernel is not None
290
290
291 def _kill_kernel(self):
291 def _kill_kernel(self):
292 self.kernel.frontends.remove(self)
292 self.kernel.frontends.remove(self)
293 self.kernel = None
293 self.kernel = None
294
294
295 def interrupt_kernel(self):
295 def interrupt_kernel(self):
296 raise NotImplementedError("Cannot interrupt in-process kernel.")
296 raise NotImplementedError("Cannot interrupt in-process kernel.")
297
297
298 def signal_kernel(self, signum):
298 def signal_kernel(self, signum):
299 raise NotImplementedError("Cannot signal in-process kernel.")
299 raise NotImplementedError("Cannot signal in-process kernel.")
300
300
301 @property
302 def is_alive(self):
301 def is_alive(self):
303 return True
302 return True
304
303
305
304
306 #-----------------------------------------------------------------------------
305 #-----------------------------------------------------------------------------
307 # ABC Registration
306 # ABC Registration
308 #-----------------------------------------------------------------------------
307 #-----------------------------------------------------------------------------
309
308
310 ShellChannelABC.register(InProcessShellChannel)
309 ShellChannelABC.register(InProcessShellChannel)
311 IOPubChannelABC.register(InProcessIOPubChannel)
310 IOPubChannelABC.register(InProcessIOPubChannel)
312 HBChannelABC.register(InProcessHBChannel)
311 HBChannelABC.register(InProcessHBChannel)
313 StdInChannelABC.register(InProcessStdInChannel)
312 StdInChannelABC.register(InProcessStdInChannel)
314 KernelManagerABC.register(InProcessKernelManager)
313 KernelManagerABC.register(InProcessKernelManager)
@@ -1,1130 +1,1129 b''
1 """Base classes to manage the interaction with a running kernel.
1 """Base classes to manage the interaction with a running kernel.
2
2
3 TODO
3 TODO
4 * Create logger to handle debugging and console messages.
4 * Create logger to handle debugging and console messages.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2011 The IPython Development Team
8 # Copyright (C) 2008-2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import absolute_import
18 from __future__ import absolute_import
19
19
20 # Standard library imports
20 # Standard library imports
21 import atexit
21 import atexit
22 import errno
22 import errno
23 import json
23 import json
24 from subprocess import Popen
24 from subprocess import Popen
25 import os
25 import os
26 import signal
26 import signal
27 import sys
27 import sys
28 from threading import Thread
28 from threading import Thread
29 import time
29 import time
30
30
31 # System library imports
31 # System library imports
32 import zmq
32 import zmq
33 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
33 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
34 # during garbage collection of threads at exit:
34 # during garbage collection of threads at exit:
35 from zmq import ZMQError
35 from zmq import ZMQError
36 from zmq.eventloop import ioloop, zmqstream
36 from zmq.eventloop import ioloop, zmqstream
37
37
38 # Local imports
38 # Local imports
39 from IPython.config.configurable import Configurable
39 from IPython.config.configurable import Configurable
40 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
40 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
41 from IPython.utils.traitlets import (
41 from IPython.utils.traitlets import (
42 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
42 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
43 )
43 )
44 from IPython.utils.py3compat import str_to_bytes
44 from IPython.utils.py3compat import str_to_bytes
45 from IPython.kernel import (
45 from IPython.kernel import (
46 write_connection_file,
46 write_connection_file,
47 make_ipkernel_cmd,
47 make_ipkernel_cmd,
48 launch_kernel,
48 launch_kernel,
49 )
49 )
50 from .zmq.session import Session
50 from .zmq.session import Session
51 from .kernelmanagerabc import (
51 from .kernelmanagerabc import (
52 ShellChannelABC, IOPubChannelABC,
52 ShellChannelABC, IOPubChannelABC,
53 HBChannelABC, StdInChannelABC,
53 HBChannelABC, StdInChannelABC,
54 KernelManagerABC
54 KernelManagerABC
55 )
55 )
56
56
57
57
58 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
59 # Constants and exceptions
59 # Constants and exceptions
60 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
61
61
62 class InvalidPortNumber(Exception):
62 class InvalidPortNumber(Exception):
63 pass
63 pass
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Utility functions
66 # Utility functions
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 # some utilities to validate message structure, these might get moved elsewhere
69 # some utilities to validate message structure, these might get moved elsewhere
70 # if they prove to have more generic utility
70 # if they prove to have more generic utility
71
71
72 def validate_string_list(lst):
72 def validate_string_list(lst):
73 """Validate that the input is a list of strings.
73 """Validate that the input is a list of strings.
74
74
75 Raises ValueError if not."""
75 Raises ValueError if not."""
76 if not isinstance(lst, list):
76 if not isinstance(lst, list):
77 raise ValueError('input %r must be a list' % lst)
77 raise ValueError('input %r must be a list' % lst)
78 for x in lst:
78 for x in lst:
79 if not isinstance(x, basestring):
79 if not isinstance(x, basestring):
80 raise ValueError('element %r in list must be a string' % x)
80 raise ValueError('element %r in list must be a string' % x)
81
81
82
82
83 def validate_string_dict(dct):
83 def validate_string_dict(dct):
84 """Validate that the input is a dict with string keys and values.
84 """Validate that the input is a dict with string keys and values.
85
85
86 Raises ValueError if not."""
86 Raises ValueError if not."""
87 for k,v in dct.iteritems():
87 for k,v in dct.iteritems():
88 if not isinstance(k, basestring):
88 if not isinstance(k, basestring):
89 raise ValueError('key %r in dict must be a string' % k)
89 raise ValueError('key %r in dict must be a string' % k)
90 if not isinstance(v, basestring):
90 if not isinstance(v, basestring):
91 raise ValueError('value %r in dict must be a string' % v)
91 raise ValueError('value %r in dict must be a string' % v)
92
92
93
93
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95 # ZMQ Socket Channel classes
95 # ZMQ Socket Channel classes
96 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
97
97
98 class ZMQSocketChannel(Thread):
98 class ZMQSocketChannel(Thread):
99 """The base class for the channels that use ZMQ sockets."""
99 """The base class for the channels that use ZMQ sockets."""
100 context = None
100 context = None
101 session = None
101 session = None
102 socket = None
102 socket = None
103 ioloop = None
103 ioloop = None
104 stream = None
104 stream = None
105 _address = None
105 _address = None
106 _exiting = False
106 _exiting = False
107
107
108 def __init__(self, context, session, address):
108 def __init__(self, context, session, address):
109 """Create a channel.
109 """Create a channel.
110
110
111 Parameters
111 Parameters
112 ----------
112 ----------
113 context : :class:`zmq.Context`
113 context : :class:`zmq.Context`
114 The ZMQ context to use.
114 The ZMQ context to use.
115 session : :class:`session.Session`
115 session : :class:`session.Session`
116 The session to use.
116 The session to use.
117 address : zmq url
117 address : zmq url
118 Standard (ip, port) tuple that the kernel is listening on.
118 Standard (ip, port) tuple that the kernel is listening on.
119 """
119 """
120 super(ZMQSocketChannel, self).__init__()
120 super(ZMQSocketChannel, self).__init__()
121 self.daemon = True
121 self.daemon = True
122
122
123 self.context = context
123 self.context = context
124 self.session = session
124 self.session = session
125 if isinstance(address, tuple):
125 if isinstance(address, tuple):
126 if address[1] == 0:
126 if address[1] == 0:
127 message = 'The port number for a channel cannot be 0.'
127 message = 'The port number for a channel cannot be 0.'
128 raise InvalidPortNumber(message)
128 raise InvalidPortNumber(message)
129 address = "tcp://%s:%i" % address
129 address = "tcp://%s:%i" % address
130 self._address = address
130 self._address = address
131 atexit.register(self._notice_exit)
131 atexit.register(self._notice_exit)
132
132
133 def _notice_exit(self):
133 def _notice_exit(self):
134 self._exiting = True
134 self._exiting = True
135
135
136 def _run_loop(self):
136 def _run_loop(self):
137 """Run my loop, ignoring EINTR events in the poller"""
137 """Run my loop, ignoring EINTR events in the poller"""
138 while True:
138 while True:
139 try:
139 try:
140 self.ioloop.start()
140 self.ioloop.start()
141 except ZMQError as e:
141 except ZMQError as e:
142 if e.errno == errno.EINTR:
142 if e.errno == errno.EINTR:
143 continue
143 continue
144 else:
144 else:
145 raise
145 raise
146 except Exception:
146 except Exception:
147 if self._exiting:
147 if self._exiting:
148 break
148 break
149 else:
149 else:
150 raise
150 raise
151 else:
151 else:
152 break
152 break
153
153
154 def stop(self):
154 def stop(self):
155 """Stop the channel's event loop and join its thread.
155 """Stop the channel's event loop and join its thread.
156
156
157 This calls :method:`Thread.join` and returns when the thread
157 This calls :method:`Thread.join` and returns when the thread
158 terminates. :class:`RuntimeError` will be raised if
158 terminates. :class:`RuntimeError` will be raised if
159 :method:`self.start` is called again.
159 :method:`self.start` is called again.
160 """
160 """
161 self.join()
161 self.join()
162
162
163 @property
163 @property
164 def address(self):
164 def address(self):
165 """Get the channel's address as a zmq url string.
165 """Get the channel's address as a zmq url string.
166
166
167 These URLS have the form: 'tcp://127.0.0.1:5555'.
167 These URLS have the form: 'tcp://127.0.0.1:5555'.
168 """
168 """
169 return self._address
169 return self._address
170
170
171 def _queue_send(self, msg):
171 def _queue_send(self, msg):
172 """Queue a message to be sent from the IOLoop's thread.
172 """Queue a message to be sent from the IOLoop's thread.
173
173
174 Parameters
174 Parameters
175 ----------
175 ----------
176 msg : message to send
176 msg : message to send
177
177
178 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
178 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
179 thread control of the action.
179 thread control of the action.
180 """
180 """
181 def thread_send():
181 def thread_send():
182 self.session.send(self.stream, msg)
182 self.session.send(self.stream, msg)
183 self.ioloop.add_callback(thread_send)
183 self.ioloop.add_callback(thread_send)
184
184
185 def _handle_recv(self, msg):
185 def _handle_recv(self, msg):
186 """Callback for stream.on_recv.
186 """Callback for stream.on_recv.
187
187
188 Unpacks message, and calls handlers with it.
188 Unpacks message, and calls handlers with it.
189 """
189 """
190 ident,smsg = self.session.feed_identities(msg)
190 ident,smsg = self.session.feed_identities(msg)
191 self.call_handlers(self.session.unserialize(smsg))
191 self.call_handlers(self.session.unserialize(smsg))
192
192
193
193
194
194
195 class ShellChannel(ZMQSocketChannel):
195 class ShellChannel(ZMQSocketChannel):
196 """The shell channel for issuing request/replies to the kernel."""
196 """The shell channel for issuing request/replies to the kernel."""
197
197
198 command_queue = None
198 command_queue = None
199 # flag for whether execute requests should be allowed to call raw_input:
199 # flag for whether execute requests should be allowed to call raw_input:
200 allow_stdin = True
200 allow_stdin = True
201
201
202 def __init__(self, context, session, address):
202 def __init__(self, context, session, address):
203 super(ShellChannel, self).__init__(context, session, address)
203 super(ShellChannel, self).__init__(context, session, address)
204 self.ioloop = ioloop.IOLoop()
204 self.ioloop = ioloop.IOLoop()
205
205
206 def run(self):
206 def run(self):
207 """The thread's main activity. Call start() instead."""
207 """The thread's main activity. Call start() instead."""
208 self.socket = self.context.socket(zmq.DEALER)
208 self.socket = self.context.socket(zmq.DEALER)
209 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
209 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
210 self.socket.connect(self.address)
210 self.socket.connect(self.address)
211 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
211 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
212 self.stream.on_recv(self._handle_recv)
212 self.stream.on_recv(self._handle_recv)
213 self._run_loop()
213 self._run_loop()
214 try:
214 try:
215 self.socket.close()
215 self.socket.close()
216 except:
216 except:
217 pass
217 pass
218
218
219 def stop(self):
219 def stop(self):
220 """Stop the channel's event loop and join its thread."""
220 """Stop the channel's event loop and join its thread."""
221 self.ioloop.stop()
221 self.ioloop.stop()
222 super(ShellChannel, self).stop()
222 super(ShellChannel, self).stop()
223
223
224 def call_handlers(self, msg):
224 def call_handlers(self, msg):
225 """This method is called in the ioloop thread when a message arrives.
225 """This method is called in the ioloop thread when a message arrives.
226
226
227 Subclasses should override this method to handle incoming messages.
227 Subclasses should override this method to handle incoming messages.
228 It is important to remember that this method is called in the thread
228 It is important to remember that this method is called in the thread
229 so that some logic must be done to ensure that the application leve
229 so that some logic must be done to ensure that the application leve
230 handlers are called in the application thread.
230 handlers are called in the application thread.
231 """
231 """
232 raise NotImplementedError('call_handlers must be defined in a subclass.')
232 raise NotImplementedError('call_handlers must be defined in a subclass.')
233
233
234 def execute(self, code, silent=False, store_history=True,
234 def execute(self, code, silent=False, store_history=True,
235 user_variables=None, user_expressions=None, allow_stdin=None):
235 user_variables=None, user_expressions=None, allow_stdin=None):
236 """Execute code in the kernel.
236 """Execute code in the kernel.
237
237
238 Parameters
238 Parameters
239 ----------
239 ----------
240 code : str
240 code : str
241 A string of Python code.
241 A string of Python code.
242
242
243 silent : bool, optional (default False)
243 silent : bool, optional (default False)
244 If set, the kernel will execute the code as quietly possible, and
244 If set, the kernel will execute the code as quietly possible, and
245 will force store_history to be False.
245 will force store_history to be False.
246
246
247 store_history : bool, optional (default True)
247 store_history : bool, optional (default True)
248 If set, the kernel will store command history. This is forced
248 If set, the kernel will store command history. This is forced
249 to be False if silent is True.
249 to be False if silent is True.
250
250
251 user_variables : list, optional
251 user_variables : list, optional
252 A list of variable names to pull from the user's namespace. They
252 A list of variable names to pull from the user's namespace. They
253 will come back as a dict with these names as keys and their
253 will come back as a dict with these names as keys and their
254 :func:`repr` as values.
254 :func:`repr` as values.
255
255
256 user_expressions : dict, optional
256 user_expressions : dict, optional
257 A dict mapping names to expressions to be evaluated in the user's
257 A dict mapping names to expressions to be evaluated in the user's
258 dict. The expression values are returned as strings formatted using
258 dict. The expression values are returned as strings formatted using
259 :func:`repr`.
259 :func:`repr`.
260
260
261 allow_stdin : bool, optional (default self.allow_stdin)
261 allow_stdin : bool, optional (default self.allow_stdin)
262 Flag for whether the kernel can send stdin requests to frontends.
262 Flag for whether the kernel can send stdin requests to frontends.
263
263
264 Some frontends (e.g. the Notebook) do not support stdin requests.
264 Some frontends (e.g. the Notebook) do not support stdin requests.
265 If raw_input is called from code executed from such a frontend, a
265 If raw_input is called from code executed from such a frontend, a
266 StdinNotImplementedError will be raised.
266 StdinNotImplementedError will be raised.
267
267
268 Returns
268 Returns
269 -------
269 -------
270 The msg_id of the message sent.
270 The msg_id of the message sent.
271 """
271 """
272 if user_variables is None:
272 if user_variables is None:
273 user_variables = []
273 user_variables = []
274 if user_expressions is None:
274 if user_expressions is None:
275 user_expressions = {}
275 user_expressions = {}
276 if allow_stdin is None:
276 if allow_stdin is None:
277 allow_stdin = self.allow_stdin
277 allow_stdin = self.allow_stdin
278
278
279
279
280 # Don't waste network traffic if inputs are invalid
280 # Don't waste network traffic if inputs are invalid
281 if not isinstance(code, basestring):
281 if not isinstance(code, basestring):
282 raise ValueError('code %r must be a string' % code)
282 raise ValueError('code %r must be a string' % code)
283 validate_string_list(user_variables)
283 validate_string_list(user_variables)
284 validate_string_dict(user_expressions)
284 validate_string_dict(user_expressions)
285
285
286 # Create class for content/msg creation. Related to, but possibly
286 # Create class for content/msg creation. Related to, but possibly
287 # not in Session.
287 # not in Session.
288 content = dict(code=code, silent=silent, store_history=store_history,
288 content = dict(code=code, silent=silent, store_history=store_history,
289 user_variables=user_variables,
289 user_variables=user_variables,
290 user_expressions=user_expressions,
290 user_expressions=user_expressions,
291 allow_stdin=allow_stdin,
291 allow_stdin=allow_stdin,
292 )
292 )
293 msg = self.session.msg('execute_request', content)
293 msg = self.session.msg('execute_request', content)
294 self._queue_send(msg)
294 self._queue_send(msg)
295 return msg['header']['msg_id']
295 return msg['header']['msg_id']
296
296
297 def complete(self, text, line, cursor_pos, block=None):
297 def complete(self, text, line, cursor_pos, block=None):
298 """Tab complete text in the kernel's namespace.
298 """Tab complete text in the kernel's namespace.
299
299
300 Parameters
300 Parameters
301 ----------
301 ----------
302 text : str
302 text : str
303 The text to complete.
303 The text to complete.
304 line : str
304 line : str
305 The full line of text that is the surrounding context for the
305 The full line of text that is the surrounding context for the
306 text to complete.
306 text to complete.
307 cursor_pos : int
307 cursor_pos : int
308 The position of the cursor in the line where the completion was
308 The position of the cursor in the line where the completion was
309 requested.
309 requested.
310 block : str, optional
310 block : str, optional
311 The full block of code in which the completion is being requested.
311 The full block of code in which the completion is being requested.
312
312
313 Returns
313 Returns
314 -------
314 -------
315 The msg_id of the message sent.
315 The msg_id of the message sent.
316 """
316 """
317 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
317 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
318 msg = self.session.msg('complete_request', content)
318 msg = self.session.msg('complete_request', content)
319 self._queue_send(msg)
319 self._queue_send(msg)
320 return msg['header']['msg_id']
320 return msg['header']['msg_id']
321
321
322 def object_info(self, oname, detail_level=0):
322 def object_info(self, oname, detail_level=0):
323 """Get metadata information about an object in the kernel's namespace.
323 """Get metadata information about an object in the kernel's namespace.
324
324
325 Parameters
325 Parameters
326 ----------
326 ----------
327 oname : str
327 oname : str
328 A string specifying the object name.
328 A string specifying the object name.
329 detail_level : int, optional
329 detail_level : int, optional
330 The level of detail for the introspection (0-2)
330 The level of detail for the introspection (0-2)
331
331
332 Returns
332 Returns
333 -------
333 -------
334 The msg_id of the message sent.
334 The msg_id of the message sent.
335 """
335 """
336 content = dict(oname=oname, detail_level=detail_level)
336 content = dict(oname=oname, detail_level=detail_level)
337 msg = self.session.msg('object_info_request', content)
337 msg = self.session.msg('object_info_request', content)
338 self._queue_send(msg)
338 self._queue_send(msg)
339 return msg['header']['msg_id']
339 return msg['header']['msg_id']
340
340
341 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
341 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
342 """Get entries from the kernel's history list.
342 """Get entries from the kernel's history list.
343
343
344 Parameters
344 Parameters
345 ----------
345 ----------
346 raw : bool
346 raw : bool
347 If True, return the raw input.
347 If True, return the raw input.
348 output : bool
348 output : bool
349 If True, then return the output as well.
349 If True, then return the output as well.
350 hist_access_type : str
350 hist_access_type : str
351 'range' (fill in session, start and stop params), 'tail' (fill in n)
351 'range' (fill in session, start and stop params), 'tail' (fill in n)
352 or 'search' (fill in pattern param).
352 or 'search' (fill in pattern param).
353
353
354 session : int
354 session : int
355 For a range request, the session from which to get lines. Session
355 For a range request, the session from which to get lines. Session
356 numbers are positive integers; negative ones count back from the
356 numbers are positive integers; negative ones count back from the
357 current session.
357 current session.
358 start : int
358 start : int
359 The first line number of a history range.
359 The first line number of a history range.
360 stop : int
360 stop : int
361 The final (excluded) line number of a history range.
361 The final (excluded) line number of a history range.
362
362
363 n : int
363 n : int
364 The number of lines of history to get for a tail request.
364 The number of lines of history to get for a tail request.
365
365
366 pattern : str
366 pattern : str
367 The glob-syntax pattern for a search request.
367 The glob-syntax pattern for a search request.
368
368
369 Returns
369 Returns
370 -------
370 -------
371 The msg_id of the message sent.
371 The msg_id of the message sent.
372 """
372 """
373 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
373 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
374 **kwargs)
374 **kwargs)
375 msg = self.session.msg('history_request', content)
375 msg = self.session.msg('history_request', content)
376 self._queue_send(msg)
376 self._queue_send(msg)
377 return msg['header']['msg_id']
377 return msg['header']['msg_id']
378
378
379 def kernel_info(self):
379 def kernel_info(self):
380 """Request kernel info."""
380 """Request kernel info."""
381 msg = self.session.msg('kernel_info_request')
381 msg = self.session.msg('kernel_info_request')
382 self._queue_send(msg)
382 self._queue_send(msg)
383 return msg['header']['msg_id']
383 return msg['header']['msg_id']
384
384
385 def shutdown(self, restart=False):
385 def shutdown(self, restart=False):
386 """Request an immediate kernel shutdown.
386 """Request an immediate kernel shutdown.
387
387
388 Upon receipt of the (empty) reply, client code can safely assume that
388 Upon receipt of the (empty) reply, client code can safely assume that
389 the kernel has shut down and it's safe to forcefully terminate it if
389 the kernel has shut down and it's safe to forcefully terminate it if
390 it's still alive.
390 it's still alive.
391
391
392 The kernel will send the reply via a function registered with Python's
392 The kernel will send the reply via a function registered with Python's
393 atexit module, ensuring it's truly done as the kernel is done with all
393 atexit module, ensuring it's truly done as the kernel is done with all
394 normal operation.
394 normal operation.
395 """
395 """
396 # Send quit message to kernel. Once we implement kernel-side setattr,
396 # Send quit message to kernel. Once we implement kernel-side setattr,
397 # this should probably be done that way, but for now this will do.
397 # this should probably be done that way, but for now this will do.
398 msg = self.session.msg('shutdown_request', {'restart':restart})
398 msg = self.session.msg('shutdown_request', {'restart':restart})
399 self._queue_send(msg)
399 self._queue_send(msg)
400 return msg['header']['msg_id']
400 return msg['header']['msg_id']
401
401
402
402
403
403
404 class IOPubChannel(ZMQSocketChannel):
404 class IOPubChannel(ZMQSocketChannel):
405 """The iopub channel which listens for messages that the kernel publishes.
405 """The iopub channel which listens for messages that the kernel publishes.
406
406
407 This channel is where all output is published to frontends.
407 This channel is where all output is published to frontends.
408 """
408 """
409
409
410 def __init__(self, context, session, address):
410 def __init__(self, context, session, address):
411 super(IOPubChannel, self).__init__(context, session, address)
411 super(IOPubChannel, self).__init__(context, session, address)
412 self.ioloop = ioloop.IOLoop()
412 self.ioloop = ioloop.IOLoop()
413
413
414 def run(self):
414 def run(self):
415 """The thread's main activity. Call start() instead."""
415 """The thread's main activity. Call start() instead."""
416 self.socket = self.context.socket(zmq.SUB)
416 self.socket = self.context.socket(zmq.SUB)
417 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
417 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
418 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
418 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
419 self.socket.connect(self.address)
419 self.socket.connect(self.address)
420 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
420 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
421 self.stream.on_recv(self._handle_recv)
421 self.stream.on_recv(self._handle_recv)
422 self._run_loop()
422 self._run_loop()
423 try:
423 try:
424 self.socket.close()
424 self.socket.close()
425 except:
425 except:
426 pass
426 pass
427
427
428 def stop(self):
428 def stop(self):
429 """Stop the channel's event loop and join its thread."""
429 """Stop the channel's event loop and join its thread."""
430 self.ioloop.stop()
430 self.ioloop.stop()
431 super(IOPubChannel, self).stop()
431 super(IOPubChannel, self).stop()
432
432
433 def call_handlers(self, msg):
433 def call_handlers(self, msg):
434 """This method is called in the ioloop thread when a message arrives.
434 """This method is called in the ioloop thread when a message arrives.
435
435
436 Subclasses should override this method to handle incoming messages.
436 Subclasses should override this method to handle incoming messages.
437 It is important to remember that this method is called in the thread
437 It is important to remember that this method is called in the thread
438 so that some logic must be done to ensure that the application leve
438 so that some logic must be done to ensure that the application leve
439 handlers are called in the application thread.
439 handlers are called in the application thread.
440 """
440 """
441 raise NotImplementedError('call_handlers must be defined in a subclass.')
441 raise NotImplementedError('call_handlers must be defined in a subclass.')
442
442
443 def flush(self, timeout=1.0):
443 def flush(self, timeout=1.0):
444 """Immediately processes all pending messages on the iopub channel.
444 """Immediately processes all pending messages on the iopub channel.
445
445
446 Callers should use this method to ensure that :method:`call_handlers`
446 Callers should use this method to ensure that :method:`call_handlers`
447 has been called for all messages that have been received on the
447 has been called for all messages that have been received on the
448 0MQ SUB socket of this channel.
448 0MQ SUB socket of this channel.
449
449
450 This method is thread safe.
450 This method is thread safe.
451
451
452 Parameters
452 Parameters
453 ----------
453 ----------
454 timeout : float, optional
454 timeout : float, optional
455 The maximum amount of time to spend flushing, in seconds. The
455 The maximum amount of time to spend flushing, in seconds. The
456 default is one second.
456 default is one second.
457 """
457 """
458 # We do the IOLoop callback process twice to ensure that the IOLoop
458 # We do the IOLoop callback process twice to ensure that the IOLoop
459 # gets to perform at least one full poll.
459 # gets to perform at least one full poll.
460 stop_time = time.time() + timeout
460 stop_time = time.time() + timeout
461 for i in xrange(2):
461 for i in xrange(2):
462 self._flushed = False
462 self._flushed = False
463 self.ioloop.add_callback(self._flush)
463 self.ioloop.add_callback(self._flush)
464 while not self._flushed and time.time() < stop_time:
464 while not self._flushed and time.time() < stop_time:
465 time.sleep(0.01)
465 time.sleep(0.01)
466
466
467 def _flush(self):
467 def _flush(self):
468 """Callback for :method:`self.flush`."""
468 """Callback for :method:`self.flush`."""
469 self.stream.flush()
469 self.stream.flush()
470 self._flushed = True
470 self._flushed = True
471
471
472
472
473 class StdInChannel(ZMQSocketChannel):
473 class StdInChannel(ZMQSocketChannel):
474 """The stdin channel to handle raw_input requests that the kernel makes."""
474 """The stdin channel to handle raw_input requests that the kernel makes."""
475
475
476 msg_queue = None
476 msg_queue = None
477
477
478 def __init__(self, context, session, address):
478 def __init__(self, context, session, address):
479 super(StdInChannel, self).__init__(context, session, address)
479 super(StdInChannel, self).__init__(context, session, address)
480 self.ioloop = ioloop.IOLoop()
480 self.ioloop = ioloop.IOLoop()
481
481
482 def run(self):
482 def run(self):
483 """The thread's main activity. Call start() instead."""
483 """The thread's main activity. Call start() instead."""
484 self.socket = self.context.socket(zmq.DEALER)
484 self.socket = self.context.socket(zmq.DEALER)
485 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
485 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
486 self.socket.connect(self.address)
486 self.socket.connect(self.address)
487 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
487 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
488 self.stream.on_recv(self._handle_recv)
488 self.stream.on_recv(self._handle_recv)
489 self._run_loop()
489 self._run_loop()
490 try:
490 try:
491 self.socket.close()
491 self.socket.close()
492 except:
492 except:
493 pass
493 pass
494
494
495 def stop(self):
495 def stop(self):
496 """Stop the channel's event loop and join its thread."""
496 """Stop the channel's event loop and join its thread."""
497 self.ioloop.stop()
497 self.ioloop.stop()
498 super(StdInChannel, self).stop()
498 super(StdInChannel, self).stop()
499
499
500 def call_handlers(self, msg):
500 def call_handlers(self, msg):
501 """This method is called in the ioloop thread when a message arrives.
501 """This method is called in the ioloop thread when a message arrives.
502
502
503 Subclasses should override this method to handle incoming messages.
503 Subclasses should override this method to handle incoming messages.
504 It is important to remember that this method is called in the thread
504 It is important to remember that this method is called in the thread
505 so that some logic must be done to ensure that the application leve
505 so that some logic must be done to ensure that the application leve
506 handlers are called in the application thread.
506 handlers are called in the application thread.
507 """
507 """
508 raise NotImplementedError('call_handlers must be defined in a subclass.')
508 raise NotImplementedError('call_handlers must be defined in a subclass.')
509
509
510 def input(self, string):
510 def input(self, string):
511 """Send a string of raw input to the kernel."""
511 """Send a string of raw input to the kernel."""
512 content = dict(value=string)
512 content = dict(value=string)
513 msg = self.session.msg('input_reply', content)
513 msg = self.session.msg('input_reply', content)
514 self._queue_send(msg)
514 self._queue_send(msg)
515
515
516
516
517 class HBChannel(ZMQSocketChannel):
517 class HBChannel(ZMQSocketChannel):
518 """The heartbeat channel which monitors the kernel heartbeat.
518 """The heartbeat channel which monitors the kernel heartbeat.
519
519
520 Note that the heartbeat channel is paused by default. As long as you start
520 Note that the heartbeat channel is paused by default. As long as you start
521 this channel, the kernel manager will ensure that it is paused and un-paused
521 this channel, the kernel manager will ensure that it is paused and un-paused
522 as appropriate.
522 as appropriate.
523 """
523 """
524
524
525 time_to_dead = 3.0
525 time_to_dead = 3.0
526 socket = None
526 socket = None
527 poller = None
527 poller = None
528 _running = None
528 _running = None
529 _pause = None
529 _pause = None
530 _beating = None
530 _beating = None
531
531
532 def __init__(self, context, session, address):
532 def __init__(self, context, session, address):
533 super(HBChannel, self).__init__(context, session, address)
533 super(HBChannel, self).__init__(context, session, address)
534 self._running = False
534 self._running = False
535 self._pause =True
535 self._pause =True
536 self.poller = zmq.Poller()
536 self.poller = zmq.Poller()
537
537
538 def _create_socket(self):
538 def _create_socket(self):
539 if self.socket is not None:
539 if self.socket is not None:
540 # close previous socket, before opening a new one
540 # close previous socket, before opening a new one
541 self.poller.unregister(self.socket)
541 self.poller.unregister(self.socket)
542 self.socket.close()
542 self.socket.close()
543 self.socket = self.context.socket(zmq.REQ)
543 self.socket = self.context.socket(zmq.REQ)
544 self.socket.setsockopt(zmq.LINGER, 0)
544 self.socket.setsockopt(zmq.LINGER, 0)
545 self.socket.connect(self.address)
545 self.socket.connect(self.address)
546
546
547 self.poller.register(self.socket, zmq.POLLIN)
547 self.poller.register(self.socket, zmq.POLLIN)
548
548
549 def _poll(self, start_time):
549 def _poll(self, start_time):
550 """poll for heartbeat replies until we reach self.time_to_dead.
550 """poll for heartbeat replies until we reach self.time_to_dead.
551
551
552 Ignores interrupts, and returns the result of poll(), which
552 Ignores interrupts, and returns the result of poll(), which
553 will be an empty list if no messages arrived before the timeout,
553 will be an empty list if no messages arrived before the timeout,
554 or the event tuple if there is a message to receive.
554 or the event tuple if there is a message to receive.
555 """
555 """
556
556
557 until_dead = self.time_to_dead - (time.time() - start_time)
557 until_dead = self.time_to_dead - (time.time() - start_time)
558 # ensure poll at least once
558 # ensure poll at least once
559 until_dead = max(until_dead, 1e-3)
559 until_dead = max(until_dead, 1e-3)
560 events = []
560 events = []
561 while True:
561 while True:
562 try:
562 try:
563 events = self.poller.poll(1000 * until_dead)
563 events = self.poller.poll(1000 * until_dead)
564 except ZMQError as e:
564 except ZMQError as e:
565 if e.errno == errno.EINTR:
565 if e.errno == errno.EINTR:
566 # ignore interrupts during heartbeat
566 # ignore interrupts during heartbeat
567 # this may never actually happen
567 # this may never actually happen
568 until_dead = self.time_to_dead - (time.time() - start_time)
568 until_dead = self.time_to_dead - (time.time() - start_time)
569 until_dead = max(until_dead, 1e-3)
569 until_dead = max(until_dead, 1e-3)
570 pass
570 pass
571 else:
571 else:
572 raise
572 raise
573 except Exception:
573 except Exception:
574 if self._exiting:
574 if self._exiting:
575 break
575 break
576 else:
576 else:
577 raise
577 raise
578 else:
578 else:
579 break
579 break
580 return events
580 return events
581
581
582 def run(self):
582 def run(self):
583 """The thread's main activity. Call start() instead."""
583 """The thread's main activity. Call start() instead."""
584 self._create_socket()
584 self._create_socket()
585 self._running = True
585 self._running = True
586 self._beating = True
586 self._beating = True
587
587
588 while self._running:
588 while self._running:
589 if self._pause:
589 if self._pause:
590 # just sleep, and skip the rest of the loop
590 # just sleep, and skip the rest of the loop
591 time.sleep(self.time_to_dead)
591 time.sleep(self.time_to_dead)
592 continue
592 continue
593
593
594 since_last_heartbeat = 0.0
594 since_last_heartbeat = 0.0
595 # io.rprint('Ping from HB channel') # dbg
595 # io.rprint('Ping from HB channel') # dbg
596 # no need to catch EFSM here, because the previous event was
596 # no need to catch EFSM here, because the previous event was
597 # either a recv or connect, which cannot be followed by EFSM
597 # either a recv or connect, which cannot be followed by EFSM
598 self.socket.send(b'ping')
598 self.socket.send(b'ping')
599 request_time = time.time()
599 request_time = time.time()
600 ready = self._poll(request_time)
600 ready = self._poll(request_time)
601 if ready:
601 if ready:
602 self._beating = True
602 self._beating = True
603 # the poll above guarantees we have something to recv
603 # the poll above guarantees we have something to recv
604 self.socket.recv()
604 self.socket.recv()
605 # sleep the remainder of the cycle
605 # sleep the remainder of the cycle
606 remainder = self.time_to_dead - (time.time() - request_time)
606 remainder = self.time_to_dead - (time.time() - request_time)
607 if remainder > 0:
607 if remainder > 0:
608 time.sleep(remainder)
608 time.sleep(remainder)
609 continue
609 continue
610 else:
610 else:
611 # nothing was received within the time limit, signal heart failure
611 # nothing was received within the time limit, signal heart failure
612 self._beating = False
612 self._beating = False
613 since_last_heartbeat = time.time() - request_time
613 since_last_heartbeat = time.time() - request_time
614 self.call_handlers(since_last_heartbeat)
614 self.call_handlers(since_last_heartbeat)
615 # and close/reopen the socket, because the REQ/REP cycle has been broken
615 # and close/reopen the socket, because the REQ/REP cycle has been broken
616 self._create_socket()
616 self._create_socket()
617 continue
617 continue
618 try:
618 try:
619 self.socket.close()
619 self.socket.close()
620 except:
620 except:
621 pass
621 pass
622
622
623 def pause(self):
623 def pause(self):
624 """Pause the heartbeat."""
624 """Pause the heartbeat."""
625 self._pause = True
625 self._pause = True
626
626
627 def unpause(self):
627 def unpause(self):
628 """Unpause the heartbeat."""
628 """Unpause the heartbeat."""
629 self._pause = False
629 self._pause = False
630
630
631 def is_beating(self):
631 def is_beating(self):
632 """Is the heartbeat running and responsive (and not paused)."""
632 """Is the heartbeat running and responsive (and not paused)."""
633 if self.is_alive() and not self._pause and self._beating:
633 if self.is_alive() and not self._pause and self._beating:
634 return True
634 return True
635 else:
635 else:
636 return False
636 return False
637
637
638 def stop(self):
638 def stop(self):
639 """Stop the channel's event loop and join its thread."""
639 """Stop the channel's event loop and join its thread."""
640 self._running = False
640 self._running = False
641 super(HBChannel, self).stop()
641 super(HBChannel, self).stop()
642
642
643 def call_handlers(self, since_last_heartbeat):
643 def call_handlers(self, since_last_heartbeat):
644 """This method is called in the ioloop thread when a message arrives.
644 """This method is called in the ioloop thread when a message arrives.
645
645
646 Subclasses should override this method to handle incoming messages.
646 Subclasses should override this method to handle incoming messages.
647 It is important to remember that this method is called in the thread
647 It is important to remember that this method is called in the thread
648 so that some logic must be done to ensure that the application level
648 so that some logic must be done to ensure that the application level
649 handlers are called in the application thread.
649 handlers are called in the application thread.
650 """
650 """
651 raise NotImplementedError('call_handlers must be defined in a subclass.')
651 raise NotImplementedError('call_handlers must be defined in a subclass.')
652
652
653
653
654 #-----------------------------------------------------------------------------
654 #-----------------------------------------------------------------------------
655 # Main kernel manager class
655 # Main kernel manager class
656 #-----------------------------------------------------------------------------
656 #-----------------------------------------------------------------------------
657
657
658 class KernelManager(Configurable):
658 class KernelManager(Configurable):
659 """Manages a single kernel on this host along with its channels.
659 """Manages a single kernel on this host along with its channels.
660
660
661 There are four channels associated with each kernel:
661 There are four channels associated with each kernel:
662
662
663 * shell: for request/reply calls to the kernel.
663 * shell: for request/reply calls to the kernel.
664 * iopub: for the kernel to publish results to frontends.
664 * iopub: for the kernel to publish results to frontends.
665 * hb: for monitoring the kernel's heartbeat.
665 * hb: for monitoring the kernel's heartbeat.
666 * stdin: for frontends to reply to raw_input calls in the kernel.
666 * stdin: for frontends to reply to raw_input calls in the kernel.
667
667
668 The usage of the channels that this class manages is optional. It is
668 The usage of the channels that this class manages is optional. It is
669 entirely possible to connect to the kernels directly using ZeroMQ
669 entirely possible to connect to the kernels directly using ZeroMQ
670 sockets. These channels are useful primarily for talking to a kernel
670 sockets. These channels are useful primarily for talking to a kernel
671 whose :class:`KernelManager` is in the same process.
671 whose :class:`KernelManager` is in the same process.
672
672
673 This version manages kernels started using Popen.
673 This version manages kernels started using Popen.
674 """
674 """
675 # The PyZMQ Context to use for communication with the kernel.
675 # The PyZMQ Context to use for communication with the kernel.
676 context = Instance(zmq.Context)
676 context = Instance(zmq.Context)
677 def _context_default(self):
677 def _context_default(self):
678 return zmq.Context.instance()
678 return zmq.Context.instance()
679
679
680 # The Session to use for communication with the kernel.
680 # The Session to use for communication with the kernel.
681 session = Instance(Session)
681 session = Instance(Session)
682 def _session_default(self):
682 def _session_default(self):
683 return Session(config=self.config)
683 return Session(config=self.config)
684
684
685 # The kernel process with which the KernelManager is communicating.
685 # The kernel process with which the KernelManager is communicating.
686 # generally a Popen instance
686 # generally a Popen instance
687 kernel = Any()
687 kernel = Any()
688
688
689 kernel_cmd = List(Unicode, config=True,
689 kernel_cmd = List(Unicode, config=True,
690 help="""The Popen Command to launch the kernel.
690 help="""The Popen Command to launch the kernel.
691 Override this if you have a custom
691 Override this if you have a custom
692 """
692 """
693 )
693 )
694 def _kernel_cmd_changed(self, name, old, new):
694 def _kernel_cmd_changed(self, name, old, new):
695 self.ipython_kernel = False
695 self.ipython_kernel = False
696
696
697 ipython_kernel = Bool(True)
697 ipython_kernel = Bool(True)
698
698
699
699
700 # The addresses for the communication channels.
700 # The addresses for the communication channels.
701 connection_file = Unicode('')
701 connection_file = Unicode('')
702
702
703 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
703 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
704
704
705 ip = Unicode(LOCALHOST, config=True,
705 ip = Unicode(LOCALHOST, config=True,
706 help="""Set the kernel\'s IP address [default localhost].
706 help="""Set the kernel\'s IP address [default localhost].
707 If the IP address is something other than localhost, then
707 If the IP address is something other than localhost, then
708 Consoles on other machines will be able to connect
708 Consoles on other machines will be able to connect
709 to the Kernel, so be careful!"""
709 to the Kernel, so be careful!"""
710 )
710 )
711 def _ip_default(self):
711 def _ip_default(self):
712 if self.transport == 'ipc':
712 if self.transport == 'ipc':
713 if self.connection_file:
713 if self.connection_file:
714 return os.path.splitext(self.connection_file)[0] + '-ipc'
714 return os.path.splitext(self.connection_file)[0] + '-ipc'
715 else:
715 else:
716 return 'kernel-ipc'
716 return 'kernel-ipc'
717 else:
717 else:
718 return LOCALHOST
718 return LOCALHOST
719 def _ip_changed(self, name, old, new):
719 def _ip_changed(self, name, old, new):
720 if new == '*':
720 if new == '*':
721 self.ip = '0.0.0.0'
721 self.ip = '0.0.0.0'
722 shell_port = Integer(0)
722 shell_port = Integer(0)
723 iopub_port = Integer(0)
723 iopub_port = Integer(0)
724 stdin_port = Integer(0)
724 stdin_port = Integer(0)
725 hb_port = Integer(0)
725 hb_port = Integer(0)
726
726
727 # The classes to use for the various channels.
727 # The classes to use for the various channels.
728 shell_channel_class = Type(ShellChannel)
728 shell_channel_class = Type(ShellChannel)
729 iopub_channel_class = Type(IOPubChannel)
729 iopub_channel_class = Type(IOPubChannel)
730 stdin_channel_class = Type(StdInChannel)
730 stdin_channel_class = Type(StdInChannel)
731 hb_channel_class = Type(HBChannel)
731 hb_channel_class = Type(HBChannel)
732
732
733 # Protected traits.
733 # Protected traits.
734 _launch_args = Any
734 _launch_args = Any
735 _shell_channel = Any
735 _shell_channel = Any
736 _iopub_channel = Any
736 _iopub_channel = Any
737 _stdin_channel = Any
737 _stdin_channel = Any
738 _hb_channel = Any
738 _hb_channel = Any
739 _connection_file_written=Bool(False)
739 _connection_file_written=Bool(False)
740
740
741 def __del__(self):
741 def __del__(self):
742 self.cleanup_connection_file()
742 self.cleanup_connection_file()
743
743
744 #--------------------------------------------------------------------------
744 #--------------------------------------------------------------------------
745 # Channel management methods:
745 # Channel management methods:
746 #--------------------------------------------------------------------------
746 #--------------------------------------------------------------------------
747
747
748 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
748 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
749 """Starts the channels for this kernel.
749 """Starts the channels for this kernel.
750
750
751 This will create the channels if they do not exist and then start
751 This will create the channels if they do not exist and then start
752 them (their activity runs in a thread). If port numbers of 0 are
752 them (their activity runs in a thread). If port numbers of 0 are
753 being used (random ports) then you must first call
753 being used (random ports) then you must first call
754 :method:`start_kernel`. If the channels have been stopped and you
754 :method:`start_kernel`. If the channels have been stopped and you
755 call this, :class:`RuntimeError` will be raised.
755 call this, :class:`RuntimeError` will be raised.
756 """
756 """
757 if shell:
757 if shell:
758 self.shell_channel.start()
758 self.shell_channel.start()
759 if iopub:
759 if iopub:
760 self.iopub_channel.start()
760 self.iopub_channel.start()
761 if stdin:
761 if stdin:
762 self.stdin_channel.start()
762 self.stdin_channel.start()
763 self.shell_channel.allow_stdin = True
763 self.shell_channel.allow_stdin = True
764 else:
764 else:
765 self.shell_channel.allow_stdin = False
765 self.shell_channel.allow_stdin = False
766 if hb:
766 if hb:
767 self.hb_channel.start()
767 self.hb_channel.start()
768
768
769 def stop_channels(self):
769 def stop_channels(self):
770 """Stops all the running channels for this kernel.
770 """Stops all the running channels for this kernel.
771
771
772 This stops their event loops and joins their threads.
772 This stops their event loops and joins their threads.
773 """
773 """
774 if self.shell_channel.is_alive():
774 if self.shell_channel.is_alive():
775 self.shell_channel.stop()
775 self.shell_channel.stop()
776 if self.iopub_channel.is_alive():
776 if self.iopub_channel.is_alive():
777 self.iopub_channel.stop()
777 self.iopub_channel.stop()
778 if self.stdin_channel.is_alive():
778 if self.stdin_channel.is_alive():
779 self.stdin_channel.stop()
779 self.stdin_channel.stop()
780 if self.hb_channel.is_alive():
780 if self.hb_channel.is_alive():
781 self.hb_channel.stop()
781 self.hb_channel.stop()
782
782
783 @property
783 @property
784 def channels_running(self):
784 def channels_running(self):
785 """Are any of the channels created and running?"""
785 """Are any of the channels created and running?"""
786 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
786 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
787 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
787 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
788
788
789 def _make_url(self, port):
789 def _make_url(self, port):
790 """Make a zmq url with a port.
790 """Make a zmq url with a port.
791
791
792 There are two cases that this handles:
792 There are two cases that this handles:
793
793
794 * tcp: tcp://ip:port
794 * tcp: tcp://ip:port
795 * ipc: ipc://ip-port
795 * ipc: ipc://ip-port
796 """
796 """
797 if self.transport == 'tcp':
797 if self.transport == 'tcp':
798 return "tcp://%s:%i" % (self.ip, port)
798 return "tcp://%s:%i" % (self.ip, port)
799 else:
799 else:
800 return "%s://%s-%s" % (self.transport, self.ip, port)
800 return "%s://%s-%s" % (self.transport, self.ip, port)
801
801
802 @property
802 @property
803 def shell_channel(self):
803 def shell_channel(self):
804 """Get the shell channel object for this kernel."""
804 """Get the shell channel object for this kernel."""
805 if self._shell_channel is None:
805 if self._shell_channel is None:
806 self._shell_channel = self.shell_channel_class(
806 self._shell_channel = self.shell_channel_class(
807 self.context, self.session, self._make_url(self.shell_port)
807 self.context, self.session, self._make_url(self.shell_port)
808 )
808 )
809 return self._shell_channel
809 return self._shell_channel
810
810
811 @property
811 @property
812 def iopub_channel(self):
812 def iopub_channel(self):
813 """Get the iopub channel object for this kernel."""
813 """Get the iopub channel object for this kernel."""
814 if self._iopub_channel is None:
814 if self._iopub_channel is None:
815 self._iopub_channel = self.iopub_channel_class(
815 self._iopub_channel = self.iopub_channel_class(
816 self.context, self.session, self._make_url(self.iopub_port)
816 self.context, self.session, self._make_url(self.iopub_port)
817 )
817 )
818 return self._iopub_channel
818 return self._iopub_channel
819
819
820 @property
820 @property
821 def stdin_channel(self):
821 def stdin_channel(self):
822 """Get the stdin channel object for this kernel."""
822 """Get the stdin channel object for this kernel."""
823 if self._stdin_channel is None:
823 if self._stdin_channel is None:
824 self._stdin_channel = self.stdin_channel_class(
824 self._stdin_channel = self.stdin_channel_class(
825 self.context, self.session, self._make_url(self.stdin_port)
825 self.context, self.session, self._make_url(self.stdin_port)
826 )
826 )
827 return self._stdin_channel
827 return self._stdin_channel
828
828
829 @property
829 @property
830 def hb_channel(self):
830 def hb_channel(self):
831 """Get the hb channel object for this kernel."""
831 """Get the hb channel object for this kernel."""
832 if self._hb_channel is None:
832 if self._hb_channel is None:
833 self._hb_channel = self.hb_channel_class(
833 self._hb_channel = self.hb_channel_class(
834 self.context, self.session, self._make_url(self.hb_port)
834 self.context, self.session, self._make_url(self.hb_port)
835 )
835 )
836 return self._hb_channel
836 return self._hb_channel
837
837
838 #--------------------------------------------------------------------------
838 #--------------------------------------------------------------------------
839 # Connection and ipc file management
839 # Connection and ipc file management
840 #--------------------------------------------------------------------------
840 #--------------------------------------------------------------------------
841
841
842 def cleanup_connection_file(self):
842 def cleanup_connection_file(self):
843 """Cleanup connection file *if we wrote it*
843 """Cleanup connection file *if we wrote it*
844
844
845 Will not raise if the connection file was already removed somehow.
845 Will not raise if the connection file was already removed somehow.
846 """
846 """
847 if self._connection_file_written:
847 if self._connection_file_written:
848 # cleanup connection files on full shutdown of kernel we started
848 # cleanup connection files on full shutdown of kernel we started
849 self._connection_file_written = False
849 self._connection_file_written = False
850 try:
850 try:
851 os.remove(self.connection_file)
851 os.remove(self.connection_file)
852 except (IOError, OSError, AttributeError):
852 except (IOError, OSError, AttributeError):
853 pass
853 pass
854
854
855 def cleanup_ipc_files(self):
855 def cleanup_ipc_files(self):
856 """Cleanup ipc files if we wrote them."""
856 """Cleanup ipc files if we wrote them."""
857 if self.transport != 'ipc':
857 if self.transport != 'ipc':
858 return
858 return
859 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
859 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
860 ipcfile = "%s-%i" % (self.ip, port)
860 ipcfile = "%s-%i" % (self.ip, port)
861 try:
861 try:
862 os.remove(ipcfile)
862 os.remove(ipcfile)
863 except (IOError, OSError):
863 except (IOError, OSError):
864 pass
864 pass
865
865
866 def load_connection_file(self):
866 def load_connection_file(self):
867 """Load connection info from JSON dict in self.connection_file."""
867 """Load connection info from JSON dict in self.connection_file."""
868 with open(self.connection_file) as f:
868 with open(self.connection_file) as f:
869 cfg = json.loads(f.read())
869 cfg = json.loads(f.read())
870
870
871 from pprint import pprint
871 from pprint import pprint
872 pprint(cfg)
872 pprint(cfg)
873 self.transport = cfg.get('transport', 'tcp')
873 self.transport = cfg.get('transport', 'tcp')
874 self.ip = cfg['ip']
874 self.ip = cfg['ip']
875 self.shell_port = cfg['shell_port']
875 self.shell_port = cfg['shell_port']
876 self.stdin_port = cfg['stdin_port']
876 self.stdin_port = cfg['stdin_port']
877 self.iopub_port = cfg['iopub_port']
877 self.iopub_port = cfg['iopub_port']
878 self.hb_port = cfg['hb_port']
878 self.hb_port = cfg['hb_port']
879 self.session.key = str_to_bytes(cfg['key'])
879 self.session.key = str_to_bytes(cfg['key'])
880
880
881 def write_connection_file(self):
881 def write_connection_file(self):
882 """Write connection info to JSON dict in self.connection_file."""
882 """Write connection info to JSON dict in self.connection_file."""
883 if self._connection_file_written:
883 if self._connection_file_written:
884 return
884 return
885 self.connection_file,cfg = write_connection_file(self.connection_file,
885 self.connection_file,cfg = write_connection_file(self.connection_file,
886 transport=self.transport, ip=self.ip, key=self.session.key,
886 transport=self.transport, ip=self.ip, key=self.session.key,
887 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
887 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
888 shell_port=self.shell_port, hb_port=self.hb_port)
888 shell_port=self.shell_port, hb_port=self.hb_port)
889 # write_connection_file also sets default ports:
889 # write_connection_file also sets default ports:
890 self.shell_port = cfg['shell_port']
890 self.shell_port = cfg['shell_port']
891 self.stdin_port = cfg['stdin_port']
891 self.stdin_port = cfg['stdin_port']
892 self.iopub_port = cfg['iopub_port']
892 self.iopub_port = cfg['iopub_port']
893 self.hb_port = cfg['hb_port']
893 self.hb_port = cfg['hb_port']
894
894
895 self._connection_file_written = True
895 self._connection_file_written = True
896
896
897 #--------------------------------------------------------------------------
897 #--------------------------------------------------------------------------
898 # Kernel management
898 # Kernel management
899 #--------------------------------------------------------------------------
899 #--------------------------------------------------------------------------
900
900
901 def format_kernel_cmd(self, **kw):
901 def format_kernel_cmd(self, **kw):
902 """format templated args (e.g. {connection_file})"""
902 """format templated args (e.g. {connection_file})"""
903 if self.kernel_cmd:
903 if self.kernel_cmd:
904 cmd = self.kernel_cmd
904 cmd = self.kernel_cmd
905 else:
905 else:
906 cmd = make_ipkernel_cmd(
906 cmd = make_ipkernel_cmd(
907 'from IPython.kernel.zmq.kernelapp import main; main()',
907 'from IPython.kernel.zmq.kernelapp import main; main()',
908 **kw
908 **kw
909 )
909 )
910 ns = dict(connection_file=self.connection_file)
910 ns = dict(connection_file=self.connection_file)
911 ns.update(self._launch_args)
911 ns.update(self._launch_args)
912 return [ c.format(**ns) for c in cmd ]
912 return [ c.format(**ns) for c in cmd ]
913
913
914 def _launch_kernel(self, kernel_cmd, **kw):
914 def _launch_kernel(self, kernel_cmd, **kw):
915 """actually launch the kernel
915 """actually launch the kernel
916
916
917 override in a subclass to launch kernel subprocesses differently
917 override in a subclass to launch kernel subprocesses differently
918 """
918 """
919 return launch_kernel(kernel_cmd, **kw)
919 return launch_kernel(kernel_cmd, **kw)
920
920
921 def start_kernel(self, **kw):
921 def start_kernel(self, **kw):
922 """Starts a kernel on this host in a separate process.
922 """Starts a kernel on this host in a separate process.
923
923
924 If random ports (port=0) are being used, this method must be called
924 If random ports (port=0) are being used, this method must be called
925 before the channels are created.
925 before the channels are created.
926
926
927 Parameters:
927 Parameters:
928 -----------
928 -----------
929 **kw : optional
929 **kw : optional
930 keyword arguments that are passed down to build the kernel_cmd
930 keyword arguments that are passed down to build the kernel_cmd
931 and launching the kernel (e.g. Popen kwargs).
931 and launching the kernel (e.g. Popen kwargs).
932 """
932 """
933 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
933 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
934 raise RuntimeError("Can only launch a kernel on a local interface. "
934 raise RuntimeError("Can only launch a kernel on a local interface. "
935 "Make sure that the '*_address' attributes are "
935 "Make sure that the '*_address' attributes are "
936 "configured properly. "
936 "configured properly. "
937 "Currently valid addresses are: %s"%LOCAL_IPS
937 "Currently valid addresses are: %s"%LOCAL_IPS
938 )
938 )
939
939
940 # write connection file / get default ports
940 # write connection file / get default ports
941 self.write_connection_file()
941 self.write_connection_file()
942
942
943 # save kwargs for use in restart
943 # save kwargs for use in restart
944 self._launch_args = kw.copy()
944 self._launch_args = kw.copy()
945 # build the Popen cmd
945 # build the Popen cmd
946 kernel_cmd = self.format_kernel_cmd(**kw)
946 kernel_cmd = self.format_kernel_cmd(**kw)
947 # launch the kernel subprocess
947 # launch the kernel subprocess
948 self.kernel = self._launch_kernel(kernel_cmd,
948 self.kernel = self._launch_kernel(kernel_cmd,
949 ipython_kernel=self.ipython_kernel,
949 ipython_kernel=self.ipython_kernel,
950 **kw)
950 **kw)
951
951
952 def shutdown_kernel(self, now=False, restart=False):
952 def shutdown_kernel(self, now=False, restart=False):
953 """Attempts to the stop the kernel process cleanly.
953 """Attempts to the stop the kernel process cleanly.
954
954
955 This attempts to shutdown the kernels cleanly by:
955 This attempts to shutdown the kernels cleanly by:
956
956
957 1. Sending it a shutdown message over the shell channel.
957 1. Sending it a shutdown message over the shell channel.
958 2. If that fails, the kernel is shutdown forcibly by sending it
958 2. If that fails, the kernel is shutdown forcibly by sending it
959 a signal.
959 a signal.
960
960
961 Parameters:
961 Parameters:
962 -----------
962 -----------
963 now : bool
963 now : bool
964 Should the kernel be forcible killed *now*. This skips the
964 Should the kernel be forcible killed *now*. This skips the
965 first, nice shutdown attempt.
965 first, nice shutdown attempt.
966 restart: bool
966 restart: bool
967 Will this kernel be restarted after it is shutdown. When this
967 Will this kernel be restarted after it is shutdown. When this
968 is True, connection files will not be cleaned up.
968 is True, connection files will not be cleaned up.
969 """
969 """
970 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
970 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
971 if sys.platform == 'win32':
971 if sys.platform == 'win32':
972 self._kill_kernel()
972 self._kill_kernel()
973 return
973 return
974
974
975 # Pause the heart beat channel if it exists.
975 # Pause the heart beat channel if it exists.
976 if self._hb_channel is not None:
976 if self._hb_channel is not None:
977 self._hb_channel.pause()
977 self._hb_channel.pause()
978
978
979 if now:
979 if now:
980 if self.has_kernel:
980 if self.has_kernel:
981 self._kill_kernel()
981 self._kill_kernel()
982 else:
982 else:
983 # Don't send any additional kernel kill messages immediately, to give
983 # Don't send any additional kernel kill messages immediately, to give
984 # the kernel a chance to properly execute shutdown actions. Wait for at
984 # the kernel a chance to properly execute shutdown actions. Wait for at
985 # most 1s, checking every 0.1s.
985 # most 1s, checking every 0.1s.
986 self.shell_channel.shutdown(restart=restart)
986 self.shell_channel.shutdown(restart=restart)
987 for i in range(10):
987 for i in range(10):
988 if self.is_alive:
988 if self.is_alive():
989 time.sleep(0.1)
989 time.sleep(0.1)
990 else:
990 else:
991 break
991 break
992 else:
992 else:
993 # OK, we've waited long enough.
993 # OK, we've waited long enough.
994 if self.has_kernel:
994 if self.has_kernel:
995 self._kill_kernel()
995 self._kill_kernel()
996
996
997 if not restart:
997 if not restart:
998 self.cleanup_connection_file()
998 self.cleanup_connection_file()
999 self.cleanup_ipc_files()
999 self.cleanup_ipc_files()
1000 else:
1000 else:
1001 self.cleanup_ipc_files()
1001 self.cleanup_ipc_files()
1002
1002
1003 def restart_kernel(self, now=False, **kw):
1003 def restart_kernel(self, now=False, **kw):
1004 """Restarts a kernel with the arguments that were used to launch it.
1004 """Restarts a kernel with the arguments that were used to launch it.
1005
1005
1006 If the old kernel was launched with random ports, the same ports will be
1006 If the old kernel was launched with random ports, the same ports will be
1007 used for the new kernel. The same connection file is used again.
1007 used for the new kernel. The same connection file is used again.
1008
1008
1009 Parameters
1009 Parameters
1010 ----------
1010 ----------
1011 now : bool, optional
1011 now : bool, optional
1012 If True, the kernel is forcefully restarted *immediately*, without
1012 If True, the kernel is forcefully restarted *immediately*, without
1013 having a chance to do any cleanup action. Otherwise the kernel is
1013 having a chance to do any cleanup action. Otherwise the kernel is
1014 given 1s to clean up before a forceful restart is issued.
1014 given 1s to clean up before a forceful restart is issued.
1015
1015
1016 In all cases the kernel is restarted, the only difference is whether
1016 In all cases the kernel is restarted, the only difference is whether
1017 it is given a chance to perform a clean shutdown or not.
1017 it is given a chance to perform a clean shutdown or not.
1018
1018
1019 **kw : optional
1019 **kw : optional
1020 Any options specified here will overwrite those used to launch the
1020 Any options specified here will overwrite those used to launch the
1021 kernel.
1021 kernel.
1022 """
1022 """
1023 if self._launch_args is None:
1023 if self._launch_args is None:
1024 raise RuntimeError("Cannot restart the kernel. "
1024 raise RuntimeError("Cannot restart the kernel. "
1025 "No previous call to 'start_kernel'.")
1025 "No previous call to 'start_kernel'.")
1026 else:
1026 else:
1027 # Stop currently running kernel.
1027 # Stop currently running kernel.
1028 self.shutdown_kernel(now=now, restart=True)
1028 self.shutdown_kernel(now=now, restart=True)
1029
1029
1030 # Start new kernel.
1030 # Start new kernel.
1031 self._launch_args.update(kw)
1031 self._launch_args.update(kw)
1032 self.start_kernel(**self._launch_args)
1032 self.start_kernel(**self._launch_args)
1033
1033
1034 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
1034 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
1035 # unless there is some delay here.
1035 # unless there is some delay here.
1036 if sys.platform == 'win32':
1036 if sys.platform == 'win32':
1037 time.sleep(0.2)
1037 time.sleep(0.2)
1038
1038
1039 @property
1039 @property
1040 def has_kernel(self):
1040 def has_kernel(self):
1041 """Has a kernel been started that we are managing."""
1041 """Has a kernel been started that we are managing."""
1042 return self.kernel is not None
1042 return self.kernel is not None
1043
1043
1044 def _kill_kernel(self):
1044 def _kill_kernel(self):
1045 """Kill the running kernel.
1045 """Kill the running kernel.
1046
1046
1047 This is a private method, callers should use shutdown_kernel(now=True).
1047 This is a private method, callers should use shutdown_kernel(now=True).
1048 """
1048 """
1049 if self.has_kernel:
1049 if self.has_kernel:
1050 # Pause the heart beat channel if it exists.
1050 # Pause the heart beat channel if it exists.
1051 if self._hb_channel is not None:
1051 if self._hb_channel is not None:
1052 self._hb_channel.pause()
1052 self._hb_channel.pause()
1053
1053
1054 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
1054 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
1055 # TerminateProcess() on Win32).
1055 # TerminateProcess() on Win32).
1056 try:
1056 try:
1057 self.kernel.kill()
1057 self.kernel.kill()
1058 except OSError as e:
1058 except OSError as e:
1059 # In Windows, we will get an Access Denied error if the process
1059 # In Windows, we will get an Access Denied error if the process
1060 # has already terminated. Ignore it.
1060 # has already terminated. Ignore it.
1061 if sys.platform == 'win32':
1061 if sys.platform == 'win32':
1062 if e.winerror != 5:
1062 if e.winerror != 5:
1063 raise
1063 raise
1064 # On Unix, we may get an ESRCH error if the process has already
1064 # On Unix, we may get an ESRCH error if the process has already
1065 # terminated. Ignore it.
1065 # terminated. Ignore it.
1066 else:
1066 else:
1067 from errno import ESRCH
1067 from errno import ESRCH
1068 if e.errno != ESRCH:
1068 if e.errno != ESRCH:
1069 raise
1069 raise
1070
1070
1071 # Block until the kernel terminates.
1071 # Block until the kernel terminates.
1072 self.kernel.wait()
1072 self.kernel.wait()
1073 self.kernel = None
1073 self.kernel = None
1074 else:
1074 else:
1075 raise RuntimeError("Cannot kill kernel. No kernel is running!")
1075 raise RuntimeError("Cannot kill kernel. No kernel is running!")
1076
1076
1077 def interrupt_kernel(self):
1077 def interrupt_kernel(self):
1078 """Interrupts the kernel by sending it a signal.
1078 """Interrupts the kernel by sending it a signal.
1079
1079
1080 Unlike ``signal_kernel``, this operation is well supported on all
1080 Unlike ``signal_kernel``, this operation is well supported on all
1081 platforms.
1081 platforms.
1082 """
1082 """
1083 if self.has_kernel:
1083 if self.has_kernel:
1084 if sys.platform == 'win32':
1084 if sys.platform == 'win32':
1085 from .zmq.parentpoller import ParentPollerWindows as Poller
1085 from .zmq.parentpoller import ParentPollerWindows as Poller
1086 Poller.send_interrupt(self.kernel.win32_interrupt_event)
1086 Poller.send_interrupt(self.kernel.win32_interrupt_event)
1087 else:
1087 else:
1088 self.kernel.send_signal(signal.SIGINT)
1088 self.kernel.send_signal(signal.SIGINT)
1089 else:
1089 else:
1090 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
1090 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
1091
1091
1092 def signal_kernel(self, signum):
1092 def signal_kernel(self, signum):
1093 """Sends a signal to the kernel.
1093 """Sends a signal to the kernel.
1094
1094
1095 Note that since only SIGTERM is supported on Windows, this function is
1095 Note that since only SIGTERM is supported on Windows, this function is
1096 only useful on Unix systems.
1096 only useful on Unix systems.
1097 """
1097 """
1098 if self.has_kernel:
1098 if self.has_kernel:
1099 self.kernel.send_signal(signum)
1099 self.kernel.send_signal(signum)
1100 else:
1100 else:
1101 raise RuntimeError("Cannot signal kernel. No kernel is running!")
1101 raise RuntimeError("Cannot signal kernel. No kernel is running!")
1102
1102
1103 @property
1104 def is_alive(self):
1103 def is_alive(self):
1105 """Is the kernel process still running?"""
1104 """Is the kernel process still running?"""
1106 if self.has_kernel:
1105 if self.has_kernel:
1107 if self.kernel.poll() is None:
1106 if self.kernel.poll() is None:
1108 return True
1107 return True
1109 else:
1108 else:
1110 return False
1109 return False
1111 elif self._hb_channel is not None:
1110 elif self._hb_channel is not None:
1112 # We didn't start the kernel with this KernelManager so we
1111 # We didn't start the kernel with this KernelManager so we
1113 # use the heartbeat.
1112 # use the heartbeat.
1114 return self._hb_channel.is_beating()
1113 return self._hb_channel.is_beating()
1115 else:
1114 else:
1116 # no heartbeat and not local, we can't tell if it's running,
1115 # no heartbeat and not local, we can't tell if it's running,
1117 # so naively return True
1116 # so naively return True
1118 return True
1117 return True
1119
1118
1120
1119
1121 #-----------------------------------------------------------------------------
1120 #-----------------------------------------------------------------------------
1122 # ABC Registration
1121 # ABC Registration
1123 #-----------------------------------------------------------------------------
1122 #-----------------------------------------------------------------------------
1124
1123
1125 ShellChannelABC.register(ShellChannel)
1124 ShellChannelABC.register(ShellChannel)
1126 IOPubChannelABC.register(IOPubChannel)
1125 IOPubChannelABC.register(IOPubChannel)
1127 HBChannelABC.register(HBChannel)
1126 HBChannelABC.register(HBChannel)
1128 StdInChannelABC.register(StdInChannel)
1127 StdInChannelABC.register(StdInChannel)
1129 KernelManagerABC.register(KernelManager)
1128 KernelManagerABC.register(KernelManager)
1130
1129
@@ -1,226 +1,226 b''
1 """Abstract base classes for kernel manager and channels."""
1 """Abstract base classes for kernel manager and channels."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2013 The IPython Development Team
4 # Copyright (C) 2013 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 # Standard library imports.
14 # Standard library imports.
15 import abc
15 import abc
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Channels
18 # Channels
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21
21
22 class ChannelABC(object):
22 class ChannelABC(object):
23 """A base class for all channel ABCs."""
23 """A base class for all channel ABCs."""
24
24
25 __metaclass__ = abc.ABCMeta
25 __metaclass__ = abc.ABCMeta
26
26
27 @abc.abstractmethod
27 @abc.abstractmethod
28 def start(self):
28 def start(self):
29 pass
29 pass
30
30
31 @abc.abstractmethod
31 @abc.abstractmethod
32 def stop(self):
32 def stop(self):
33 pass
33 pass
34
34
35 @abc.abstractmethod
35 @abc.abstractmethod
36 def is_alive(self):
36 def is_alive(self):
37 pass
37 pass
38
38
39
39
40 class ShellChannelABC(ChannelABC):
40 class ShellChannelABC(ChannelABC):
41 """ShellChannel ABC.
41 """ShellChannel ABC.
42
42
43 The docstrings for this class can be found in the base implementation:
43 The docstrings for this class can be found in the base implementation:
44
44
45 `IPython.kernel.kernelmanager.ShellChannel`
45 `IPython.kernel.kernelmanager.ShellChannel`
46 """
46 """
47
47
48 @abc.abstractproperty
48 @abc.abstractproperty
49 def allow_stdin(self):
49 def allow_stdin(self):
50 pass
50 pass
51
51
52 @abc.abstractmethod
52 @abc.abstractmethod
53 def execute(self, code, silent=False, store_history=True,
53 def execute(self, code, silent=False, store_history=True,
54 user_variables=None, user_expressions=None, allow_stdin=None):
54 user_variables=None, user_expressions=None, allow_stdin=None):
55 pass
55 pass
56
56
57 @abc.abstractmethod
57 @abc.abstractmethod
58 def complete(self, text, line, cursor_pos, block=None):
58 def complete(self, text, line, cursor_pos, block=None):
59 pass
59 pass
60
60
61 @abc.abstractmethod
61 @abc.abstractmethod
62 def object_info(self, oname, detail_level=0):
62 def object_info(self, oname, detail_level=0):
63 pass
63 pass
64
64
65 @abc.abstractmethod
65 @abc.abstractmethod
66 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
66 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
67 pass
67 pass
68
68
69 @abc.abstractmethod
69 @abc.abstractmethod
70 def kernel_info(self):
70 def kernel_info(self):
71 pass
71 pass
72
72
73 @abc.abstractmethod
73 @abc.abstractmethod
74 def shutdown(self, restart=False):
74 def shutdown(self, restart=False):
75 pass
75 pass
76
76
77
77
78 class IOPubChannelABC(ChannelABC):
78 class IOPubChannelABC(ChannelABC):
79 """IOPubChannel ABC.
79 """IOPubChannel ABC.
80
80
81 The docstrings for this class can be found in the base implementation:
81 The docstrings for this class can be found in the base implementation:
82
82
83 `IPython.kernel.kernelmanager.IOPubChannel`
83 `IPython.kernel.kernelmanager.IOPubChannel`
84 """
84 """
85
85
86 @abc.abstractmethod
86 @abc.abstractmethod
87 def flush(self, timeout=1.0):
87 def flush(self, timeout=1.0):
88 pass
88 pass
89
89
90
90
91 class StdInChannelABC(ChannelABC):
91 class StdInChannelABC(ChannelABC):
92 """StdInChannel ABC.
92 """StdInChannel ABC.
93
93
94 The docstrings for this class can be found in the base implementation:
94 The docstrings for this class can be found in the base implementation:
95
95
96 `IPython.kernel.kernelmanager.StdInChannel`
96 `IPython.kernel.kernelmanager.StdInChannel`
97 """
97 """
98
98
99 @abc.abstractmethod
99 @abc.abstractmethod
100 def input(self, string):
100 def input(self, string):
101 pass
101 pass
102
102
103
103
104 class HBChannelABC(ChannelABC):
104 class HBChannelABC(ChannelABC):
105 """HBChannel ABC.
105 """HBChannel ABC.
106
106
107 The docstrings for this class can be found in the base implementation:
107 The docstrings for this class can be found in the base implementation:
108
108
109 `IPython.kernel.kernelmanager.HBChannel`
109 `IPython.kernel.kernelmanager.HBChannel`
110 """
110 """
111
111
112 @abc.abstractproperty
112 @abc.abstractproperty
113 def time_to_dead(self):
113 def time_to_dead(self):
114 pass
114 pass
115
115
116 @abc.abstractmethod
116 @abc.abstractmethod
117 def pause(self):
117 def pause(self):
118 pass
118 pass
119
119
120 @abc.abstractmethod
120 @abc.abstractmethod
121 def unpause(self):
121 def unpause(self):
122 pass
122 pass
123
123
124 @abc.abstractmethod
124 @abc.abstractmethod
125 def is_beating(self):
125 def is_beating(self):
126 pass
126 pass
127
127
128
128
129 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
130 # Main kernel manager class
130 # Main kernel manager class
131 #-----------------------------------------------------------------------------
131 #-----------------------------------------------------------------------------
132
132
133 class KernelManagerABC(object):
133 class KernelManagerABC(object):
134 """KernelManager ABC.
134 """KernelManager ABC.
135
135
136 The docstrings for this class can be found in the base implementation:
136 The docstrings for this class can be found in the base implementation:
137
137
138 `IPython.kernel.kernelmanager.KernelManager`
138 `IPython.kernel.kernelmanager.KernelManager`
139 """
139 """
140
140
141 __metaclass__ = abc.ABCMeta
141 __metaclass__ = abc.ABCMeta
142
142
143 @abc.abstractproperty
143 @abc.abstractproperty
144 def kernel(self):
144 def kernel(self):
145 pass
145 pass
146
146
147 @abc.abstractproperty
147 @abc.abstractproperty
148 def shell_channel_class(self):
148 def shell_channel_class(self):
149 pass
149 pass
150
150
151 @abc.abstractproperty
151 @abc.abstractproperty
152 def iopub_channel_class(self):
152 def iopub_channel_class(self):
153 pass
153 pass
154
154
155 @abc.abstractproperty
155 @abc.abstractproperty
156 def hb_channel_class(self):
156 def hb_channel_class(self):
157 pass
157 pass
158
158
159 @abc.abstractproperty
159 @abc.abstractproperty
160 def stdin_channel_class(self):
160 def stdin_channel_class(self):
161 pass
161 pass
162
162
163 #--------------------------------------------------------------------------
163 #--------------------------------------------------------------------------
164 # Channel management methods
164 # Channel management methods
165 #--------------------------------------------------------------------------
165 #--------------------------------------------------------------------------
166
166
167 @abc.abstractmethod
167 @abc.abstractmethod
168 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
168 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
169 pass
169 pass
170
170
171 @abc.abstractmethod
171 @abc.abstractmethod
172 def stop_channels(self):
172 def stop_channels(self):
173 pass
173 pass
174
174
175 @abc.abstractproperty
175 @abc.abstractproperty
176 def channels_running(self):
176 def channels_running(self):
177 pass
177 pass
178
178
179 @abc.abstractproperty
179 @abc.abstractproperty
180 def shell_channel(self):
180 def shell_channel(self):
181 pass
181 pass
182
182
183 @abc.abstractproperty
183 @abc.abstractproperty
184 def iopub_channel(self):
184 def iopub_channel(self):
185 pass
185 pass
186
186
187 @abc.abstractproperty
187 @abc.abstractproperty
188 def stdin_channel(self):
188 def stdin_channel(self):
189 pass
189 pass
190
190
191 @abc.abstractproperty
191 @abc.abstractproperty
192 def hb_channel(self):
192 def hb_channel(self):
193 pass
193 pass
194
194
195 #--------------------------------------------------------------------------
195 #--------------------------------------------------------------------------
196 # Kernel management
196 # Kernel management
197 #--------------------------------------------------------------------------
197 #--------------------------------------------------------------------------
198
198
199 @abc.abstractmethod
199 @abc.abstractmethod
200 def start_kernel(self, **kw):
200 def start_kernel(self, **kw):
201 pass
201 pass
202
202
203 @abc.abstractmethod
203 @abc.abstractmethod
204 def shutdown_kernel(self, now=False, restart=False):
204 def shutdown_kernel(self, now=False, restart=False):
205 pass
205 pass
206
206
207 @abc.abstractmethod
207 @abc.abstractmethod
208 def restart_kernel(self, now=False, **kw):
208 def restart_kernel(self, now=False, **kw):
209 pass
209 pass
210
210
211 @abc.abstractproperty
211 @abc.abstractproperty
212 def has_kernel(self):
212 def has_kernel(self):
213 pass
213 pass
214
214
215 @abc.abstractmethod
215 @abc.abstractmethod
216 def interrupt_kernel(self):
216 def interrupt_kernel(self):
217 pass
217 pass
218
218
219 @abc.abstractmethod
219 @abc.abstractmethod
220 def signal_kernel(self, signum):
220 def signal_kernel(self, signum):
221 pass
221 pass
222
222
223 @abc.abstractproperty
223 @abc.abstractmethod
224 def is_alive(self):
224 def is_alive(self):
225 pass
225 pass
226
226
@@ -1,259 +1,272 b''
1 """A kernel manager for multiple kernels
1 """A kernel manager for multiple kernels
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from __future__ import absolute_import
19 from __future__ import absolute_import
20
20
21 import os
21 import os
22 import uuid
22 import uuid
23
23
24 import zmq
24 import zmq
25 from zmq.eventloop.zmqstream import ZMQStream
25 from zmq.eventloop.zmqstream import ZMQStream
26
26
27 from IPython.config.configurable import LoggingConfigurable
27 from IPython.config.configurable import LoggingConfigurable
28 from IPython.utils.importstring import import_item
28 from IPython.utils.importstring import import_item
29 from IPython.utils.traitlets import (
29 from IPython.utils.traitlets import (
30 Instance, Dict, Unicode, Any, DottedObjectName,
30 Instance, Dict, Unicode, Any, DottedObjectName,
31 )
31 )
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Classes
33 # Classes
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 class DuplicateKernelError(Exception):
36 class DuplicateKernelError(Exception):
37 pass
37 pass
38
38
39
39
40 class MultiKernelManager(LoggingConfigurable):
40 class MultiKernelManager(LoggingConfigurable):
41 """A class for managing multiple kernels."""
41 """A class for managing multiple kernels."""
42
42
43 kernel_manager_class = DottedObjectName(
43 kernel_manager_class = DottedObjectName(
44 "IPython.kernel.blockingkernelmanager.BlockingKernelManager", config=True,
44 "IPython.kernel.blockingkernelmanager.BlockingKernelManager", config=True,
45 help="""The kernel manager class. This is configurable to allow
45 help="""The kernel manager class. This is configurable to allow
46 subclassing of the KernelManager for customized behavior.
46 subclassing of the KernelManager for customized behavior.
47 """
47 """
48 )
48 )
49 def _kernel_manager_class_changed(self, name, old, new):
49 def _kernel_manager_class_changed(self, name, old, new):
50 self.kernel_manager_factory = import_item(new)
50 self.kernel_manager_factory = import_item(new)
51
51
52 kernel_manager_factory = Any(help="this is kernel_manager_class after import")
52 kernel_manager_factory = Any(help="this is kernel_manager_class after import")
53 def _kernel_manager_factory_default(self):
53 def _kernel_manager_factory_default(self):
54 return import_item(self.kernel_manager_class)
54 return import_item(self.kernel_manager_class)
55
55
56 context = Instance('zmq.Context')
56 context = Instance('zmq.Context')
57 def _context_default(self):
57 def _context_default(self):
58 return zmq.Context.instance()
58 return zmq.Context.instance()
59
59
60 connection_dir = Unicode('')
60 connection_dir = Unicode('')
61
61
62 _kernels = Dict()
62 _kernels = Dict()
63
63
64 def list_kernel_ids(self):
64 def list_kernel_ids(self):
65 """Return a list of the kernel ids of the active kernels."""
65 """Return a list of the kernel ids of the active kernels."""
66 # Create a copy so we can iterate over kernels in operations
66 # Create a copy so we can iterate over kernels in operations
67 # that delete keys.
67 # that delete keys.
68 return list(self._kernels.keys())
68 return list(self._kernels.keys())
69
69
70 def __len__(self):
70 def __len__(self):
71 """Return the number of running kernels."""
71 """Return the number of running kernels."""
72 return len(self.list_kernel_ids())
72 return len(self.list_kernel_ids())
73
73
74 def __contains__(self, kernel_id):
74 def __contains__(self, kernel_id):
75 return kernel_id in self._kernels
75 return kernel_id in self._kernels
76
76
77 def start_kernel(self, **kwargs):
77 def start_kernel(self, **kwargs):
78 """Start a new kernel.
78 """Start a new kernel.
79
79
80 The caller can pick a kernel_id by passing one in as a keyword arg,
80 The caller can pick a kernel_id by passing one in as a keyword arg,
81 otherwise one will be picked using a uuid.
81 otherwise one will be picked using a uuid.
82
82
83 To silence the kernel's stdout/stderr, call this using::
83 To silence the kernel's stdout/stderr, call this using::
84
84
85 km.start_kernel(stdout=PIPE, stderr=PIPE)
85 km.start_kernel(stdout=PIPE, stderr=PIPE)
86
86
87 """
87 """
88 kernel_id = kwargs.pop('kernel_id', unicode(uuid.uuid4()))
88 kernel_id = kwargs.pop('kernel_id', unicode(uuid.uuid4()))
89 if kernel_id in self:
89 if kernel_id in self:
90 raise DuplicateKernelError('Kernel already exists: %s' % kernel_id)
90 raise DuplicateKernelError('Kernel already exists: %s' % kernel_id)
91 # kernel_manager_factory is the constructor for the KernelManager
91 # kernel_manager_factory is the constructor for the KernelManager
92 # subclass we are using. It can be configured as any Configurable,
92 # subclass we are using. It can be configured as any Configurable,
93 # including things like its transport and ip.
93 # including things like its transport and ip.
94 km = self.kernel_manager_factory(connection_file=os.path.join(
94 km = self.kernel_manager_factory(connection_file=os.path.join(
95 self.connection_dir, "kernel-%s.json" % kernel_id),
95 self.connection_dir, "kernel-%s.json" % kernel_id),
96 config=self.config,
96 config=self.config,
97 )
97 )
98 km.start_kernel(**kwargs)
98 km.start_kernel(**kwargs)
99 # start just the shell channel, needed for graceful restart
99 # start just the shell channel, needed for graceful restart
100 km.start_channels(shell=True, iopub=False, stdin=False, hb=False)
100 km.start_channels(shell=True, iopub=False, stdin=False, hb=False)
101 self._kernels[kernel_id] = km
101 self._kernels[kernel_id] = km
102 return kernel_id
102 return kernel_id
103
103
104 def shutdown_kernel(self, kernel_id, now=False):
104 def shutdown_kernel(self, kernel_id, now=False):
105 """Shutdown a kernel by its kernel uuid.
105 """Shutdown a kernel by its kernel uuid.
106
106
107 Parameters
107 Parameters
108 ==========
108 ==========
109 kernel_id : uuid
109 kernel_id : uuid
110 The id of the kernel to shutdown.
110 The id of the kernel to shutdown.
111 now : bool
111 now : bool
112 Should the kernel be shutdown forcibly using a signal.
112 Should the kernel be shutdown forcibly using a signal.
113 """
113 """
114 k = self.get_kernel(kernel_id)
114 k = self.get_kernel(kernel_id)
115 k.shutdown_kernel(now=now)
115 k.shutdown_kernel(now=now)
116 k.shell_channel.stop()
116 k.shell_channel.stop()
117 del self._kernels[kernel_id]
117 del self._kernels[kernel_id]
118
118
119 def shutdown_all(self, now=False):
119 def shutdown_all(self, now=False):
120 """Shutdown all kernels."""
120 """Shutdown all kernels."""
121 for kid in self.list_kernel_ids():
121 for kid in self.list_kernel_ids():
122 self.shutdown_kernel(kid, now=now)
122 self.shutdown_kernel(kid, now=now)
123
123
124 def interrupt_kernel(self, kernel_id):
124 def interrupt_kernel(self, kernel_id):
125 """Interrupt (SIGINT) the kernel by its uuid.
125 """Interrupt (SIGINT) the kernel by its uuid.
126
126
127 Parameters
127 Parameters
128 ==========
128 ==========
129 kernel_id : uuid
129 kernel_id : uuid
130 The id of the kernel to interrupt.
130 The id of the kernel to interrupt.
131 """
131 """
132 return self.get_kernel(kernel_id).interrupt_kernel()
132 return self.get_kernel(kernel_id).interrupt_kernel()
133
133
134 def signal_kernel(self, kernel_id, signum):
134 def signal_kernel(self, kernel_id, signum):
135 """Sends a signal to the kernel by its uuid.
135 """Sends a signal to the kernel by its uuid.
136
136
137 Note that since only SIGTERM is supported on Windows, this function
137 Note that since only SIGTERM is supported on Windows, this function
138 is only useful on Unix systems.
138 is only useful on Unix systems.
139
139
140 Parameters
140 Parameters
141 ==========
141 ==========
142 kernel_id : uuid
142 kernel_id : uuid
143 The id of the kernel to signal.
143 The id of the kernel to signal.
144 """
144 """
145 return self.get_kernel(kernel_id).signal_kernel(signum)
145 return self.get_kernel(kernel_id).signal_kernel(signum)
146
146
147 def restart_kernel(self, kernel_id):
147 def restart_kernel(self, kernel_id):
148 """Restart a kernel by its uuid, keeping the same ports.
148 """Restart a kernel by its uuid, keeping the same ports.
149
149
150 Parameters
150 Parameters
151 ==========
151 ==========
152 kernel_id : uuid
152 kernel_id : uuid
153 The id of the kernel to interrupt.
153 The id of the kernel to interrupt.
154 """
154 """
155 return self.get_kernel(kernel_id).restart_kernel()
155 return self.get_kernel(kernel_id).restart_kernel()
156
156
157 def is_alive(self, kernel_id):
158 """Is the kernel alive.
159
160 This calls KernelManager.is_alive() which calls Popen.poll on the
161 actual kernel subprocess.
162
163 Parameters
164 ==========
165 kernel_id : uuid
166 The id of the kernel.
167 """
168 return self.get_kernel(kernel_id).is_alive()
169
157 def get_kernel(self, kernel_id):
170 def get_kernel(self, kernel_id):
158 """Get the single KernelManager object for a kernel by its uuid.
171 """Get the single KernelManager object for a kernel by its uuid.
159
172
160 Parameters
173 Parameters
161 ==========
174 ==========
162 kernel_id : uuid
175 kernel_id : uuid
163 The id of the kernel.
176 The id of the kernel.
164 """
177 """
165 km = self._kernels.get(kernel_id)
178 km = self._kernels.get(kernel_id)
166 if km is not None:
179 if km is not None:
167 return km
180 return km
168 else:
181 else:
169 raise KeyError("Kernel with id not found: %s" % kernel_id)
182 raise KeyError("Kernel with id not found: %s" % kernel_id)
170
183
171 def get_connection_info(self, kernel_id):
184 def get_connection_info(self, kernel_id):
172 """Return a dictionary of connection data for a kernel.
185 """Return a dictionary of connection data for a kernel.
173
186
174 Parameters
187 Parameters
175 ==========
188 ==========
176 kernel_id : uuid
189 kernel_id : uuid
177 The id of the kernel.
190 The id of the kernel.
178
191
179 Returns
192 Returns
180 =======
193 =======
181 connection_dict : dict
194 connection_dict : dict
182 A dict of the information needed to connect to a kernel.
195 A dict of the information needed to connect to a kernel.
183 This includes the ip address and the integer port
196 This includes the ip address and the integer port
184 numbers of the different channels (stdin_port, iopub_port,
197 numbers of the different channels (stdin_port, iopub_port,
185 shell_port, hb_port).
198 shell_port, hb_port).
186 """
199 """
187 km = self.get_kernel(kernel_id)
200 km = self.get_kernel(kernel_id)
188 return dict(transport=km.transport,
201 return dict(transport=km.transport,
189 ip=km.ip,
202 ip=km.ip,
190 shell_port=km.shell_port,
203 shell_port=km.shell_port,
191 iopub_port=km.iopub_port,
204 iopub_port=km.iopub_port,
192 stdin_port=km.stdin_port,
205 stdin_port=km.stdin_port,
193 hb_port=km.hb_port,
206 hb_port=km.hb_port,
194 )
207 )
195
208
196 def _make_url(self, transport, ip, port):
209 def _make_url(self, transport, ip, port):
197 """Make a ZeroMQ URL for a given transport, ip and port."""
210 """Make a ZeroMQ URL for a given transport, ip and port."""
198 if transport == 'tcp':
211 if transport == 'tcp':
199 return "tcp://%s:%i" % (ip, port)
212 return "tcp://%s:%i" % (ip, port)
200 else:
213 else:
201 return "%s://%s-%s" % (transport, ip, port)
214 return "%s://%s-%s" % (transport, ip, port)
202
215
203 def _create_connected_stream(self, kernel_id, socket_type, channel):
216 def _create_connected_stream(self, kernel_id, socket_type, channel):
204 """Create a connected ZMQStream for a kernel."""
217 """Create a connected ZMQStream for a kernel."""
205 cinfo = self.get_connection_info(kernel_id)
218 cinfo = self.get_connection_info(kernel_id)
206 url = self._make_url(cinfo['transport'], cinfo['ip'],
219 url = self._make_url(cinfo['transport'], cinfo['ip'],
207 cinfo['%s_port' % channel]
220 cinfo['%s_port' % channel]
208 )
221 )
209 sock = self.context.socket(socket_type)
222 sock = self.context.socket(socket_type)
210 self.log.info("Connecting to: %s" % url)
223 self.log.info("Connecting to: %s" % url)
211 sock.connect(url)
224 sock.connect(url)
212 return ZMQStream(sock)
225 return ZMQStream(sock)
213
226
214 def create_iopub_stream(self, kernel_id):
227 def create_iopub_stream(self, kernel_id):
215 """Return a ZMQStream object connected to the iopub channel.
228 """Return a ZMQStream object connected to the iopub channel.
216
229
217 Parameters
230 Parameters
218 ==========
231 ==========
219 kernel_id : uuid
232 kernel_id : uuid
220 The id of the kernel.
233 The id of the kernel.
221
234
222 Returns
235 Returns
223 =======
236 =======
224 stream : ZMQStream
237 stream : ZMQStream
225 """
238 """
226 iopub_stream = self._create_connected_stream(kernel_id, zmq.SUB, 'iopub')
239 iopub_stream = self._create_connected_stream(kernel_id, zmq.SUB, 'iopub')
227 iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'')
240 iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'')
228 return iopub_stream
241 return iopub_stream
229
242
230 def create_shell_stream(self, kernel_id):
243 def create_shell_stream(self, kernel_id):
231 """Return a ZMQStream object connected to the shell channel.
244 """Return a ZMQStream object connected to the shell channel.
232
245
233 Parameters
246 Parameters
234 ==========
247 ==========
235 kernel_id : uuid
248 kernel_id : uuid
236 The id of the kernel.
249 The id of the kernel.
237
250
238 Returns
251 Returns
239 =======
252 =======
240 stream : ZMQStream
253 stream : ZMQStream
241 """
254 """
242 shell_stream = self._create_connected_stream(kernel_id, zmq.DEALER, 'shell')
255 shell_stream = self._create_connected_stream(kernel_id, zmq.DEALER, 'shell')
243 return shell_stream
256 return shell_stream
244
257
245 def create_hb_stream(self, kernel_id):
258 def create_hb_stream(self, kernel_id):
246 """Return a ZMQStream object connected to the hb channel.
259 """Return a ZMQStream object connected to the hb channel.
247
260
248 Parameters
261 Parameters
249 ==========
262 ==========
250 kernel_id : uuid
263 kernel_id : uuid
251 The id of the kernel.
264 The id of the kernel.
252
265
253 Returns
266 Returns
254 =======
267 =======
255 stream : ZMQStream
268 stream : ZMQStream
256 """
269 """
257 hb_stream = self._create_connected_stream(kernel_id, zmq.REQ, 'hb')
270 hb_stream = self._create_connected_stream(kernel_id, zmq.REQ, 'hb')
258 return hb_stream
271 return hb_stream
259
272
@@ -1,48 +1,50 b''
1 """Tests for the notebook kernel and session manager"""
1 """Tests for the notebook kernel and session manager"""
2
2
3 from subprocess import PIPE
3 from subprocess import PIPE
4 import time
4 import time
5 from unittest import TestCase
5 from unittest import TestCase
6
6
7 from IPython.testing import decorators as dec
7 from IPython.testing import decorators as dec
8
8
9 from IPython.config.loader import Config
9 from IPython.config.loader import Config
10 from IPython.kernel.kernelmanager import KernelManager
10 from IPython.kernel.kernelmanager import KernelManager
11
11
12 class TestKernelManager(TestCase):
12 class TestKernelManager(TestCase):
13
13
14 def _get_tcp_km(self):
14 def _get_tcp_km(self):
15 return KernelManager()
15 return KernelManager()
16
16
17 def _get_ipc_km(self):
17 def _get_ipc_km(self):
18 c = Config()
18 c = Config()
19 c.KernelManager.transport = 'ipc'
19 c.KernelManager.transport = 'ipc'
20 c.KernelManager.ip = 'test'
20 c.KernelManager.ip = 'test'
21 km = KernelManager(config=c)
21 km = KernelManager(config=c)
22 return km
22 return km
23
23
24 def _run_lifecycle(self, km):
24 def _run_lifecycle(self, km):
25 km.start_kernel(stdout=PIPE, stderr=PIPE)
25 km.start_kernel(stdout=PIPE, stderr=PIPE)
26 self.assertTrue(km.is_alive())
26 km.start_channels(shell=True, iopub=False, stdin=False, hb=False)
27 km.start_channels(shell=True, iopub=False, stdin=False, hb=False)
27 km.restart_kernel()
28 km.restart_kernel()
29 self.assertTrue(km.is_alive())
28 # We need a delay here to give the restarting kernel a chance to
30 # We need a delay here to give the restarting kernel a chance to
29 # restart. Otherwise, the interrupt will kill it, causing the test
31 # restart. Otherwise, the interrupt will kill it, causing the test
30 # suite to hang. The reason it *hangs* is that the shutdown
32 # suite to hang. The reason it *hangs* is that the shutdown
31 # message for the restart sometimes hasn't been sent to the kernel.
33 # message for the restart sometimes hasn't been sent to the kernel.
32 # Because linger is oo on the shell channel, the context can't
34 # Because linger is oo on the shell channel, the context can't
33 # close until the message is sent to the kernel, which is not dead.
35 # close until the message is sent to the kernel, which is not dead.
34 time.sleep(1.0)
36 time.sleep(1.0)
35 km.interrupt_kernel()
37 km.interrupt_kernel()
36 self.assertTrue(isinstance(km, KernelManager))
38 self.assertTrue(isinstance(km, KernelManager))
37 km.shutdown_kernel()
39 km.shutdown_kernel()
38 km.shell_channel.stop()
40 km.shell_channel.stop()
39
41
40 def test_tcp_lifecycle(self):
42 def test_tcp_lifecycle(self):
41 km = self._get_tcp_km()
43 km = self._get_tcp_km()
42 self._run_lifecycle(km)
44 self._run_lifecycle(km)
43
45
44 @dec.skip_win32
46 @dec.skip_win32
45 def test_ipc_lifecycle(self):
47 def test_ipc_lifecycle(self):
46 km = self._get_ipc_km()
48 km = self._get_ipc_km()
47 self._run_lifecycle(km)
49 self._run_lifecycle(km)
48
50
@@ -1,81 +1,83 b''
1 """Tests for the notebook kernel and session manager."""
1 """Tests for the notebook kernel and session manager."""
2
2
3 from subprocess import PIPE
3 from subprocess import PIPE
4 import time
4 import time
5 from unittest import TestCase
5 from unittest import TestCase
6
6
7 from IPython.testing import decorators as dec
7 from IPython.testing import decorators as dec
8
8
9 from IPython.config.loader import Config
9 from IPython.config.loader import Config
10 from IPython.utils.localinterfaces import LOCALHOST
10 from IPython.utils.localinterfaces import LOCALHOST
11 from IPython.kernel.kernelmanager import KernelManager
11 from IPython.kernel.kernelmanager import KernelManager
12 from IPython.kernel.multikernelmanager import MultiKernelManager
12 from IPython.kernel.multikernelmanager import MultiKernelManager
13
13
14 class TestKernelManager(TestCase):
14 class TestKernelManager(TestCase):
15
15
16 def _get_tcp_km(self):
16 def _get_tcp_km(self):
17 return MultiKernelManager()
17 return MultiKernelManager()
18
18
19 def _get_ipc_km(self):
19 def _get_ipc_km(self):
20 c = Config()
20 c = Config()
21 c.KernelManager.transport = 'ipc'
21 c.KernelManager.transport = 'ipc'
22 c.KernelManager.ip = 'test'
22 c.KernelManager.ip = 'test'
23 km = MultiKernelManager(config=c)
23 km = MultiKernelManager(config=c)
24 return km
24 return km
25
25
26 def _run_lifecycle(self, km):
26 def _run_lifecycle(self, km):
27 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
27 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
28 self.assertTrue(km.is_alive(kid))
28 self.assertTrue(kid in km)
29 self.assertTrue(kid in km)
29 self.assertTrue(kid in km.list_kernel_ids())
30 self.assertTrue(kid in km.list_kernel_ids())
30 self.assertEqual(len(km),1)
31 self.assertEqual(len(km),1)
31 km.restart_kernel(kid)
32 km.restart_kernel(kid)
33 self.assertTrue(km.is_alive(kid))
32 self.assertTrue(kid in km.list_kernel_ids())
34 self.assertTrue(kid in km.list_kernel_ids())
33 # We need a delay here to give the restarting kernel a chance to
35 # We need a delay here to give the restarting kernel a chance to
34 # restart. Otherwise, the interrupt will kill it, causing the test
36 # restart. Otherwise, the interrupt will kill it, causing the test
35 # suite to hang. The reason it *hangs* is that the shutdown
37 # suite to hang. The reason it *hangs* is that the shutdown
36 # message for the restart sometimes hasn't been sent to the kernel.
38 # message for the restart sometimes hasn't been sent to the kernel.
37 # Because linger is oo on the shell channel, the context can't
39 # Because linger is oo on the shell channel, the context can't
38 # close until the message is sent to the kernel, which is not dead.
40 # close until the message is sent to the kernel, which is not dead.
39 time.sleep(1.0)
41 time.sleep(1.0)
40 km.interrupt_kernel(kid)
42 km.interrupt_kernel(kid)
41 k = km.get_kernel(kid)
43 k = km.get_kernel(kid)
42 self.assertTrue(isinstance(k, KernelManager))
44 self.assertTrue(isinstance(k, KernelManager))
43 km.shutdown_kernel(kid)
45 km.shutdown_kernel(kid)
44 self.assertTrue(not kid in km)
46 self.assertTrue(not kid in km)
45
47
46 def _run_cinfo(self, km, transport, ip):
48 def _run_cinfo(self, km, transport, ip):
47 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
49 kid = km.start_kernel(stdout=PIPE, stderr=PIPE)
48 k = km.get_kernel(kid)
50 k = km.get_kernel(kid)
49 cinfo = km.get_connection_info(kid)
51 cinfo = km.get_connection_info(kid)
50 self.assertEqual(transport, cinfo['transport'])
52 self.assertEqual(transport, cinfo['transport'])
51 self.assertEqual(ip, cinfo['ip'])
53 self.assertEqual(ip, cinfo['ip'])
52 self.assertTrue('stdin_port' in cinfo)
54 self.assertTrue('stdin_port' in cinfo)
53 self.assertTrue('iopub_port' in cinfo)
55 self.assertTrue('iopub_port' in cinfo)
54 stream = km.create_iopub_stream(kid)
56 stream = km.create_iopub_stream(kid)
55 stream.close()
57 stream.close()
56 self.assertTrue('shell_port' in cinfo)
58 self.assertTrue('shell_port' in cinfo)
57 stream = km.create_shell_stream(kid)
59 stream = km.create_shell_stream(kid)
58 stream.close()
60 stream.close()
59 self.assertTrue('hb_port' in cinfo)
61 self.assertTrue('hb_port' in cinfo)
60 stream = km.create_hb_stream(kid)
62 stream = km.create_hb_stream(kid)
61 stream.close()
63 stream.close()
62 km.shutdown_kernel(kid)
64 km.shutdown_kernel(kid)
63
65
64 def test_tcp_lifecycle(self):
66 def test_tcp_lifecycle(self):
65 km = self._get_tcp_km()
67 km = self._get_tcp_km()
66 self._run_lifecycle(km)
68 self._run_lifecycle(km)
67
69
68 def test_tcp_cinfo(self):
70 def test_tcp_cinfo(self):
69 km = self._get_tcp_km()
71 km = self._get_tcp_km()
70 self._run_cinfo(km, 'tcp', LOCALHOST)
72 self._run_cinfo(km, 'tcp', LOCALHOST)
71
73
72 @dec.skip_win32
74 @dec.skip_win32
73 def test_ipc_lifecycle(self):
75 def test_ipc_lifecycle(self):
74 km = self._get_ipc_km()
76 km = self._get_ipc_km()
75 self._run_lifecycle(km)
77 self._run_lifecycle(km)
76
78
77 @dec.skip_win32
79 @dec.skip_win32
78 def test_ipc_cinfo(self):
80 def test_ipc_cinfo(self):
79 km = self._get_ipc_km()
81 km = self._get_ipc_km()
80 self._run_cinfo(km, 'ipc', 'test')
82 self._run_cinfo(km, 'ipc', 'test')
81
83
General Comments 0
You need to be logged in to leave comments. Login now