Show More
@@ -0,0 +1,75 b'' | |||||
|
1 | """A manager for session and channels for a single kernel.""" | |||
|
2 | ||||
|
3 | import zmq | |||
|
4 | from zmq.eventloop.zmqstream import ZMQStream | |||
|
5 | ||||
|
6 | from IPython.utils.traitlets import Instance, Dict, CBytes, Bool | |||
|
7 | from IPython.zmq.session import SessionFactory | |||
|
8 | ||||
|
9 | ||||
|
10 | class SessionManagerRunningError(Exception): | |||
|
11 | pass | |||
|
12 | ||||
|
13 | ||||
|
14 | class SessionManager(SessionFactory): | |||
|
15 | """Manages a session for a kernel. | |||
|
16 | ||||
|
17 | The object manages a variety of things for a connection session to | |||
|
18 | a running kernel: | |||
|
19 | ||||
|
20 | * The set of channels or connected ZMQ streams to the kernel. | |||
|
21 | * An IPython.zmq.session.Session object that manages send/recv logic | |||
|
22 | for those channels. | |||
|
23 | """ | |||
|
24 | ||||
|
25 | kernel_manager = Instance('IPython.frontend.html.notebook.kernelmanager.KernelManager') | |||
|
26 | kernel_id = CBytes(b'') | |||
|
27 | _session_streams = Dict() | |||
|
28 | _running = Bool(False) | |||
|
29 | ||||
|
30 | def __init__(self, **kwargs): | |||
|
31 | kernel_id = kwargs.pop('kernel_id') | |||
|
32 | super(SessionManager, self).__init__(**kwargs) | |||
|
33 | self.kernel_id = kernel_id | |||
|
34 | self.start() | |||
|
35 | ||||
|
36 | def __del__(self): | |||
|
37 | self.stop() | |||
|
38 | ||||
|
39 | def start(self): | |||
|
40 | if not self._running: | |||
|
41 | ports = self.kernel_manager.get_kernel_ports(self.kernel_id) | |||
|
42 | iopub_stream = self.create_connected_stream(ports['iopub_port'], zmq.SUB) | |||
|
43 | iopub_stream.socket.setsockopt(zmq.SUBSCRIBE, b'') | |||
|
44 | shell_stream = self.create_connected_stream(ports['shell_port'], zmq.XREQ) | |||
|
45 | self._session_streams = dict( | |||
|
46 | iopub_stream = iopub_stream, | |||
|
47 | shell_stream = shell_stream | |||
|
48 | ) | |||
|
49 | self._running = True | |||
|
50 | else: | |||
|
51 | raise SessionManagerRunningError( | |||
|
52 | 'Session manager is already running, call stop() before start()' | |||
|
53 | ) | |||
|
54 | ||||
|
55 | def stop(self): | |||
|
56 | if self._running: | |||
|
57 | for name, stream in self._session_streams.items(): | |||
|
58 | stream.close() | |||
|
59 | self._session_streams = {} | |||
|
60 | self._running = False | |||
|
61 | ||||
|
62 | def create_connected_stream(self, port, socket_type): | |||
|
63 | sock = self.context.socket(socket_type) | |||
|
64 | addr = "tcp://%s:%i" % (self.kernel_manager.get_kernel_ip(self.kernel_id), port) | |||
|
65 | self.log.info("Connecting to: %s, %r" % (addr, socket_type)) | |||
|
66 | sock.connect(addr) | |||
|
67 | return ZMQStream(sock) | |||
|
68 | ||||
|
69 | def get_iopub_stream(self): | |||
|
70 | return self._session_streams['iopub_stream'] | |||
|
71 | ||||
|
72 | def get_shell_stream(self): | |||
|
73 | return self._session_streams['shell_stream'] | |||
|
74 | ||||
|
75 |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 |
1 | NO CONTENT: new file 100644, binary diff hidden |
|
NO CONTENT: new file 100644, binary diff hidden |
@@ -0,0 +1,38 b'' | |||||
|
1 | ||||
|
2 | from unittest import TestCase | |||
|
3 | ||||
|
4 | from IPython.frontend.html.notebook.kernelmanager import KernelManager | |||
|
5 | from IPython.frontend.html.notebook.sessionmanager import SessionManagerRunningError | |||
|
6 | ||||
|
7 | class TestKernelManager(TestCase): | |||
|
8 | ||||
|
9 | def test_km_lifecycle(self): | |||
|
10 | km = KernelManager() | |||
|
11 | kid = km.start_kernel() | |||
|
12 | self.assert_(kid in km) | |||
|
13 | self.assertEquals(len(km),1) | |||
|
14 | km.kill_kernel(kid) | |||
|
15 | self.assert_(not kid in km) | |||
|
16 | ||||
|
17 | kid = km.start_kernel() | |||
|
18 | self.assertEquals('127.0.0.1',km.get_kernel_ip(kid)) | |||
|
19 | port_dict = km.get_kernel_ports(kid) | |||
|
20 | self.assert_('stdin_port' in port_dict) | |||
|
21 | self.assert_('iopub_port' in port_dict) | |||
|
22 | self.assert_('shell_port' in port_dict) | |||
|
23 | self.assert_('hb_port' in port_dict) | |||
|
24 | km.get_kernel_process(kid) | |||
|
25 | ||||
|
26 | def test_session_manager(self): | |||
|
27 | km = KernelManager() | |||
|
28 | kid = km.start_kernel() | |||
|
29 | sm = km.create_session_manager(kid) | |||
|
30 | self.assert_(sm._running) | |||
|
31 | sm.stop() | |||
|
32 | self.assert_(not sm._running) | |||
|
33 | sm.start() | |||
|
34 | self.assertRaises(SessionManagerRunningError, sm.start) | |||
|
35 | sm.get_iopub_stream() | |||
|
36 | sm.get_shell_stream() | |||
|
37 | sm.session | |||
|
38 |
@@ -1,28 +1,41 b'' | |||||
|
1 | """A kernel manager for multiple kernels.""" | |||
|
2 | ||||
|
3 | import logging | |||
1 | import signal |
|
4 | import signal | |
2 | import sys |
|
5 | import sys | |
3 | import uuid |
|
6 | import uuid | |
4 |
|
7 | |||
|
8 | import zmq | |||
|
9 | ||||
|
10 | from IPython.config.configurable import Configurable | |||
5 | from IPython.zmq.ipkernel import launch_kernel |
|
11 | from IPython.zmq.ipkernel import launch_kernel | |
6 | from session import SessionManager |
|
12 | from IPython.utils.traitlets import Instance, Dict, Unicode | |
7 |
|
13 | |||
8 |
|
14 | |||
9 | class DuplicateKernelError(Exception): |
|
15 | class DuplicateKernelError(Exception): | |
10 | pass |
|
16 | pass | |
11 |
|
17 | |||
12 |
|
18 | |||
13 |
class KernelManager( |
|
19 | class KernelManager(Configurable): | |
|
20 | """A class for managing multiple kernels.""" | |||
|
21 | ||||
|
22 | context = Instance('zmq.Context') | |||
|
23 | def _context_default(self): | |||
|
24 | return zmq.Context.instance() | |||
14 |
|
25 | |||
15 | ip = '127.0.0.1' |
|
26 | logname = Unicode('') | |
|
27 | def _logname_changed(self, name, old, new): | |||
|
28 | self.log = logging.getLogger(new) | |||
16 |
|
29 | |||
17 | def __init__(self, context): |
|
30 | _kernels = Dict() | |
18 | self.context = context |
|
|||
19 | self._kernels = {} |
|
|||
20 |
|
31 | |||
21 | @property |
|
32 | @property | |
22 | def kernel_ids(self): |
|
33 | def kernel_ids(self): | |
|
34 | """Return a list of the kernel ids of the active kernels.""" | |||
23 | return self._kernels.keys() |
|
35 | return self._kernels.keys() | |
24 |
|
36 | |||
25 | def __len__(self): |
|
37 | def __len__(self): | |
|
38 | """Return the number of running kernels.""" | |||
26 | return len(self.kernel_ids) |
|
39 | return len(self.kernel_ids) | |
27 |
|
40 | |||
28 | def __contains__(self, kernel_id): |
|
41 | def __contains__(self, kernel_id): | |
@@ -31,21 +44,31 b' class KernelManager(object):' | |||||
31 | else: |
|
44 | else: | |
32 | return False |
|
45 | return False | |
33 |
|
46 | |||
34 | def start_kernel(self): |
|
47 | def start_kernel(self, **kwargs): | |
|
48 | """Start a new kernel.""" | |||
35 | kernel_id = str(uuid.uuid4()) |
|
49 | kernel_id = str(uuid.uuid4()) | |
36 |
(process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel( |
|
50 | (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs) | |
|
51 | # Store the information for contacting the kernel. This assumes the kernel is | |||
|
52 | # running on localhost. | |||
37 | d = dict( |
|
53 | d = dict( | |
38 | process = process, |
|
54 | process = process, | |
39 | stdin_port = stdin_port, |
|
55 | stdin_port = stdin_port, | |
40 | iopub_port = iopub_port, |
|
56 | iopub_port = iopub_port, | |
41 | shell_port = shell_port, |
|
57 | shell_port = shell_port, | |
42 | hb_port = hb_port, |
|
58 | hb_port = hb_port, | |
43 | session_manager = SessionManager(self, kernel_id, self.context) |
|
59 | ip = '127.0.0.1' | |
44 | ) |
|
60 | ) | |
45 | self._kernels[kernel_id] = d |
|
61 | self._kernels[kernel_id] = d | |
46 | return kernel_id |
|
62 | return kernel_id | |
47 |
|
63 | |||
48 | def kill_kernel(self, kernel_id): |
|
64 | def kill_kernel(self, kernel_id): | |
|
65 | """Kill a kernel by its kernel uuid. | |||
|
66 | ||||
|
67 | Parameters | |||
|
68 | ========== | |||
|
69 | kernel_id : uuid | |||
|
70 | The id of the kernel to kill. | |||
|
71 | """ | |||
49 | kernel_process = self.get_kernel_process(kernel_id) |
|
72 | kernel_process = self.get_kernel_process(kernel_id) | |
50 | if kernel_process is not None: |
|
73 | if kernel_process is not None: | |
51 | # Attempt to kill the kernel. |
|
74 | # Attempt to kill the kernel. | |
@@ -59,6 +82,13 b' class KernelManager(object):' | |||||
59 | del self._kernels[kernel_id] |
|
82 | del self._kernels[kernel_id] | |
60 |
|
83 | |||
61 | def interrupt_kernel(self, kernel_id): |
|
84 | def interrupt_kernel(self, kernel_id): | |
|
85 | """Interrupt (SIGINT) the kernel by its uuid. | |||
|
86 | ||||
|
87 | Parameters | |||
|
88 | ========== | |||
|
89 | kernel_id : uuid | |||
|
90 | The id of the kernel to interrupt. | |||
|
91 | """ | |||
62 | kernel_process = self.get_kernel_process(kernel_id) |
|
92 | kernel_process = self.get_kernel_process(kernel_id) | |
63 | if kernel_process is not None: |
|
93 | if kernel_process is not None: | |
64 | if sys.platform == 'win32': |
|
94 | if sys.platform == 'win32': | |
@@ -68,14 +98,28 b' class KernelManager(object):' | |||||
68 | kernel_process.send_signal(signal.SIGINT) |
|
98 | kernel_process.send_signal(signal.SIGINT) | |
69 |
|
99 | |||
70 | def signal_kernel(self, kernel_id, signum): |
|
100 | def signal_kernel(self, kernel_id, signum): | |
71 |
""" Sends a signal to the kernel |
|
101 | """ Sends a signal to the kernel by its uuid. | |
72 | supported on Windows, this function is only useful on Unix systems. |
|
102 | ||
|
103 | Note that since only SIGTERM is supported on Windows, this function | |||
|
104 | is only useful on Unix systems. | |||
|
105 | ||||
|
106 | Parameters | |||
|
107 | ========== | |||
|
108 | kernel_id : uuid | |||
|
109 | The id of the kernel to signal. | |||
73 | """ |
|
110 | """ | |
74 | kernel_process = self.get_kernel_process(kernel_id) |
|
111 | kernel_process = self.get_kernel_process(kernel_id) | |
75 | if kernel_process is not None: |
|
112 | if kernel_process is not None: | |
76 | kernel_process.send_signal(signum) |
|
113 | kernel_process.send_signal(signum) | |
77 |
|
114 | |||
78 | def get_kernel_process(self, kernel_id): |
|
115 | def get_kernel_process(self, kernel_id): | |
|
116 | """Get the process object for a kernel by its uuid. | |||
|
117 | ||||
|
118 | Parameters | |||
|
119 | ========== | |||
|
120 | kernel_id : uuid | |||
|
121 | The id of the kernel. | |||
|
122 | """ | |||
79 | d = self._kernels.get(kernel_id) |
|
123 | d = self._kernels.get(kernel_id) | |
80 | if d is not None: |
|
124 | if d is not None: | |
81 | return d['process'] |
|
125 | return d['process'] | |
@@ -83,20 +127,53 b' class KernelManager(object):' | |||||
83 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
127 | raise KeyError("Kernel with id not found: %s" % kernel_id) | |
84 |
|
128 | |||
85 | def get_kernel_ports(self, kernel_id): |
|
129 | def get_kernel_ports(self, kernel_id): | |
|
130 | """Return a dictionary of ports for a kernel. | |||
|
131 | ||||
|
132 | Parameters | |||
|
133 | ========== | |||
|
134 | kernel_id : uuid | |||
|
135 | The id of the kernel. | |||
|
136 | ||||
|
137 | Returns | |||
|
138 | ======= | |||
|
139 | port_dict : dict | |||
|
140 | A dict of key, value pairs where the keys are the names | |||
|
141 | (stdin_port,iopub_port,shell_port) and the values are the | |||
|
142 | integer port numbers for those channels. | |||
|
143 | """ | |||
86 | d = self._kernels.get(kernel_id) |
|
144 | d = self._kernels.get(kernel_id) | |
87 | if d is not None: |
|
145 | if d is not None: | |
88 | dcopy = d.copy() |
|
146 | dcopy = d.copy() | |
89 | dcopy.pop('process') |
|
147 | dcopy.pop('process') | |
|
148 | dcopy.pop('ip') | |||
90 | return dcopy |
|
149 | return dcopy | |
91 | else: |
|
150 | else: | |
92 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
151 | raise KeyError("Kernel with id not found: %s" % kernel_id) | |
93 |
|
152 | |||
94 |
def get_ |
|
153 | def get_kernel_ip(self, kernel_id): | |
|
154 | """Return ip address for a kernel. | |||
|
155 | ||||
|
156 | Parameters | |||
|
157 | ========== | |||
|
158 | kernel_id : uuid | |||
|
159 | The id of the kernel. | |||
|
160 | ||||
|
161 | Returns | |||
|
162 | ======= | |||
|
163 | ip : str | |||
|
164 | The ip address of the kernel. | |||
|
165 | """ | |||
95 | d = self._kernels.get(kernel_id) |
|
166 | d = self._kernels.get(kernel_id) | |
96 | if d is not None: |
|
167 | if d is not None: | |
97 |
return d[' |
|
168 | return d['ip'] | |
98 | else: |
|
169 | else: | |
99 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
170 | raise KeyError("Kernel with id not found: %s" % kernel_id) | |
100 |
|
171 | |||
101 |
|
172 | def create_session_manager(self, kernel_id): | ||
|
173 | """Create a new session manager for a kernel by its uuid.""" | |||
|
174 | from sessionmanager import SessionManager | |||
|
175 | return SessionManager( | |||
|
176 | kernel_id=kernel_id, kernel_manager=self, | |||
|
177 | config=self.config, context=self.context, logname=self.logname | |||
|
178 | ) | |||
102 |
|
179 |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now