##// END OF EJS Templates
s/make_kernel_cmd/make_ipkernel_cmd/
MinRK -
Show More
@@ -1,262 +1,262 b''
1 """ Defines helper functions for creating kernel entry points and process
1 """ Defines helper functions for creating kernel entry points and process
2 launchers.
2 launchers.
3 """
3 """
4
4
5 # Standard library imports
5 # Standard library imports
6 import json
6 import json
7 import os
7 import os
8 import socket
8 import socket
9 from subprocess import Popen, PIPE
9 from subprocess import Popen, PIPE
10 import sys
10 import sys
11 import tempfile
11 import tempfile
12
12
13 # System library imports
13 # System library imports
14
14
15 # IPython imports
15 # IPython imports
16 from IPython.utils.localinterfaces import LOCALHOST
16 from IPython.utils.localinterfaces import LOCALHOST
17 from IPython.utils.py3compat import bytes_to_str
17 from IPython.utils.py3compat import bytes_to_str
18
18
19 # Local imports
19 # Local imports
20 from parentpoller import ParentPollerWindows
20 from parentpoller import ParentPollerWindows
21
21
22 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
22 def write_connection_file(fname=None, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
23 ip=LOCALHOST, key=b'', transport='tcp'):
23 ip=LOCALHOST, key=b'', transport='tcp'):
24 """Generates a JSON config file, including the selection of random ports.
24 """Generates a JSON config file, including the selection of random ports.
25
25
26 Parameters
26 Parameters
27 ----------
27 ----------
28
28
29 fname : unicode
29 fname : unicode
30 The path to the file to write
30 The path to the file to write
31
31
32 shell_port : int, optional
32 shell_port : int, optional
33 The port to use for ROUTER channel.
33 The port to use for ROUTER channel.
34
34
35 iopub_port : int, optional
35 iopub_port : int, optional
36 The port to use for the SUB channel.
36 The port to use for the SUB channel.
37
37
38 stdin_port : int, optional
38 stdin_port : int, optional
39 The port to use for the REQ (raw input) channel.
39 The port to use for the REQ (raw input) channel.
40
40
41 hb_port : int, optional
41 hb_port : int, optional
42 The port to use for the hearbeat REP channel.
42 The port to use for the hearbeat REP channel.
43
43
44 ip : str, optional
44 ip : str, optional
45 The ip address the kernel will bind to.
45 The ip address the kernel will bind to.
46
46
47 key : str, optional
47 key : str, optional
48 The Session key used for HMAC authentication.
48 The Session key used for HMAC authentication.
49
49
50 """
50 """
51 # default to temporary connector file
51 # default to temporary connector file
52 if not fname:
52 if not fname:
53 fname = tempfile.mktemp('.json')
53 fname = tempfile.mktemp('.json')
54
54
55 # Find open ports as necessary.
55 # Find open ports as necessary.
56
56
57 ports = []
57 ports = []
58 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
58 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
59 int(stdin_port <= 0) + int(hb_port <= 0)
59 int(stdin_port <= 0) + int(hb_port <= 0)
60 if transport == 'tcp':
60 if transport == 'tcp':
61 for i in range(ports_needed):
61 for i in range(ports_needed):
62 sock = socket.socket()
62 sock = socket.socket()
63 sock.bind(('', 0))
63 sock.bind(('', 0))
64 ports.append(sock)
64 ports.append(sock)
65 for i, sock in enumerate(ports):
65 for i, sock in enumerate(ports):
66 port = sock.getsockname()[1]
66 port = sock.getsockname()[1]
67 sock.close()
67 sock.close()
68 ports[i] = port
68 ports[i] = port
69 else:
69 else:
70 N = 1
70 N = 1
71 for i in range(ports_needed):
71 for i in range(ports_needed):
72 while os.path.exists("%s-%s" % (ip, str(N))):
72 while os.path.exists("%s-%s" % (ip, str(N))):
73 N += 1
73 N += 1
74 ports.append(N)
74 ports.append(N)
75 N += 1
75 N += 1
76 if shell_port <= 0:
76 if shell_port <= 0:
77 shell_port = ports.pop(0)
77 shell_port = ports.pop(0)
78 if iopub_port <= 0:
78 if iopub_port <= 0:
79 iopub_port = ports.pop(0)
79 iopub_port = ports.pop(0)
80 if stdin_port <= 0:
80 if stdin_port <= 0:
81 stdin_port = ports.pop(0)
81 stdin_port = ports.pop(0)
82 if hb_port <= 0:
82 if hb_port <= 0:
83 hb_port = ports.pop(0)
83 hb_port = ports.pop(0)
84
84
85 cfg = dict( shell_port=shell_port,
85 cfg = dict( shell_port=shell_port,
86 iopub_port=iopub_port,
86 iopub_port=iopub_port,
87 stdin_port=stdin_port,
87 stdin_port=stdin_port,
88 hb_port=hb_port,
88 hb_port=hb_port,
89 )
89 )
90 cfg['ip'] = ip
90 cfg['ip'] = ip
91 cfg['key'] = bytes_to_str(key)
91 cfg['key'] = bytes_to_str(key)
92 cfg['transport'] = transport
92 cfg['transport'] = transport
93
93
94 with open(fname, 'w') as f:
94 with open(fname, 'w') as f:
95 f.write(json.dumps(cfg, indent=2))
95 f.write(json.dumps(cfg, indent=2))
96
96
97 return fname, cfg
97 return fname, cfg
98
98
99
99
100 def make_kernel_cmd(code, executable=None, extra_arguments=[], **kw):
100 def make_ipkernel_cmd(code, executable=None, extra_arguments=[], **kw):
101 """ Launches a localhost kernel, binding to the specified ports.
101 """Build Popen command list for launching an IPython kernel.
102
102
103 Parameters
103 Parameters
104 ----------
104 ----------
105 code : str,
105 code : str,
106 A string of Python code that imports and executes a kernel entry point.
106 A string of Python code that imports and executes a kernel entry point.
107
107
108 executable : str, optional (default sys.executable)
108 executable : str, optional (default sys.executable)
109 The Python executable to use for the kernel process.
109 The Python executable to use for the kernel process.
110
110
111 extra_arguments : list, optional
111 extra_arguments : list, optional
112 A list of extra arguments to pass when executing the launch code.
112 A list of extra arguments to pass when executing the launch code.
113
113
114 Returns
114 Returns
115 -------
115 -------
116
116
117 A Popen command list
117 A Popen command list
118 """
118 """
119
119
120 # Build the kernel launch command.
120 # Build the kernel launch command.
121 if executable is None:
121 if executable is None:
122 executable = sys.executable
122 executable = sys.executable
123 arguments = [ executable, '-c', code, '-f', '{connection_file}' ]
123 arguments = [ executable, '-c', code, '-f', '{connection_file}' ]
124 arguments.extend(extra_arguments)
124 arguments.extend(extra_arguments)
125
125
126 # Spawn a kernel.
126 # Spawn a kernel.
127 if sys.platform == 'win32':
127 if sys.platform == 'win32':
128
128
129 # If the kernel is running on pythonw and stdout/stderr are not been
129 # If the kernel is running on pythonw and stdout/stderr are not been
130 # re-directed, it will crash when more than 4KB of data is written to
130 # re-directed, it will crash when more than 4KB of data is written to
131 # stdout or stderr. This is a bug that has been with Python for a very
131 # stdout or stderr. This is a bug that has been with Python for a very
132 # long time; see http://bugs.python.org/issue706263.
132 # long time; see http://bugs.python.org/issue706263.
133 # A cleaner solution to this problem would be to pass os.devnull to
133 # A cleaner solution to this problem would be to pass os.devnull to
134 # Popen directly. Unfortunately, that does not work.
134 # Popen directly. Unfortunately, that does not work.
135 if executable.endswith('pythonw.exe'):
135 if executable.endswith('pythonw.exe'):
136 arguments.append('--no-stdout')
136 arguments.append('--no-stdout')
137 arguments.append('--no-stderr')
137 arguments.append('--no-stderr')
138
138
139 return arguments
139 return arguments
140
140
141
141
142 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
142 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
143 independent=False,
143 independent=False,
144 cwd=None, ipython_kernel=True,
144 cwd=None, ipython_kernel=True,
145 **kw
145 **kw
146 ):
146 ):
147 """ Launches a localhost kernel, binding to the specified ports.
147 """ Launches a localhost kernel, binding to the specified ports.
148
148
149 Parameters
149 Parameters
150 ----------
150 ----------
151 cmd : Popen list,
151 cmd : Popen list,
152 A string of Python code that imports and executes a kernel entry point.
152 A string of Python code that imports and executes a kernel entry point.
153
153
154 stdin, stdout, stderr : optional (default None)
154 stdin, stdout, stderr : optional (default None)
155 Standards streams, as defined in subprocess.Popen.
155 Standards streams, as defined in subprocess.Popen.
156
156
157 independent : bool, optional (default False)
157 independent : bool, optional (default False)
158 If set, the kernel process is guaranteed to survive if this process
158 If set, the kernel process is guaranteed to survive if this process
159 dies. If not set, an effort is made to ensure that the kernel is killed
159 dies. If not set, an effort is made to ensure that the kernel is killed
160 when this process dies. Note that in this case it is still good practice
160 when this process dies. Note that in this case it is still good practice
161 to kill kernels manually before exiting.
161 to kill kernels manually before exiting.
162
162
163 cwd : path, optional
163 cwd : path, optional
164 The working dir of the kernel process (default: cwd of this process).
164 The working dir of the kernel process (default: cwd of this process).
165
165
166 ipython_kernel : bool, optional
166 ipython_kernel : bool, optional
167 Whether the kernel is an official IPython one,
167 Whether the kernel is an official IPython one,
168 and should get a bit of special treatment.
168 and should get a bit of special treatment.
169
169
170 Returns
170 Returns
171 -------
171 -------
172
172
173 Popen instance for the kernel subprocess
173 Popen instance for the kernel subprocess
174 """
174 """
175
175
176 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
176 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
177 # are invalid. Unfortunately, there is in general no way to detect whether
177 # are invalid. Unfortunately, there is in general no way to detect whether
178 # they are valid. The following two blocks redirect them to (temporary)
178 # they are valid. The following two blocks redirect them to (temporary)
179 # pipes in certain important cases.
179 # pipes in certain important cases.
180
180
181 # If this process has been backgrounded, our stdin is invalid. Since there
181 # If this process has been backgrounded, our stdin is invalid. Since there
182 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
182 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
183 # place this one safe and always redirect.
183 # place this one safe and always redirect.
184 redirect_in = True
184 redirect_in = True
185 _stdin = PIPE if stdin is None else stdin
185 _stdin = PIPE if stdin is None else stdin
186
186
187 # If this process in running on pythonw, we know that stdin, stdout, and
187 # If this process in running on pythonw, we know that stdin, stdout, and
188 # stderr are all invalid.
188 # stderr are all invalid.
189 redirect_out = sys.executable.endswith('pythonw.exe')
189 redirect_out = sys.executable.endswith('pythonw.exe')
190 if redirect_out:
190 if redirect_out:
191 _stdout = PIPE if stdout is None else stdout
191 _stdout = PIPE if stdout is None else stdout
192 _stderr = PIPE if stderr is None else stderr
192 _stderr = PIPE if stderr is None else stderr
193 else:
193 else:
194 _stdout, _stderr = stdout, stderr
194 _stdout, _stderr = stdout, stderr
195
195
196 # Spawn a kernel.
196 # Spawn a kernel.
197 if sys.platform == 'win32':
197 if sys.platform == 'win32':
198
198
199 # Create a Win32 event for interrupting the kernel.
199 # Create a Win32 event for interrupting the kernel.
200 interrupt_event = ParentPollerWindows.create_interrupt_event()
200 interrupt_event = ParentPollerWindows.create_interrupt_event()
201 if ipython_kernel:
201 if ipython_kernel:
202 cmd += [ '--interrupt=%i' % interrupt_event ]
202 cmd += [ '--interrupt=%i' % interrupt_event ]
203
203
204 # If the kernel is running on pythonw and stdout/stderr are not been
204 # If the kernel is running on pythonw and stdout/stderr are not been
205 # re-directed, it will crash when more than 4KB of data is written to
205 # re-directed, it will crash when more than 4KB of data is written to
206 # stdout or stderr. This is a bug that has been with Python for a very
206 # stdout or stderr. This is a bug that has been with Python for a very
207 # long time; see http://bugs.python.org/issue706263.
207 # long time; see http://bugs.python.org/issue706263.
208 # A cleaner solution to this problem would be to pass os.devnull to
208 # A cleaner solution to this problem would be to pass os.devnull to
209 # Popen directly. Unfortunately, that does not work.
209 # Popen directly. Unfortunately, that does not work.
210 if cmd[0].endswith('pythonw.exe'):
210 if cmd[0].endswith('pythonw.exe'):
211 if stdout is None:
211 if stdout is None:
212 cmd.append('--no-stdout')
212 cmd.append('--no-stdout')
213 if stderr is None:
213 if stderr is None:
214 cmd.append('--no-stderr')
214 cmd.append('--no-stderr')
215
215
216 # Launch the kernel process.
216 # Launch the kernel process.
217 if independent:
217 if independent:
218 proc = Popen(cmd,
218 proc = Popen(cmd,
219 creationflags=512, # CREATE_NEW_PROCESS_GROUP
219 creationflags=512, # CREATE_NEW_PROCESS_GROUP
220 stdin=_stdin, stdout=_stdout, stderr=_stderr)
220 stdin=_stdin, stdout=_stdout, stderr=_stderr)
221 else:
221 else:
222 if ipython_kernel:
222 if ipython_kernel:
223 try:
223 try:
224 from _winapi import DuplicateHandle, GetCurrentProcess, \
224 from _winapi import DuplicateHandle, GetCurrentProcess, \
225 DUPLICATE_SAME_ACCESS
225 DUPLICATE_SAME_ACCESS
226 except:
226 except:
227 from _subprocess import DuplicateHandle, GetCurrentProcess, \
227 from _subprocess import DuplicateHandle, GetCurrentProcess, \
228 DUPLICATE_SAME_ACCESS
228 DUPLICATE_SAME_ACCESS
229 pid = GetCurrentProcess()
229 pid = GetCurrentProcess()
230 handle = DuplicateHandle(pid, pid, pid, 0,
230 handle = DuplicateHandle(pid, pid, pid, 0,
231 True, # Inheritable by new processes.
231 True, # Inheritable by new processes.
232 DUPLICATE_SAME_ACCESS)
232 DUPLICATE_SAME_ACCESS)
233 cmd +=[ '--parent=%i' % handle ]
233 cmd +=[ '--parent=%i' % handle ]
234
234
235
235
236 proc = Popen(cmd,
236 proc = Popen(cmd,
237 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
237 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
238
238
239 # Attach the interrupt event to the Popen objet so it can be used later.
239 # Attach the interrupt event to the Popen objet so it can be used later.
240 proc.win32_interrupt_event = interrupt_event
240 proc.win32_interrupt_event = interrupt_event
241
241
242 else:
242 else:
243 if independent:
243 if independent:
244 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
244 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
245 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
245 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
246 else:
246 else:
247 if ipython_kernel:
247 if ipython_kernel:
248 cmd += ['--parent=1']
248 cmd += ['--parent=1']
249 proc = Popen(cmd,
249 proc = Popen(cmd,
250 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
250 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd)
251
251
252 # Clean up pipes created to work around Popen bug.
252 # Clean up pipes created to work around Popen bug.
253 if redirect_in:
253 if redirect_in:
254 if stdin is None:
254 if stdin is None:
255 proc.stdin.close()
255 proc.stdin.close()
256 if redirect_out:
256 if redirect_out:
257 if stdout is None:
257 if stdout is None:
258 proc.stdout.close()
258 proc.stdout.close()
259 if stderr is None:
259 if stderr is None:
260 proc.stderr.close()
260 proc.stderr.close()
261
261
262 return proc
262 return proc
@@ -1,1134 +1,1134 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 # Standard library imports.
18 # Standard library imports.
19 import atexit
19 import atexit
20 import errno
20 import errno
21 import json
21 import json
22 from subprocess import Popen
22 from subprocess import Popen
23 import os
23 import os
24 import signal
24 import signal
25 import sys
25 import sys
26 from threading import Thread
26 from threading import Thread
27 import time
27 import time
28
28
29 # System library imports.
29 # System library imports.
30 import zmq
30 import zmq
31 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
31 # import ZMQError in top-level namespace, to avoid ugly attribute-error messages
32 # during garbage collection of threads at exit:
32 # during garbage collection of threads at exit:
33 from zmq import ZMQError
33 from zmq import ZMQError
34 from zmq.eventloop import ioloop, zmqstream
34 from zmq.eventloop import ioloop, zmqstream
35
35
36 # Local imports.
36 # Local imports.
37 from IPython.config.configurable import Configurable
37 from IPython.config.configurable import Configurable
38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
39 from IPython.utils.traitlets import (
39 from IPython.utils.traitlets import (
40 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
40 Any, Instance, Type, Unicode, List, Integer, Bool, CaselessStrEnum
41 )
41 )
42 from IPython.utils.py3compat import str_to_bytes
42 from IPython.utils.py3compat import str_to_bytes
43 from IPython.zmq.entry_point import (
43 from IPython.zmq.entry_point import (
44 write_connection_file,
44 write_connection_file,
45 make_kernel_cmd,
45 make_ipkernel_cmd,
46 launch_kernel,
46 launch_kernel,
47 )
47 )
48 from session import Session
48 from session import Session
49 from IPython.zmq.kernelmanagerabc import (
49 from IPython.zmq.kernelmanagerabc import (
50 ShellChannelABC, IOPubChannelABC,
50 ShellChannelABC, IOPubChannelABC,
51 HBChannelABC, StdInChannelABC,
51 HBChannelABC, StdInChannelABC,
52 KernelManagerABC
52 KernelManagerABC
53 )
53 )
54
54
55
55
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57 # Constants and exceptions
57 # Constants and exceptions
58 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
59
59
60 class InvalidPortNumber(Exception):
60 class InvalidPortNumber(Exception):
61 pass
61 pass
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Utility functions
64 # Utility functions
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67 # some utilities to validate message structure, these might get moved elsewhere
67 # some utilities to validate message structure, these might get moved elsewhere
68 # if they prove to have more generic utility
68 # if they prove to have more generic utility
69
69
70 def validate_string_list(lst):
70 def validate_string_list(lst):
71 """Validate that the input is a list of strings.
71 """Validate that the input is a list of strings.
72
72
73 Raises ValueError if not."""
73 Raises ValueError if not."""
74 if not isinstance(lst, list):
74 if not isinstance(lst, list):
75 raise ValueError('input %r must be a list' % lst)
75 raise ValueError('input %r must be a list' % lst)
76 for x in lst:
76 for x in lst:
77 if not isinstance(x, basestring):
77 if not isinstance(x, basestring):
78 raise ValueError('element %r in list must be a string' % x)
78 raise ValueError('element %r in list must be a string' % x)
79
79
80
80
81 def validate_string_dict(dct):
81 def validate_string_dict(dct):
82 """Validate that the input is a dict with string keys and values.
82 """Validate that the input is a dict with string keys and values.
83
83
84 Raises ValueError if not."""
84 Raises ValueError if not."""
85 for k,v in dct.iteritems():
85 for k,v in dct.iteritems():
86 if not isinstance(k, basestring):
86 if not isinstance(k, basestring):
87 raise ValueError('key %r in dict must be a string' % k)
87 raise ValueError('key %r in dict must be a string' % k)
88 if not isinstance(v, basestring):
88 if not isinstance(v, basestring):
89 raise ValueError('value %r in dict must be a string' % v)
89 raise ValueError('value %r in dict must be a string' % v)
90
90
91
91
92 #-----------------------------------------------------------------------------
92 #-----------------------------------------------------------------------------
93 # ZMQ Socket Channel classes
93 # ZMQ Socket Channel classes
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95
95
96 class ZMQSocketChannel(Thread):
96 class ZMQSocketChannel(Thread):
97 """The base class for the channels that use ZMQ sockets."""
97 """The base class for the channels that use ZMQ sockets."""
98 context = None
98 context = None
99 session = None
99 session = None
100 socket = None
100 socket = None
101 ioloop = None
101 ioloop = None
102 stream = None
102 stream = None
103 _address = None
103 _address = None
104 _exiting = False
104 _exiting = False
105
105
106 def __init__(self, context, session, address):
106 def __init__(self, context, session, address):
107 """Create a channel.
107 """Create a channel.
108
108
109 Parameters
109 Parameters
110 ----------
110 ----------
111 context : :class:`zmq.Context`
111 context : :class:`zmq.Context`
112 The ZMQ context to use.
112 The ZMQ context to use.
113 session : :class:`session.Session`
113 session : :class:`session.Session`
114 The session to use.
114 The session to use.
115 address : zmq url
115 address : zmq url
116 Standard (ip, port) tuple that the kernel is listening on.
116 Standard (ip, port) tuple that the kernel is listening on.
117 """
117 """
118 super(ZMQSocketChannel, self).__init__()
118 super(ZMQSocketChannel, self).__init__()
119 self.daemon = True
119 self.daemon = True
120
120
121 self.context = context
121 self.context = context
122 self.session = session
122 self.session = session
123 if isinstance(address, tuple):
123 if isinstance(address, tuple):
124 if address[1] == 0:
124 if address[1] == 0:
125 message = 'The port number for a channel cannot be 0.'
125 message = 'The port number for a channel cannot be 0.'
126 raise InvalidPortNumber(message)
126 raise InvalidPortNumber(message)
127 address = "tcp://%s:%i" % address
127 address = "tcp://%s:%i" % address
128 self._address = address
128 self._address = address
129 atexit.register(self._notice_exit)
129 atexit.register(self._notice_exit)
130
130
131 def _notice_exit(self):
131 def _notice_exit(self):
132 self._exiting = True
132 self._exiting = True
133
133
134 def _run_loop(self):
134 def _run_loop(self):
135 """Run my loop, ignoring EINTR events in the poller"""
135 """Run my loop, ignoring EINTR events in the poller"""
136 while True:
136 while True:
137 try:
137 try:
138 self.ioloop.start()
138 self.ioloop.start()
139 except ZMQError as e:
139 except ZMQError as e:
140 if e.errno == errno.EINTR:
140 if e.errno == errno.EINTR:
141 continue
141 continue
142 else:
142 else:
143 raise
143 raise
144 except Exception:
144 except Exception:
145 if self._exiting:
145 if self._exiting:
146 break
146 break
147 else:
147 else:
148 raise
148 raise
149 else:
149 else:
150 break
150 break
151
151
152 def stop(self):
152 def stop(self):
153 """Stop the channel's event loop and join its thread.
153 """Stop the channel's event loop and join its thread.
154
154
155 This calls :method:`Thread.join` and returns when the thread
155 This calls :method:`Thread.join` and returns when the thread
156 terminates. :class:`RuntimeError` will be raised if
156 terminates. :class:`RuntimeError` will be raised if
157 :method:`self.start` is called again.
157 :method:`self.start` is called again.
158 """
158 """
159 self.join()
159 self.join()
160
160
161 @property
161 @property
162 def address(self):
162 def address(self):
163 """Get the channel's address as a zmq url string.
163 """Get the channel's address as a zmq url string.
164
164
165 These URLS have the form: 'tcp://127.0.0.1:5555'.
165 These URLS have the form: 'tcp://127.0.0.1:5555'.
166 """
166 """
167 return self._address
167 return self._address
168
168
169 def _queue_send(self, msg):
169 def _queue_send(self, msg):
170 """Queue a message to be sent from the IOLoop's thread.
170 """Queue a message to be sent from the IOLoop's thread.
171
171
172 Parameters
172 Parameters
173 ----------
173 ----------
174 msg : message to send
174 msg : message to send
175
175
176 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
176 This is threadsafe, as it uses IOLoop.add_callback to give the loop's
177 thread control of the action.
177 thread control of the action.
178 """
178 """
179 def thread_send():
179 def thread_send():
180 self.session.send(self.stream, msg)
180 self.session.send(self.stream, msg)
181 self.ioloop.add_callback(thread_send)
181 self.ioloop.add_callback(thread_send)
182
182
183 def _handle_recv(self, msg):
183 def _handle_recv(self, msg):
184 """Callback for stream.on_recv.
184 """Callback for stream.on_recv.
185
185
186 Unpacks message, and calls handlers with it.
186 Unpacks message, and calls handlers with it.
187 """
187 """
188 ident,smsg = self.session.feed_identities(msg)
188 ident,smsg = self.session.feed_identities(msg)
189 self.call_handlers(self.session.unserialize(smsg))
189 self.call_handlers(self.session.unserialize(smsg))
190
190
191
191
192
192
193 class ShellChannel(ZMQSocketChannel):
193 class ShellChannel(ZMQSocketChannel):
194 """The shell channel for issuing request/replies to the kernel."""
194 """The shell channel for issuing request/replies to the kernel."""
195
195
196 command_queue = None
196 command_queue = None
197 # flag for whether execute requests should be allowed to call raw_input:
197 # flag for whether execute requests should be allowed to call raw_input:
198 allow_stdin = True
198 allow_stdin = True
199
199
200 def __init__(self, context, session, address):
200 def __init__(self, context, session, address):
201 super(ShellChannel, self).__init__(context, session, address)
201 super(ShellChannel, self).__init__(context, session, address)
202 self.ioloop = ioloop.IOLoop()
202 self.ioloop = ioloop.IOLoop()
203
203
204 def run(self):
204 def run(self):
205 """The thread's main activity. Call start() instead."""
205 """The thread's main activity. Call start() instead."""
206 self.socket = self.context.socket(zmq.DEALER)
206 self.socket = self.context.socket(zmq.DEALER)
207 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
207 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
208 self.socket.connect(self.address)
208 self.socket.connect(self.address)
209 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
209 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
210 self.stream.on_recv(self._handle_recv)
210 self.stream.on_recv(self._handle_recv)
211 self._run_loop()
211 self._run_loop()
212 try:
212 try:
213 self.socket.close()
213 self.socket.close()
214 except:
214 except:
215 pass
215 pass
216
216
217 def stop(self):
217 def stop(self):
218 """Stop the channel's event loop and join its thread."""
218 """Stop the channel's event loop and join its thread."""
219 self.ioloop.stop()
219 self.ioloop.stop()
220 super(ShellChannel, self).stop()
220 super(ShellChannel, self).stop()
221
221
222 def call_handlers(self, msg):
222 def call_handlers(self, msg):
223 """This method is called in the ioloop thread when a message arrives.
223 """This method is called in the ioloop thread when a message arrives.
224
224
225 Subclasses should override this method to handle incoming messages.
225 Subclasses should override this method to handle incoming messages.
226 It is important to remember that this method is called in the thread
226 It is important to remember that this method is called in the thread
227 so that some logic must be done to ensure that the application leve
227 so that some logic must be done to ensure that the application leve
228 handlers are called in the application thread.
228 handlers are called in the application thread.
229 """
229 """
230 raise NotImplementedError('call_handlers must be defined in a subclass.')
230 raise NotImplementedError('call_handlers must be defined in a subclass.')
231
231
232 def execute(self, code, silent=False, store_history=True,
232 def execute(self, code, silent=False, store_history=True,
233 user_variables=None, user_expressions=None, allow_stdin=None):
233 user_variables=None, user_expressions=None, allow_stdin=None):
234 """Execute code in the kernel.
234 """Execute code in the kernel.
235
235
236 Parameters
236 Parameters
237 ----------
237 ----------
238 code : str
238 code : str
239 A string of Python code.
239 A string of Python code.
240
240
241 silent : bool, optional (default False)
241 silent : bool, optional (default False)
242 If set, the kernel will execute the code as quietly possible, and
242 If set, the kernel will execute the code as quietly possible, and
243 will force store_history to be False.
243 will force store_history to be False.
244
244
245 store_history : bool, optional (default True)
245 store_history : bool, optional (default True)
246 If set, the kernel will store command history. This is forced
246 If set, the kernel will store command history. This is forced
247 to be False if silent is True.
247 to be False if silent is True.
248
248
249 user_variables : list, optional
249 user_variables : list, optional
250 A list of variable names to pull from the user's namespace. They
250 A list of variable names to pull from the user's namespace. They
251 will come back as a dict with these names as keys and their
251 will come back as a dict with these names as keys and their
252 :func:`repr` as values.
252 :func:`repr` as values.
253
253
254 user_expressions : dict, optional
254 user_expressions : dict, optional
255 A dict mapping names to expressions to be evaluated in the user's
255 A dict mapping names to expressions to be evaluated in the user's
256 dict. The expression values are returned as strings formatted using
256 dict. The expression values are returned as strings formatted using
257 :func:`repr`.
257 :func:`repr`.
258
258
259 allow_stdin : bool, optional (default self.allow_stdin)
259 allow_stdin : bool, optional (default self.allow_stdin)
260 Flag for whether the kernel can send stdin requests to frontends.
260 Flag for whether the kernel can send stdin requests to frontends.
261
261
262 Some frontends (e.g. the Notebook) do not support stdin requests.
262 Some frontends (e.g. the Notebook) do not support stdin requests.
263 If raw_input is called from code executed from such a frontend, a
263 If raw_input is called from code executed from such a frontend, a
264 StdinNotImplementedError will be raised.
264 StdinNotImplementedError will be raised.
265
265
266 Returns
266 Returns
267 -------
267 -------
268 The msg_id of the message sent.
268 The msg_id of the message sent.
269 """
269 """
270 if user_variables is None:
270 if user_variables is None:
271 user_variables = []
271 user_variables = []
272 if user_expressions is None:
272 if user_expressions is None:
273 user_expressions = {}
273 user_expressions = {}
274 if allow_stdin is None:
274 if allow_stdin is None:
275 allow_stdin = self.allow_stdin
275 allow_stdin = self.allow_stdin
276
276
277
277
278 # Don't waste network traffic if inputs are invalid
278 # Don't waste network traffic if inputs are invalid
279 if not isinstance(code, basestring):
279 if not isinstance(code, basestring):
280 raise ValueError('code %r must be a string' % code)
280 raise ValueError('code %r must be a string' % code)
281 validate_string_list(user_variables)
281 validate_string_list(user_variables)
282 validate_string_dict(user_expressions)
282 validate_string_dict(user_expressions)
283
283
284 # Create class for content/msg creation. Related to, but possibly
284 # Create class for content/msg creation. Related to, but possibly
285 # not in Session.
285 # not in Session.
286 content = dict(code=code, silent=silent, store_history=store_history,
286 content = dict(code=code, silent=silent, store_history=store_history,
287 user_variables=user_variables,
287 user_variables=user_variables,
288 user_expressions=user_expressions,
288 user_expressions=user_expressions,
289 allow_stdin=allow_stdin,
289 allow_stdin=allow_stdin,
290 )
290 )
291 msg = self.session.msg('execute_request', content)
291 msg = self.session.msg('execute_request', content)
292 self._queue_send(msg)
292 self._queue_send(msg)
293 return msg['header']['msg_id']
293 return msg['header']['msg_id']
294
294
295 def complete(self, text, line, cursor_pos, block=None):
295 def complete(self, text, line, cursor_pos, block=None):
296 """Tab complete text in the kernel's namespace.
296 """Tab complete text in the kernel's namespace.
297
297
298 Parameters
298 Parameters
299 ----------
299 ----------
300 text : str
300 text : str
301 The text to complete.
301 The text to complete.
302 line : str
302 line : str
303 The full line of text that is the surrounding context for the
303 The full line of text that is the surrounding context for the
304 text to complete.
304 text to complete.
305 cursor_pos : int
305 cursor_pos : int
306 The position of the cursor in the line where the completion was
306 The position of the cursor in the line where the completion was
307 requested.
307 requested.
308 block : str, optional
308 block : str, optional
309 The full block of code in which the completion is being requested.
309 The full block of code in which the completion is being requested.
310
310
311 Returns
311 Returns
312 -------
312 -------
313 The msg_id of the message sent.
313 The msg_id of the message sent.
314 """
314 """
315 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
315 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
316 msg = self.session.msg('complete_request', content)
316 msg = self.session.msg('complete_request', content)
317 self._queue_send(msg)
317 self._queue_send(msg)
318 return msg['header']['msg_id']
318 return msg['header']['msg_id']
319
319
320 def object_info(self, oname, detail_level=0):
320 def object_info(self, oname, detail_level=0):
321 """Get metadata information about an object in the kernel's namespace.
321 """Get metadata information about an object in the kernel's namespace.
322
322
323 Parameters
323 Parameters
324 ----------
324 ----------
325 oname : str
325 oname : str
326 A string specifying the object name.
326 A string specifying the object name.
327 detail_level : int, optional
327 detail_level : int, optional
328 The level of detail for the introspection (0-2)
328 The level of detail for the introspection (0-2)
329
329
330 Returns
330 Returns
331 -------
331 -------
332 The msg_id of the message sent.
332 The msg_id of the message sent.
333 """
333 """
334 content = dict(oname=oname, detail_level=detail_level)
334 content = dict(oname=oname, detail_level=detail_level)
335 msg = self.session.msg('object_info_request', content)
335 msg = self.session.msg('object_info_request', content)
336 self._queue_send(msg)
336 self._queue_send(msg)
337 return msg['header']['msg_id']
337 return msg['header']['msg_id']
338
338
339 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
339 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
340 """Get entries from the kernel's history list.
340 """Get entries from the kernel's history list.
341
341
342 Parameters
342 Parameters
343 ----------
343 ----------
344 raw : bool
344 raw : bool
345 If True, return the raw input.
345 If True, return the raw input.
346 output : bool
346 output : bool
347 If True, then return the output as well.
347 If True, then return the output as well.
348 hist_access_type : str
348 hist_access_type : str
349 'range' (fill in session, start and stop params), 'tail' (fill in n)
349 'range' (fill in session, start and stop params), 'tail' (fill in n)
350 or 'search' (fill in pattern param).
350 or 'search' (fill in pattern param).
351
351
352 session : int
352 session : int
353 For a range request, the session from which to get lines. Session
353 For a range request, the session from which to get lines. Session
354 numbers are positive integers; negative ones count back from the
354 numbers are positive integers; negative ones count back from the
355 current session.
355 current session.
356 start : int
356 start : int
357 The first line number of a history range.
357 The first line number of a history range.
358 stop : int
358 stop : int
359 The final (excluded) line number of a history range.
359 The final (excluded) line number of a history range.
360
360
361 n : int
361 n : int
362 The number of lines of history to get for a tail request.
362 The number of lines of history to get for a tail request.
363
363
364 pattern : str
364 pattern : str
365 The glob-syntax pattern for a search request.
365 The glob-syntax pattern for a search request.
366
366
367 Returns
367 Returns
368 -------
368 -------
369 The msg_id of the message sent.
369 The msg_id of the message sent.
370 """
370 """
371 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
371 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
372 **kwargs)
372 **kwargs)
373 msg = self.session.msg('history_request', content)
373 msg = self.session.msg('history_request', content)
374 self._queue_send(msg)
374 self._queue_send(msg)
375 return msg['header']['msg_id']
375 return msg['header']['msg_id']
376
376
377 def kernel_info(self):
377 def kernel_info(self):
378 """Request kernel info."""
378 """Request kernel info."""
379 msg = self.session.msg('kernel_info_request')
379 msg = self.session.msg('kernel_info_request')
380 self._queue_send(msg)
380 self._queue_send(msg)
381 return msg['header']['msg_id']
381 return msg['header']['msg_id']
382
382
383 def shutdown(self, restart=False):
383 def shutdown(self, restart=False):
384 """Request an immediate kernel shutdown.
384 """Request an immediate kernel shutdown.
385
385
386 Upon receipt of the (empty) reply, client code can safely assume that
386 Upon receipt of the (empty) reply, client code can safely assume that
387 the kernel has shut down and it's safe to forcefully terminate it if
387 the kernel has shut down and it's safe to forcefully terminate it if
388 it's still alive.
388 it's still alive.
389
389
390 The kernel will send the reply via a function registered with Python's
390 The kernel will send the reply via a function registered with Python's
391 atexit module, ensuring it's truly done as the kernel is done with all
391 atexit module, ensuring it's truly done as the kernel is done with all
392 normal operation.
392 normal operation.
393 """
393 """
394 # Send quit message to kernel. Once we implement kernel-side setattr,
394 # Send quit message to kernel. Once we implement kernel-side setattr,
395 # this should probably be done that way, but for now this will do.
395 # this should probably be done that way, but for now this will do.
396 msg = self.session.msg('shutdown_request', {'restart':restart})
396 msg = self.session.msg('shutdown_request', {'restart':restart})
397 self._queue_send(msg)
397 self._queue_send(msg)
398 return msg['header']['msg_id']
398 return msg['header']['msg_id']
399
399
400
400
401
401
402 class IOPubChannel(ZMQSocketChannel):
402 class IOPubChannel(ZMQSocketChannel):
403 """The iopub channel which listens for messages that the kernel publishes.
403 """The iopub channel which listens for messages that the kernel publishes.
404
404
405 This channel is where all output is published to frontends.
405 This channel is where all output is published to frontends.
406 """
406 """
407
407
408 def __init__(self, context, session, address):
408 def __init__(self, context, session, address):
409 super(IOPubChannel, self).__init__(context, session, address)
409 super(IOPubChannel, self).__init__(context, session, address)
410 self.ioloop = ioloop.IOLoop()
410 self.ioloop = ioloop.IOLoop()
411
411
412 def run(self):
412 def run(self):
413 """The thread's main activity. Call start() instead."""
413 """The thread's main activity. Call start() instead."""
414 self.socket = self.context.socket(zmq.SUB)
414 self.socket = self.context.socket(zmq.SUB)
415 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
415 self.socket.setsockopt(zmq.SUBSCRIBE,b'')
416 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
416 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
417 self.socket.connect(self.address)
417 self.socket.connect(self.address)
418 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
418 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
419 self.stream.on_recv(self._handle_recv)
419 self.stream.on_recv(self._handle_recv)
420 self._run_loop()
420 self._run_loop()
421 try:
421 try:
422 self.socket.close()
422 self.socket.close()
423 except:
423 except:
424 pass
424 pass
425
425
426 def stop(self):
426 def stop(self):
427 """Stop the channel's event loop and join its thread."""
427 """Stop the channel's event loop and join its thread."""
428 self.ioloop.stop()
428 self.ioloop.stop()
429 super(IOPubChannel, self).stop()
429 super(IOPubChannel, self).stop()
430
430
431 def call_handlers(self, msg):
431 def call_handlers(self, msg):
432 """This method is called in the ioloop thread when a message arrives.
432 """This method is called in the ioloop thread when a message arrives.
433
433
434 Subclasses should override this method to handle incoming messages.
434 Subclasses should override this method to handle incoming messages.
435 It is important to remember that this method is called in the thread
435 It is important to remember that this method is called in the thread
436 so that some logic must be done to ensure that the application leve
436 so that some logic must be done to ensure that the application leve
437 handlers are called in the application thread.
437 handlers are called in the application thread.
438 """
438 """
439 raise NotImplementedError('call_handlers must be defined in a subclass.')
439 raise NotImplementedError('call_handlers must be defined in a subclass.')
440
440
441 def flush(self, timeout=1.0):
441 def flush(self, timeout=1.0):
442 """Immediately processes all pending messages on the iopub channel.
442 """Immediately processes all pending messages on the iopub channel.
443
443
444 Callers should use this method to ensure that :method:`call_handlers`
444 Callers should use this method to ensure that :method:`call_handlers`
445 has been called for all messages that have been received on the
445 has been called for all messages that have been received on the
446 0MQ SUB socket of this channel.
446 0MQ SUB socket of this channel.
447
447
448 This method is thread safe.
448 This method is thread safe.
449
449
450 Parameters
450 Parameters
451 ----------
451 ----------
452 timeout : float, optional
452 timeout : float, optional
453 The maximum amount of time to spend flushing, in seconds. The
453 The maximum amount of time to spend flushing, in seconds. The
454 default is one second.
454 default is one second.
455 """
455 """
456 # We do the IOLoop callback process twice to ensure that the IOLoop
456 # We do the IOLoop callback process twice to ensure that the IOLoop
457 # gets to perform at least one full poll.
457 # gets to perform at least one full poll.
458 stop_time = time.time() + timeout
458 stop_time = time.time() + timeout
459 for i in xrange(2):
459 for i in xrange(2):
460 self._flushed = False
460 self._flushed = False
461 self.ioloop.add_callback(self._flush)
461 self.ioloop.add_callback(self._flush)
462 while not self._flushed and time.time() < stop_time:
462 while not self._flushed and time.time() < stop_time:
463 time.sleep(0.01)
463 time.sleep(0.01)
464
464
465 def _flush(self):
465 def _flush(self):
466 """Callback for :method:`self.flush`."""
466 """Callback for :method:`self.flush`."""
467 self.stream.flush()
467 self.stream.flush()
468 self._flushed = True
468 self._flushed = True
469
469
470
470
471 class StdInChannel(ZMQSocketChannel):
471 class StdInChannel(ZMQSocketChannel):
472 """The stdin channel to handle raw_input requests that the kernel makes."""
472 """The stdin channel to handle raw_input requests that the kernel makes."""
473
473
474 msg_queue = None
474 msg_queue = None
475
475
476 def __init__(self, context, session, address):
476 def __init__(self, context, session, address):
477 super(StdInChannel, self).__init__(context, session, address)
477 super(StdInChannel, self).__init__(context, session, address)
478 self.ioloop = ioloop.IOLoop()
478 self.ioloop = ioloop.IOLoop()
479
479
480 def run(self):
480 def run(self):
481 """The thread's main activity. Call start() instead."""
481 """The thread's main activity. Call start() instead."""
482 self.socket = self.context.socket(zmq.DEALER)
482 self.socket = self.context.socket(zmq.DEALER)
483 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
483 self.socket.setsockopt(zmq.IDENTITY, self.session.bsession)
484 self.socket.connect(self.address)
484 self.socket.connect(self.address)
485 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
485 self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
486 self.stream.on_recv(self._handle_recv)
486 self.stream.on_recv(self._handle_recv)
487 self._run_loop()
487 self._run_loop()
488 try:
488 try:
489 self.socket.close()
489 self.socket.close()
490 except:
490 except:
491 pass
491 pass
492
492
493 def stop(self):
493 def stop(self):
494 """Stop the channel's event loop and join its thread."""
494 """Stop the channel's event loop and join its thread."""
495 self.ioloop.stop()
495 self.ioloop.stop()
496 super(StdInChannel, self).stop()
496 super(StdInChannel, self).stop()
497
497
498 def call_handlers(self, msg):
498 def call_handlers(self, msg):
499 """This method is called in the ioloop thread when a message arrives.
499 """This method is called in the ioloop thread when a message arrives.
500
500
501 Subclasses should override this method to handle incoming messages.
501 Subclasses should override this method to handle incoming messages.
502 It is important to remember that this method is called in the thread
502 It is important to remember that this method is called in the thread
503 so that some logic must be done to ensure that the application leve
503 so that some logic must be done to ensure that the application leve
504 handlers are called in the application thread.
504 handlers are called in the application thread.
505 """
505 """
506 raise NotImplementedError('call_handlers must be defined in a subclass.')
506 raise NotImplementedError('call_handlers must be defined in a subclass.')
507
507
508 def input(self, string):
508 def input(self, string):
509 """Send a string of raw input to the kernel."""
509 """Send a string of raw input to the kernel."""
510 content = dict(value=string)
510 content = dict(value=string)
511 msg = self.session.msg('input_reply', content)
511 msg = self.session.msg('input_reply', content)
512 self._queue_send(msg)
512 self._queue_send(msg)
513
513
514
514
515 class HBChannel(ZMQSocketChannel):
515 class HBChannel(ZMQSocketChannel):
516 """The heartbeat channel which monitors the kernel heartbeat.
516 """The heartbeat channel which monitors the kernel heartbeat.
517
517
518 Note that the heartbeat channel is paused by default. As long as you start
518 Note that the heartbeat channel is paused by default. As long as you start
519 this channel, the kernel manager will ensure that it is paused and un-paused
519 this channel, the kernel manager will ensure that it is paused and un-paused
520 as appropriate.
520 as appropriate.
521 """
521 """
522
522
523 time_to_dead = 3.0
523 time_to_dead = 3.0
524 socket = None
524 socket = None
525 poller = None
525 poller = None
526 _running = None
526 _running = None
527 _pause = None
527 _pause = None
528 _beating = None
528 _beating = None
529
529
530 def __init__(self, context, session, address):
530 def __init__(self, context, session, address):
531 super(HBChannel, self).__init__(context, session, address)
531 super(HBChannel, self).__init__(context, session, address)
532 self._running = False
532 self._running = False
533 self._pause =True
533 self._pause =True
534 self.poller = zmq.Poller()
534 self.poller = zmq.Poller()
535
535
536 def _create_socket(self):
536 def _create_socket(self):
537 if self.socket is not None:
537 if self.socket is not None:
538 # close previous socket, before opening a new one
538 # close previous socket, before opening a new one
539 self.poller.unregister(self.socket)
539 self.poller.unregister(self.socket)
540 self.socket.close()
540 self.socket.close()
541 self.socket = self.context.socket(zmq.REQ)
541 self.socket = self.context.socket(zmq.REQ)
542 self.socket.setsockopt(zmq.LINGER, 0)
542 self.socket.setsockopt(zmq.LINGER, 0)
543 self.socket.connect(self.address)
543 self.socket.connect(self.address)
544
544
545 self.poller.register(self.socket, zmq.POLLIN)
545 self.poller.register(self.socket, zmq.POLLIN)
546
546
547 def _poll(self, start_time):
547 def _poll(self, start_time):
548 """poll for heartbeat replies until we reach self.time_to_dead.
548 """poll for heartbeat replies until we reach self.time_to_dead.
549
549
550 Ignores interrupts, and returns the result of poll(), which
550 Ignores interrupts, and returns the result of poll(), which
551 will be an empty list if no messages arrived before the timeout,
551 will be an empty list if no messages arrived before the timeout,
552 or the event tuple if there is a message to receive.
552 or the event tuple if there is a message to receive.
553 """
553 """
554
554
555 until_dead = self.time_to_dead - (time.time() - start_time)
555 until_dead = self.time_to_dead - (time.time() - start_time)
556 # ensure poll at least once
556 # ensure poll at least once
557 until_dead = max(until_dead, 1e-3)
557 until_dead = max(until_dead, 1e-3)
558 events = []
558 events = []
559 while True:
559 while True:
560 try:
560 try:
561 events = self.poller.poll(1000 * until_dead)
561 events = self.poller.poll(1000 * until_dead)
562 except ZMQError as e:
562 except ZMQError as e:
563 if e.errno == errno.EINTR:
563 if e.errno == errno.EINTR:
564 # ignore interrupts during heartbeat
564 # ignore interrupts during heartbeat
565 # this may never actually happen
565 # this may never actually happen
566 until_dead = self.time_to_dead - (time.time() - start_time)
566 until_dead = self.time_to_dead - (time.time() - start_time)
567 until_dead = max(until_dead, 1e-3)
567 until_dead = max(until_dead, 1e-3)
568 pass
568 pass
569 else:
569 else:
570 raise
570 raise
571 except Exception:
571 except Exception:
572 if self._exiting:
572 if self._exiting:
573 break
573 break
574 else:
574 else:
575 raise
575 raise
576 else:
576 else:
577 break
577 break
578 return events
578 return events
579
579
580 def run(self):
580 def run(self):
581 """The thread's main activity. Call start() instead."""
581 """The thread's main activity. Call start() instead."""
582 self._create_socket()
582 self._create_socket()
583 self._running = True
583 self._running = True
584 self._beating = True
584 self._beating = True
585
585
586 while self._running:
586 while self._running:
587 if self._pause:
587 if self._pause:
588 # just sleep, and skip the rest of the loop
588 # just sleep, and skip the rest of the loop
589 time.sleep(self.time_to_dead)
589 time.sleep(self.time_to_dead)
590 continue
590 continue
591
591
592 since_last_heartbeat = 0.0
592 since_last_heartbeat = 0.0
593 # io.rprint('Ping from HB channel') # dbg
593 # io.rprint('Ping from HB channel') # dbg
594 # no need to catch EFSM here, because the previous event was
594 # no need to catch EFSM here, because the previous event was
595 # either a recv or connect, which cannot be followed by EFSM
595 # either a recv or connect, which cannot be followed by EFSM
596 self.socket.send(b'ping')
596 self.socket.send(b'ping')
597 request_time = time.time()
597 request_time = time.time()
598 ready = self._poll(request_time)
598 ready = self._poll(request_time)
599 if ready:
599 if ready:
600 self._beating = True
600 self._beating = True
601 # the poll above guarantees we have something to recv
601 # the poll above guarantees we have something to recv
602 self.socket.recv()
602 self.socket.recv()
603 # sleep the remainder of the cycle
603 # sleep the remainder of the cycle
604 remainder = self.time_to_dead - (time.time() - request_time)
604 remainder = self.time_to_dead - (time.time() - request_time)
605 if remainder > 0:
605 if remainder > 0:
606 time.sleep(remainder)
606 time.sleep(remainder)
607 continue
607 continue
608 else:
608 else:
609 # nothing was received within the time limit, signal heart failure
609 # nothing was received within the time limit, signal heart failure
610 self._beating = False
610 self._beating = False
611 since_last_heartbeat = time.time() - request_time
611 since_last_heartbeat = time.time() - request_time
612 self.call_handlers(since_last_heartbeat)
612 self.call_handlers(since_last_heartbeat)
613 # and close/reopen the socket, because the REQ/REP cycle has been broken
613 # and close/reopen the socket, because the REQ/REP cycle has been broken
614 self._create_socket()
614 self._create_socket()
615 continue
615 continue
616 try:
616 try:
617 self.socket.close()
617 self.socket.close()
618 except:
618 except:
619 pass
619 pass
620
620
621 def pause(self):
621 def pause(self):
622 """Pause the heartbeat."""
622 """Pause the heartbeat."""
623 self._pause = True
623 self._pause = True
624
624
625 def unpause(self):
625 def unpause(self):
626 """Unpause the heartbeat."""
626 """Unpause the heartbeat."""
627 self._pause = False
627 self._pause = False
628
628
629 def is_beating(self):
629 def is_beating(self):
630 """Is the heartbeat running and responsive (and not paused)."""
630 """Is the heartbeat running and responsive (and not paused)."""
631 if self.is_alive() and not self._pause and self._beating:
631 if self.is_alive() and not self._pause and self._beating:
632 return True
632 return True
633 else:
633 else:
634 return False
634 return False
635
635
636 def stop(self):
636 def stop(self):
637 """Stop the channel's event loop and join its thread."""
637 """Stop the channel's event loop and join its thread."""
638 self._running = False
638 self._running = False
639 super(HBChannel, self).stop()
639 super(HBChannel, self).stop()
640
640
641 def call_handlers(self, since_last_heartbeat):
641 def call_handlers(self, since_last_heartbeat):
642 """This method is called in the ioloop thread when a message arrives.
642 """This method is called in the ioloop thread when a message arrives.
643
643
644 Subclasses should override this method to handle incoming messages.
644 Subclasses should override this method to handle incoming messages.
645 It is important to remember that this method is called in the thread
645 It is important to remember that this method is called in the thread
646 so that some logic must be done to ensure that the application level
646 so that some logic must be done to ensure that the application level
647 handlers are called in the application thread.
647 handlers are called in the application thread.
648 """
648 """
649 raise NotImplementedError('call_handlers must be defined in a subclass.')
649 raise NotImplementedError('call_handlers must be defined in a subclass.')
650
650
651
651
652 #-----------------------------------------------------------------------------
652 #-----------------------------------------------------------------------------
653 # Main kernel manager class
653 # Main kernel manager class
654 #-----------------------------------------------------------------------------
654 #-----------------------------------------------------------------------------
655
655
656 class KernelManager(Configurable):
656 class KernelManager(Configurable):
657 """Manages a single kernel on this host along with its channels.
657 """Manages a single kernel on this host along with its channels.
658
658
659 There are four channels associated with each kernel:
659 There are four channels associated with each kernel:
660
660
661 * shell: for request/reply calls to the kernel.
661 * shell: for request/reply calls to the kernel.
662 * iopub: for the kernel to publish results to frontends.
662 * iopub: for the kernel to publish results to frontends.
663 * hb: for monitoring the kernel's heartbeat.
663 * hb: for monitoring the kernel's heartbeat.
664 * stdin: for frontends to reply to raw_input calls in the kernel.
664 * stdin: for frontends to reply to raw_input calls in the kernel.
665
665
666 The usage of the channels that this class manages is optional. It is
666 The usage of the channels that this class manages is optional. It is
667 entirely possible to connect to the kernels directly using ZeroMQ
667 entirely possible to connect to the kernels directly using ZeroMQ
668 sockets. These channels are useful primarily for talking to a kernel
668 sockets. These channels are useful primarily for talking to a kernel
669 whose :class:`KernelManager` is in the same process.
669 whose :class:`KernelManager` is in the same process.
670
670
671 This version manages kernels started using Popen.
671 This version manages kernels started using Popen.
672 """
672 """
673 # The PyZMQ Context to use for communication with the kernel.
673 # The PyZMQ Context to use for communication with the kernel.
674 context = Instance(zmq.Context)
674 context = Instance(zmq.Context)
675 def _context_default(self):
675 def _context_default(self):
676 return zmq.Context.instance()
676 return zmq.Context.instance()
677
677
678 # The Session to use for communication with the kernel.
678 # The Session to use for communication with the kernel.
679 session = Instance(Session)
679 session = Instance(Session)
680 def _session_default(self):
680 def _session_default(self):
681 return Session(config=self.config)
681 return Session(config=self.config)
682
682
683 # The kernel process with which the KernelManager is communicating.
683 # The kernel process with which the KernelManager is communicating.
684 # generally a Popen instance
684 # generally a Popen instance
685 kernel = Any()
685 kernel = Any()
686
686
687 kernel_cmd = List(Unicode, config=True,
687 kernel_cmd = List(Unicode, config=True,
688 help="""The Popen Command to launch the kernel.
688 help="""The Popen Command to launch the kernel.
689 Override this if you have a custom
689 Override this if you have a custom
690 """
690 """
691 )
691 )
692 def _kernel_cmd_changed(self, name, old, new):
692 def _kernel_cmd_changed(self, name, old, new):
693 print 'kernel cmd changed', new
693 print 'kernel cmd changed', new
694 self.ipython_kernel = False
694 self.ipython_kernel = False
695
695
696 ipython_kernel = Bool(True)
696 ipython_kernel = Bool(True)
697
697
698
698
699 # The addresses for the communication channels.
699 # The addresses for the communication channels.
700 connection_file = Unicode('')
700 connection_file = Unicode('')
701
701
702 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
702 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
703
703
704 ip = Unicode(LOCALHOST, config=True,
704 ip = Unicode(LOCALHOST, config=True,
705 help="""Set the kernel\'s IP address [default localhost].
705 help="""Set the kernel\'s IP address [default localhost].
706 If the IP address is something other than localhost, then
706 If the IP address is something other than localhost, then
707 Consoles on other machines will be able to connect
707 Consoles on other machines will be able to connect
708 to the Kernel, so be careful!"""
708 to the Kernel, so be careful!"""
709 )
709 )
710 def _ip_default(self):
710 def _ip_default(self):
711 if self.transport == 'ipc':
711 if self.transport == 'ipc':
712 if self.connection_file:
712 if self.connection_file:
713 return os.path.splitext(self.connection_file)[0] + '-ipc'
713 return os.path.splitext(self.connection_file)[0] + '-ipc'
714 else:
714 else:
715 return 'kernel-ipc'
715 return 'kernel-ipc'
716 else:
716 else:
717 return LOCALHOST
717 return LOCALHOST
718 def _ip_changed(self, name, old, new):
718 def _ip_changed(self, name, old, new):
719 if new == '*':
719 if new == '*':
720 self.ip = '0.0.0.0'
720 self.ip = '0.0.0.0'
721 shell_port = Integer(0)
721 shell_port = Integer(0)
722 iopub_port = Integer(0)
722 iopub_port = Integer(0)
723 stdin_port = Integer(0)
723 stdin_port = Integer(0)
724 hb_port = Integer(0)
724 hb_port = Integer(0)
725
725
726 # The classes to use for the various channels.
726 # The classes to use for the various channels.
727 shell_channel_class = Type(ShellChannel)
727 shell_channel_class = Type(ShellChannel)
728 iopub_channel_class = Type(IOPubChannel)
728 iopub_channel_class = Type(IOPubChannel)
729 stdin_channel_class = Type(StdInChannel)
729 stdin_channel_class = Type(StdInChannel)
730 hb_channel_class = Type(HBChannel)
730 hb_channel_class = Type(HBChannel)
731
731
732 # Protected traits.
732 # Protected traits.
733 _launch_args = Any
733 _launch_args = Any
734 _shell_channel = Any
734 _shell_channel = Any
735 _iopub_channel = Any
735 _iopub_channel = Any
736 _stdin_channel = Any
736 _stdin_channel = Any
737 _hb_channel = Any
737 _hb_channel = Any
738 _connection_file_written=Bool(False)
738 _connection_file_written=Bool(False)
739
739
740 def __del__(self):
740 def __del__(self):
741 self.cleanup_connection_file()
741 self.cleanup_connection_file()
742
742
743 #--------------------------------------------------------------------------
743 #--------------------------------------------------------------------------
744 # Channel management methods:
744 # Channel management methods:
745 #--------------------------------------------------------------------------
745 #--------------------------------------------------------------------------
746
746
747 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
747 def start_channels(self, shell=True, iopub=True, stdin=True, hb=True):
748 """Starts the channels for this kernel.
748 """Starts the channels for this kernel.
749
749
750 This will create the channels if they do not exist and then start
750 This will create the channels if they do not exist and then start
751 them (their activity runs in a thread). If port numbers of 0 are
751 them (their activity runs in a thread). If port numbers of 0 are
752 being used (random ports) then you must first call
752 being used (random ports) then you must first call
753 :method:`start_kernel`. If the channels have been stopped and you
753 :method:`start_kernel`. If the channels have been stopped and you
754 call this, :class:`RuntimeError` will be raised.
754 call this, :class:`RuntimeError` will be raised.
755 """
755 """
756 if shell:
756 if shell:
757 self.shell_channel.start()
757 self.shell_channel.start()
758 if iopub:
758 if iopub:
759 self.iopub_channel.start()
759 self.iopub_channel.start()
760 if stdin:
760 if stdin:
761 self.stdin_channel.start()
761 self.stdin_channel.start()
762 self.shell_channel.allow_stdin = True
762 self.shell_channel.allow_stdin = True
763 else:
763 else:
764 self.shell_channel.allow_stdin = False
764 self.shell_channel.allow_stdin = False
765 if hb:
765 if hb:
766 self.hb_channel.start()
766 self.hb_channel.start()
767
767
768 def stop_channels(self):
768 def stop_channels(self):
769 """Stops all the running channels for this kernel.
769 """Stops all the running channels for this kernel.
770
770
771 This stops their event loops and joins their threads.
771 This stops their event loops and joins their threads.
772 """
772 """
773 if self.shell_channel.is_alive():
773 if self.shell_channel.is_alive():
774 self.shell_channel.stop()
774 self.shell_channel.stop()
775 if self.iopub_channel.is_alive():
775 if self.iopub_channel.is_alive():
776 self.iopub_channel.stop()
776 self.iopub_channel.stop()
777 if self.stdin_channel.is_alive():
777 if self.stdin_channel.is_alive():
778 self.stdin_channel.stop()
778 self.stdin_channel.stop()
779 if self.hb_channel.is_alive():
779 if self.hb_channel.is_alive():
780 self.hb_channel.stop()
780 self.hb_channel.stop()
781
781
782 @property
782 @property
783 def channels_running(self):
783 def channels_running(self):
784 """Are any of the channels created and running?"""
784 """Are any of the channels created and running?"""
785 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
785 return (self.shell_channel.is_alive() or self.iopub_channel.is_alive() or
786 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
786 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
787
787
788 def _make_url(self, port):
788 def _make_url(self, port):
789 """Make a zmq url with a port.
789 """Make a zmq url with a port.
790
790
791 There are two cases that this handles:
791 There are two cases that this handles:
792
792
793 * tcp: tcp://ip:port
793 * tcp: tcp://ip:port
794 * ipc: ipc://ip-port
794 * ipc: ipc://ip-port
795 """
795 """
796 if self.transport == 'tcp':
796 if self.transport == 'tcp':
797 return "tcp://%s:%i" % (self.ip, port)
797 return "tcp://%s:%i" % (self.ip, port)
798 else:
798 else:
799 return "%s://%s-%s" % (self.transport, self.ip, port)
799 return "%s://%s-%s" % (self.transport, self.ip, port)
800
800
801 @property
801 @property
802 def shell_channel(self):
802 def shell_channel(self):
803 """Get the shell channel object for this kernel."""
803 """Get the shell channel object for this kernel."""
804 if self._shell_channel is None:
804 if self._shell_channel is None:
805 self._shell_channel = self.shell_channel_class(
805 self._shell_channel = self.shell_channel_class(
806 self.context, self.session, self._make_url(self.shell_port)
806 self.context, self.session, self._make_url(self.shell_port)
807 )
807 )
808 return self._shell_channel
808 return self._shell_channel
809
809
810 @property
810 @property
811 def iopub_channel(self):
811 def iopub_channel(self):
812 """Get the iopub channel object for this kernel."""
812 """Get the iopub channel object for this kernel."""
813 if self._iopub_channel is None:
813 if self._iopub_channel is None:
814 self._iopub_channel = self.iopub_channel_class(
814 self._iopub_channel = self.iopub_channel_class(
815 self.context, self.session, self._make_url(self.iopub_port)
815 self.context, self.session, self._make_url(self.iopub_port)
816 )
816 )
817 return self._iopub_channel
817 return self._iopub_channel
818
818
819 @property
819 @property
820 def stdin_channel(self):
820 def stdin_channel(self):
821 """Get the stdin channel object for this kernel."""
821 """Get the stdin channel object for this kernel."""
822 if self._stdin_channel is None:
822 if self._stdin_channel is None:
823 self._stdin_channel = self.stdin_channel_class(
823 self._stdin_channel = self.stdin_channel_class(
824 self.context, self.session, self._make_url(self.stdin_port)
824 self.context, self.session, self._make_url(self.stdin_port)
825 )
825 )
826 return self._stdin_channel
826 return self._stdin_channel
827
827
828 @property
828 @property
829 def hb_channel(self):
829 def hb_channel(self):
830 """Get the hb channel object for this kernel."""
830 """Get the hb channel object for this kernel."""
831 if self._hb_channel is None:
831 if self._hb_channel is None:
832 self._hb_channel = self.hb_channel_class(
832 self._hb_channel = self.hb_channel_class(
833 self.context, self.session, self._make_url(self.hb_port)
833 self.context, self.session, self._make_url(self.hb_port)
834 )
834 )
835 return self._hb_channel
835 return self._hb_channel
836
836
837 #--------------------------------------------------------------------------
837 #--------------------------------------------------------------------------
838 # Connection and ipc file management
838 # Connection and ipc file management
839 #--------------------------------------------------------------------------
839 #--------------------------------------------------------------------------
840
840
841 def cleanup_connection_file(self):
841 def cleanup_connection_file(self):
842 """Cleanup connection file *if we wrote it*
842 """Cleanup connection file *if we wrote it*
843
843
844 Will not raise if the connection file was already removed somehow.
844 Will not raise if the connection file was already removed somehow.
845 """
845 """
846 if self._connection_file_written:
846 if self._connection_file_written:
847 # cleanup connection files on full shutdown of kernel we started
847 # cleanup connection files on full shutdown of kernel we started
848 self._connection_file_written = False
848 self._connection_file_written = False
849 try:
849 try:
850 os.remove(self.connection_file)
850 os.remove(self.connection_file)
851 except (IOError, OSError):
851 except (IOError, OSError):
852 pass
852 pass
853
853
854 def cleanup_ipc_files(self):
854 def cleanup_ipc_files(self):
855 """Cleanup ipc files if we wrote them."""
855 """Cleanup ipc files if we wrote them."""
856 if self.transport != 'ipc':
856 if self.transport != 'ipc':
857 return
857 return
858 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
858 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port):
859 ipcfile = "%s-%i" % (self.ip, port)
859 ipcfile = "%s-%i" % (self.ip, port)
860 try:
860 try:
861 os.remove(ipcfile)
861 os.remove(ipcfile)
862 except (IOError, OSError):
862 except (IOError, OSError):
863 pass
863 pass
864
864
865 def load_connection_file(self):
865 def load_connection_file(self):
866 """Load connection info from JSON dict in self.connection_file."""
866 """Load connection info from JSON dict in self.connection_file."""
867 with open(self.connection_file) as f:
867 with open(self.connection_file) as f:
868 cfg = json.loads(f.read())
868 cfg = json.loads(f.read())
869
869
870 from pprint import pprint
870 from pprint import pprint
871 pprint(cfg)
871 pprint(cfg)
872 self.transport = cfg.get('transport', 'tcp')
872 self.transport = cfg.get('transport', 'tcp')
873 self.ip = cfg['ip']
873 self.ip = cfg['ip']
874 self.shell_port = cfg['shell_port']
874 self.shell_port = cfg['shell_port']
875 self.stdin_port = cfg['stdin_port']
875 self.stdin_port = cfg['stdin_port']
876 self.iopub_port = cfg['iopub_port']
876 self.iopub_port = cfg['iopub_port']
877 self.hb_port = cfg['hb_port']
877 self.hb_port = cfg['hb_port']
878 self.session.key = str_to_bytes(cfg['key'])
878 self.session.key = str_to_bytes(cfg['key'])
879
879
880 def write_connection_file(self):
880 def write_connection_file(self):
881 """Write connection info to JSON dict in self.connection_file."""
881 """Write connection info to JSON dict in self.connection_file."""
882 if self._connection_file_written:
882 if self._connection_file_written:
883 return
883 return
884 self.connection_file,cfg = write_connection_file(self.connection_file,
884 self.connection_file,cfg = write_connection_file(self.connection_file,
885 transport=self.transport, ip=self.ip, key=self.session.key,
885 transport=self.transport, ip=self.ip, key=self.session.key,
886 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
886 stdin_port=self.stdin_port, iopub_port=self.iopub_port,
887 shell_port=self.shell_port, hb_port=self.hb_port)
887 shell_port=self.shell_port, hb_port=self.hb_port)
888 # write_connection_file also sets default ports:
888 # write_connection_file also sets default ports:
889 self.shell_port = cfg['shell_port']
889 self.shell_port = cfg['shell_port']
890 self.stdin_port = cfg['stdin_port']
890 self.stdin_port = cfg['stdin_port']
891 self.iopub_port = cfg['iopub_port']
891 self.iopub_port = cfg['iopub_port']
892 self.hb_port = cfg['hb_port']
892 self.hb_port = cfg['hb_port']
893
893
894 self._connection_file_written = True
894 self._connection_file_written = True
895
895
896 #--------------------------------------------------------------------------
896 #--------------------------------------------------------------------------
897 # Kernel management
897 # Kernel management
898 #--------------------------------------------------------------------------
898 #--------------------------------------------------------------------------
899
899
900 def format_kernel_cmd(self, **kw):
900 def format_kernel_cmd(self, **kw):
901 """format templated args (e.g. {connection_file})"""
901 """format templated args (e.g. {connection_file})"""
902 if self.kernel_cmd:
902 if self.kernel_cmd:
903 cmd = self.kernel_cmd
903 cmd = self.kernel_cmd
904 else:
904 else:
905 cmd = make_kernel_cmd(
905 cmd = make_ipkernel_cmd(
906 'from IPython.zmq.ipkernel import main; main()',
906 'from IPython.zmq.ipkernel import main; main()',
907 **kw
907 **kw
908 )
908 )
909 ns = dict(connection_file=self.connection_file)
909 ns = dict(connection_file=self.connection_file)
910 ns.update(self._launch_args)
910 ns.update(self._launch_args)
911 return [ c.format(**ns) for c in cmd ]
911 return [ c.format(**ns) for c in cmd ]
912
912
913 def _launch_kernel(self, kernel_cmd, **kw):
913 def _launch_kernel(self, kernel_cmd, **kw):
914 """actually launch the kernel
914 """actually launch the kernel
915
915
916 override in a subclass to launch kernel subprocesses differently
916 override in a subclass to launch kernel subprocesses differently
917 """
917 """
918 return launch_kernel(kernel_cmd, **kw)
918 return launch_kernel(kernel_cmd, **kw)
919
919
920 def start_kernel(self, **kw):
920 def start_kernel(self, **kw):
921 """Starts a kernel on this host in a separate process.
921 """Starts a kernel on this host in a separate process.
922
922
923 If random ports (port=0) are being used, this method must be called
923 If random ports (port=0) are being used, this method must be called
924 before the channels are created.
924 before the channels are created.
925
925
926 Parameters:
926 Parameters:
927 -----------
927 -----------
928 launcher : callable, optional (default None)
928 launcher : callable, optional (default None)
929 A custom function for launching the kernel process (generally a
929 A custom function for launching the kernel process (generally a
930 wrapper around ``entry_point.base_launch_kernel``). In most cases,
930 wrapper around ``entry_point.base_launch_kernel``). In most cases,
931 it should not be necessary to use this parameter.
931 it should not be necessary to use this parameter.
932
932
933 **kw : optional
933 **kw : optional
934 keyword arguments that are passed down into the launcher
934 keyword arguments that are passed down into the launcher
935 callable.
935 callable.
936 """
936 """
937 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
937 if self.transport == 'tcp' and self.ip not in LOCAL_IPS:
938 raise RuntimeError("Can only launch a kernel on a local interface. "
938 raise RuntimeError("Can only launch a kernel on a local interface. "
939 "Make sure that the '*_address' attributes are "
939 "Make sure that the '*_address' attributes are "
940 "configured properly. "
940 "configured properly. "
941 "Currently valid addresses are: %s"%LOCAL_IPS
941 "Currently valid addresses are: %s"%LOCAL_IPS
942 )
942 )
943
943
944 # write connection file / get default ports
944 # write connection file / get default ports
945 self.write_connection_file()
945 self.write_connection_file()
946
946
947 # save kwargs for use in restart
947 # save kwargs for use in restart
948 self._launch_args = kw.copy()
948 self._launch_args = kw.copy()
949 # build the Popen cmd
949 # build the Popen cmd
950 kernel_cmd = self.format_kernel_cmd(**kw)
950 kernel_cmd = self.format_kernel_cmd(**kw)
951 # launch the kernel subprocess
951 # launch the kernel subprocess
952 self.kernel = self._launch_kernel(kernel_cmd,
952 self.kernel = self._launch_kernel(kernel_cmd,
953 ipython_kernel=self.ipython_kernel,
953 ipython_kernel=self.ipython_kernel,
954 **kw)
954 **kw)
955
955
956 def shutdown_kernel(self, now=False, restart=False):
956 def shutdown_kernel(self, now=False, restart=False):
957 """Attempts to the stop the kernel process cleanly.
957 """Attempts to the stop the kernel process cleanly.
958
958
959 This attempts to shutdown the kernels cleanly by:
959 This attempts to shutdown the kernels cleanly by:
960
960
961 1. Sending it a shutdown message over the shell channel.
961 1. Sending it a shutdown message over the shell channel.
962 2. If that fails, the kernel is shutdown forcibly by sending it
962 2. If that fails, the kernel is shutdown forcibly by sending it
963 a signal.
963 a signal.
964
964
965 Parameters:
965 Parameters:
966 -----------
966 -----------
967 now : bool
967 now : bool
968 Should the kernel be forcible killed *now*. This skips the
968 Should the kernel be forcible killed *now*. This skips the
969 first, nice shutdown attempt.
969 first, nice shutdown attempt.
970 restart: bool
970 restart: bool
971 Will this kernel be restarted after it is shutdown. When this
971 Will this kernel be restarted after it is shutdown. When this
972 is True, connection files will not be cleaned up.
972 is True, connection files will not be cleaned up.
973 """
973 """
974 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
974 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
975 if sys.platform == 'win32':
975 if sys.platform == 'win32':
976 self._kill_kernel()
976 self._kill_kernel()
977 return
977 return
978
978
979 # Pause the heart beat channel if it exists.
979 # Pause the heart beat channel if it exists.
980 if self._hb_channel is not None:
980 if self._hb_channel is not None:
981 self._hb_channel.pause()
981 self._hb_channel.pause()
982
982
983 if now:
983 if now:
984 if self.has_kernel:
984 if self.has_kernel:
985 self._kill_kernel()
985 self._kill_kernel()
986 else:
986 else:
987 # Don't send any additional kernel kill messages immediately, to give
987 # Don't send any additional kernel kill messages immediately, to give
988 # the kernel a chance to properly execute shutdown actions. Wait for at
988 # the kernel a chance to properly execute shutdown actions. Wait for at
989 # most 1s, checking every 0.1s.
989 # most 1s, checking every 0.1s.
990 self.shell_channel.shutdown(restart=restart)
990 self.shell_channel.shutdown(restart=restart)
991 for i in range(10):
991 for i in range(10):
992 if self.is_alive:
992 if self.is_alive:
993 time.sleep(0.1)
993 time.sleep(0.1)
994 else:
994 else:
995 break
995 break
996 else:
996 else:
997 # OK, we've waited long enough.
997 # OK, we've waited long enough.
998 if self.has_kernel:
998 if self.has_kernel:
999 self._kill_kernel()
999 self._kill_kernel()
1000
1000
1001 if not restart:
1001 if not restart:
1002 self.cleanup_connection_file()
1002 self.cleanup_connection_file()
1003 self.cleanup_ipc_files()
1003 self.cleanup_ipc_files()
1004 else:
1004 else:
1005 self.cleanup_ipc_files()
1005 self.cleanup_ipc_files()
1006
1006
1007 def restart_kernel(self, now=False, **kw):
1007 def restart_kernel(self, now=False, **kw):
1008 """Restarts a kernel with the arguments that were used to launch it.
1008 """Restarts a kernel with the arguments that were used to launch it.
1009
1009
1010 If the old kernel was launched with random ports, the same ports will be
1010 If the old kernel was launched with random ports, the same ports will be
1011 used for the new kernel. The same connection file is used again.
1011 used for the new kernel. The same connection file is used again.
1012
1012
1013 Parameters
1013 Parameters
1014 ----------
1014 ----------
1015 now : bool, optional
1015 now : bool, optional
1016 If True, the kernel is forcefully restarted *immediately*, without
1016 If True, the kernel is forcefully restarted *immediately*, without
1017 having a chance to do any cleanup action. Otherwise the kernel is
1017 having a chance to do any cleanup action. Otherwise the kernel is
1018 given 1s to clean up before a forceful restart is issued.
1018 given 1s to clean up before a forceful restart is issued.
1019
1019
1020 In all cases the kernel is restarted, the only difference is whether
1020 In all cases the kernel is restarted, the only difference is whether
1021 it is given a chance to perform a clean shutdown or not.
1021 it is given a chance to perform a clean shutdown or not.
1022
1022
1023 **kw : optional
1023 **kw : optional
1024 Any options specified here will overwrite those used to launch the
1024 Any options specified here will overwrite those used to launch the
1025 kernel.
1025 kernel.
1026 """
1026 """
1027 if self._launch_args is None:
1027 if self._launch_args is None:
1028 raise RuntimeError("Cannot restart the kernel. "
1028 raise RuntimeError("Cannot restart the kernel. "
1029 "No previous call to 'start_kernel'.")
1029 "No previous call to 'start_kernel'.")
1030 else:
1030 else:
1031 # Stop currently running kernel.
1031 # Stop currently running kernel.
1032 self.shutdown_kernel(now=now, restart=True)
1032 self.shutdown_kernel(now=now, restart=True)
1033
1033
1034 # Start new kernel.
1034 # Start new kernel.
1035 self._launch_args.update(kw)
1035 self._launch_args.update(kw)
1036 self.start_kernel(**self._launch_args)
1036 self.start_kernel(**self._launch_args)
1037
1037
1038 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
1038 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
1039 # unless there is some delay here.
1039 # unless there is some delay here.
1040 if sys.platform == 'win32':
1040 if sys.platform == 'win32':
1041 time.sleep(0.2)
1041 time.sleep(0.2)
1042
1042
1043 @property
1043 @property
1044 def has_kernel(self):
1044 def has_kernel(self):
1045 """Has a kernel been started that we are managing."""
1045 """Has a kernel been started that we are managing."""
1046 return self.kernel is not None
1046 return self.kernel is not None
1047
1047
1048 def _kill_kernel(self):
1048 def _kill_kernel(self):
1049 """Kill the running kernel.
1049 """Kill the running kernel.
1050
1050
1051 This is a private method, callers should use shutdown_kernel(now=True).
1051 This is a private method, callers should use shutdown_kernel(now=True).
1052 """
1052 """
1053 if self.has_kernel:
1053 if self.has_kernel:
1054 # Pause the heart beat channel if it exists.
1054 # Pause the heart beat channel if it exists.
1055 if self._hb_channel is not None:
1055 if self._hb_channel is not None:
1056 self._hb_channel.pause()
1056 self._hb_channel.pause()
1057
1057
1058 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
1058 # Signal the kernel to terminate (sends SIGKILL on Unix and calls
1059 # TerminateProcess() on Win32).
1059 # TerminateProcess() on Win32).
1060 try:
1060 try:
1061 self.kernel.kill()
1061 self.kernel.kill()
1062 except OSError as e:
1062 except OSError as e:
1063 # In Windows, we will get an Access Denied error if the process
1063 # In Windows, we will get an Access Denied error if the process
1064 # has already terminated. Ignore it.
1064 # has already terminated. Ignore it.
1065 if sys.platform == 'win32':
1065 if sys.platform == 'win32':
1066 if e.winerror != 5:
1066 if e.winerror != 5:
1067 raise
1067 raise
1068 # On Unix, we may get an ESRCH error if the process has already
1068 # On Unix, we may get an ESRCH error if the process has already
1069 # terminated. Ignore it.
1069 # terminated. Ignore it.
1070 else:
1070 else:
1071 from errno import ESRCH
1071 from errno import ESRCH
1072 if e.errno != ESRCH:
1072 if e.errno != ESRCH:
1073 raise
1073 raise
1074
1074
1075 # Block until the kernel terminates.
1075 # Block until the kernel terminates.
1076 self.kernel.wait()
1076 self.kernel.wait()
1077 self.kernel = None
1077 self.kernel = None
1078 else:
1078 else:
1079 raise RuntimeError("Cannot kill kernel. No kernel is running!")
1079 raise RuntimeError("Cannot kill kernel. No kernel is running!")
1080
1080
1081 def interrupt_kernel(self):
1081 def interrupt_kernel(self):
1082 """Interrupts the kernel by sending it a signal.
1082 """Interrupts the kernel by sending it a signal.
1083
1083
1084 Unlike ``signal_kernel``, this operation is well supported on all
1084 Unlike ``signal_kernel``, this operation is well supported on all
1085 platforms.
1085 platforms.
1086 """
1086 """
1087 if self.has_kernel:
1087 if self.has_kernel:
1088 if sys.platform == 'win32':
1088 if sys.platform == 'win32':
1089 from parentpoller import ParentPollerWindows as Poller
1089 from parentpoller import ParentPollerWindows as Poller
1090 Poller.send_interrupt(self.kernel.win32_interrupt_event)
1090 Poller.send_interrupt(self.kernel.win32_interrupt_event)
1091 else:
1091 else:
1092 self.kernel.send_signal(signal.SIGINT)
1092 self.kernel.send_signal(signal.SIGINT)
1093 else:
1093 else:
1094 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
1094 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
1095
1095
1096 def signal_kernel(self, signum):
1096 def signal_kernel(self, signum):
1097 """Sends a signal to the kernel.
1097 """Sends a signal to the kernel.
1098
1098
1099 Note that since only SIGTERM is supported on Windows, this function is
1099 Note that since only SIGTERM is supported on Windows, this function is
1100 only useful on Unix systems.
1100 only useful on Unix systems.
1101 """
1101 """
1102 if self.has_kernel:
1102 if self.has_kernel:
1103 self.kernel.send_signal(signum)
1103 self.kernel.send_signal(signum)
1104 else:
1104 else:
1105 raise RuntimeError("Cannot signal kernel. No kernel is running!")
1105 raise RuntimeError("Cannot signal kernel. No kernel is running!")
1106
1106
1107 @property
1107 @property
1108 def is_alive(self):
1108 def is_alive(self):
1109 """Is the kernel process still running?"""
1109 """Is the kernel process still running?"""
1110 if self.has_kernel:
1110 if self.has_kernel:
1111 if self.kernel.poll() is None:
1111 if self.kernel.poll() is None:
1112 return True
1112 return True
1113 else:
1113 else:
1114 return False
1114 return False
1115 elif self._hb_channel is not None:
1115 elif self._hb_channel is not None:
1116 # We didn't start the kernel with this KernelManager so we
1116 # We didn't start the kernel with this KernelManager so we
1117 # use the heartbeat.
1117 # use the heartbeat.
1118 return self._hb_channel.is_beating()
1118 return self._hb_channel.is_beating()
1119 else:
1119 else:
1120 # no heartbeat and not local, we can't tell if it's running,
1120 # no heartbeat and not local, we can't tell if it's running,
1121 # so naively return True
1121 # so naively return True
1122 return True
1122 return True
1123
1123
1124
1124
1125 #-----------------------------------------------------------------------------
1125 #-----------------------------------------------------------------------------
1126 # ABC Registration
1126 # ABC Registration
1127 #-----------------------------------------------------------------------------
1127 #-----------------------------------------------------------------------------
1128
1128
1129 ShellChannelABC.register(ShellChannel)
1129 ShellChannelABC.register(ShellChannel)
1130 IOPubChannelABC.register(IOPubChannel)
1130 IOPubChannelABC.register(IOPubChannel)
1131 HBChannelABC.register(HBChannel)
1131 HBChannelABC.register(HBChannel)
1132 StdInChannelABC.register(StdInChannel)
1132 StdInChannelABC.register(StdInChannel)
1133 KernelManagerABC.register(KernelManager)
1133 KernelManagerABC.register(KernelManager)
1134
1134
General Comments 0
You need to be logged in to leave comments. Login now