Show More
@@ -16,6 +16,7 b' Authors:' | |||||
16 | # Imports |
|
16 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 |
|
18 | |||
|
19 | import os | |||
19 | import signal |
|
20 | import signal | |
20 | import sys |
|
21 | import sys | |
21 | import uuid |
|
22 | import uuid | |
@@ -27,6 +28,7 b' from tornado import web' | |||||
27 |
|
28 | |||
28 | from IPython.config.configurable import LoggingConfigurable |
|
29 | from IPython.config.configurable import LoggingConfigurable | |
29 | from IPython.zmq.ipkernel import launch_kernel |
|
30 | from IPython.zmq.ipkernel import launch_kernel | |
|
31 | from IPython.zmq.kernelmanager import KernelManager | |||
30 | from IPython.utils.traitlets import Instance, Dict, List, Unicode, Float, Int |
|
32 | from IPython.utils.traitlets import Instance, Dict, List, Unicode, Float, Int | |
31 |
|
33 | |||
32 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
@@ -37,12 +39,14 b' class DuplicateKernelError(Exception):' | |||||
37 | pass |
|
39 | pass | |
38 |
|
40 | |||
39 |
|
41 | |||
40 | class KernelManager(LoggingConfigurable): |
|
42 | class MultiKernelManager(LoggingConfigurable): | |
41 | """A class for managing multiple kernels.""" |
|
43 | """A class for managing multiple kernels.""" | |
42 |
|
44 | |||
43 | context = Instance('zmq.Context') |
|
45 | context = Instance('zmq.Context') | |
44 | def _context_default(self): |
|
46 | def _context_default(self): | |
45 | return zmq.Context.instance() |
|
47 | return zmq.Context.instance() | |
|
48 | ||||
|
49 | connection_dir = Unicode('') | |||
46 |
|
50 | |||
47 | _kernels = Dict() |
|
51 | _kernels = Dict() | |
48 |
|
52 | |||
@@ -64,18 +68,12 b' class KernelManager(LoggingConfigurable):' | |||||
64 | def start_kernel(self, **kwargs): |
|
68 | def start_kernel(self, **kwargs): | |
65 | """Start a new kernel.""" |
|
69 | """Start a new kernel.""" | |
66 | kernel_id = unicode(uuid.uuid4()) |
|
70 | kernel_id = unicode(uuid.uuid4()) | |
67 | (process, shell_port, iopub_port, stdin_port, hb_port) = launch_kernel(**kwargs) |
|
71 | # use base KernelManager for each Kernel | |
68 | # Store the information for contacting the kernel. This assumes the kernel is |
|
72 | km = KernelManager(connection_file=os.path.join( | |
69 | # running on localhost. |
|
73 | self.connection_dir, "kernel-%s.json" % kernel_id) | |
70 | d = dict( |
|
|||
71 | process = process, |
|
|||
72 | stdin_port = stdin_port, |
|
|||
73 | iopub_port = iopub_port, |
|
|||
74 | shell_port = shell_port, |
|
|||
75 | hb_port = hb_port, |
|
|||
76 | ip = '127.0.0.1' |
|
|||
77 | ) |
|
74 | ) | |
78 | self._kernels[kernel_id] = d |
|
75 | km.start_kernel(**kwargs) | |
|
76 | self._kernels[kernel_id] = km | |||
79 | return kernel_id |
|
77 | return kernel_id | |
80 |
|
78 | |||
81 | def kill_kernel(self, kernel_id): |
|
79 | def kill_kernel(self, kernel_id): | |
@@ -86,17 +84,8 b' class KernelManager(LoggingConfigurable):' | |||||
86 | kernel_id : uuid |
|
84 | kernel_id : uuid | |
87 | The id of the kernel to kill. |
|
85 | The id of the kernel to kill. | |
88 | """ |
|
86 | """ | |
89 |
|
|
87 | self.get_kernel(kernel_id).kill_kernel() | |
90 | if kernel_process is not None: |
|
88 | del self._kernels[kernel_id] | |
91 | # Attempt to kill the kernel. |
|
|||
92 | try: |
|
|||
93 | kernel_process.kill() |
|
|||
94 | except OSError, e: |
|
|||
95 | # In Windows, we will get an Access Denied error if the process |
|
|||
96 | # has already terminated. Ignore it. |
|
|||
97 | if not (sys.platform == 'win32' and e.winerror == 5): |
|
|||
98 | raise |
|
|||
99 | del self._kernels[kernel_id] |
|
|||
100 |
|
89 | |||
101 | def interrupt_kernel(self, kernel_id): |
|
90 | def interrupt_kernel(self, kernel_id): | |
102 | """Interrupt (SIGINT) the kernel by its uuid. |
|
91 | """Interrupt (SIGINT) the kernel by its uuid. | |
@@ -106,14 +95,7 b' class KernelManager(LoggingConfigurable):' | |||||
106 | kernel_id : uuid |
|
95 | kernel_id : uuid | |
107 | The id of the kernel to interrupt. |
|
96 | The id of the kernel to interrupt. | |
108 | """ |
|
97 | """ | |
109 |
|
|
98 | return self.get_kernel(kernel_id).interrupt_kernel() | |
110 | if kernel_process is not None: |
|
|||
111 | if sys.platform == 'win32': |
|
|||
112 | from parentpoller import ParentPollerWindows as Poller |
|
|||
113 | Poller.send_interrupt(kernel_process.win32_interrupt_event) |
|
|||
114 | else: |
|
|||
115 | kernel_process.send_signal(signal.SIGINT) |
|
|||
116 |
|
||||
117 |
|
99 | |||
118 | def signal_kernel(self, kernel_id, signum): |
|
100 | def signal_kernel(self, kernel_id, signum): | |
119 | """ Sends a signal to the kernel by its uuid. |
|
101 | """ Sends a signal to the kernel by its uuid. | |
@@ -126,21 +108,19 b' class KernelManager(LoggingConfigurable):' | |||||
126 | kernel_id : uuid |
|
108 | kernel_id : uuid | |
127 | The id of the kernel to signal. |
|
109 | The id of the kernel to signal. | |
128 | """ |
|
110 | """ | |
129 |
|
|
111 | return self.get_kernel(kernel_id).signal_kernel(signum) | |
130 | if kernel_process is not None: |
|
|||
131 | kernel_process.send_signal(signum) |
|
|||
132 |
|
112 | |||
133 |
def get_kernel |
|
113 | def get_kernel(self, kernel_id): | |
134 |
"""Get the |
|
114 | """Get the single KernelManager object for a kernel by its uuid. | |
135 |
|
115 | |||
136 | Parameters |
|
116 | Parameters | |
137 | ========== |
|
117 | ========== | |
138 | kernel_id : uuid |
|
118 | kernel_id : uuid | |
139 | The id of the kernel. |
|
119 | The id of the kernel. | |
140 | """ |
|
120 | """ | |
141 |
|
|
121 | km = self._kernels.get(kernel_id) | |
142 |
if |
|
122 | if km is not None: | |
143 |
return |
|
123 | return km | |
144 | else: |
|
124 | else: | |
145 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
125 | raise KeyError("Kernel with id not found: %s" % kernel_id) | |
146 |
|
126 | |||
@@ -159,14 +139,13 b' class KernelManager(LoggingConfigurable):' | |||||
159 | (stdin_port,iopub_port,shell_port) and the values are the |
|
139 | (stdin_port,iopub_port,shell_port) and the values are the | |
160 | integer port numbers for those channels. |
|
140 | integer port numbers for those channels. | |
161 | """ |
|
141 | """ | |
162 | d = self._kernels.get(kernel_id) |
|
142 | # this will raise a KeyError if not found: | |
163 | if d is not None: |
|
143 | km = self.get_kernel(kernel_id) | |
164 | dcopy = d.copy() |
|
144 | return dict(shell_port=km.shell_port, | |
165 | dcopy.pop('process') |
|
145 | iopub_port=km.iopub_port, | |
166 | dcopy.pop('ip') |
|
146 | stdin_port=km.stdin_port, | |
167 | return dcopy |
|
147 | hb_port=km.hb_port, | |
168 |
|
|
148 | ) | |
169 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
|||
170 |
|
149 | |||
171 | def get_kernel_ip(self, kernel_id): |
|
150 | def get_kernel_ip(self, kernel_id): | |
172 | """Return ip address for a kernel. |
|
151 | """Return ip address for a kernel. | |
@@ -181,11 +160,7 b' class KernelManager(LoggingConfigurable):' | |||||
181 | ip : str |
|
160 | ip : str | |
182 | The ip address of the kernel. |
|
161 | The ip address of the kernel. | |
183 | """ |
|
162 | """ | |
184 |
|
|
163 | return self.get_kernel(kernel_id).ip | |
185 | if d is not None: |
|
|||
186 | return d['ip'] |
|
|||
187 | else: |
|
|||
188 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
|||
189 |
|
164 | |||
190 | def create_connected_stream(self, ip, port, socket_type): |
|
165 | def create_connected_stream(self, ip, port, socket_type): | |
191 | sock = self.context.socket(socket_type) |
|
166 | sock = self.context.socket(socket_type) | |
@@ -214,7 +189,7 b' class KernelManager(LoggingConfigurable):' | |||||
214 | return hb_stream |
|
189 | return hb_stream | |
215 |
|
190 | |||
216 |
|
191 | |||
217 | class MappingKernelManager(KernelManager): |
|
192 | class MappingKernelManager(MultiKernelManager): | |
218 | """A KernelManager that handles notebok mapping and HTTP error handling""" |
|
193 | """A KernelManager that handles notebok mapping and HTTP error handling""" | |
219 |
|
194 | |||
220 | kernel_argv = List(Unicode) |
|
195 | kernel_argv = List(Unicode) | |
@@ -292,6 +267,13 b' class MappingKernelManager(KernelManager):' | |||||
292 | def restart_kernel(self, kernel_id): |
|
267 | def restart_kernel(self, kernel_id): | |
293 | """Restart a kernel while keeping clients connected.""" |
|
268 | """Restart a kernel while keeping clients connected.""" | |
294 | self._check_kernel_id(kernel_id) |
|
269 | self._check_kernel_id(kernel_id) | |
|
270 | km = self.get_kernel(kernel_id) | |||
|
271 | km.restart_kernel(now=True) | |||
|
272 | self.log.info("Kernel restarted: %s" % kernel_id) | |||
|
273 | return kernel_id | |||
|
274 | ||||
|
275 | # the following remains, in case the KM restart machinery is | |||
|
276 | # somehow unacceptable | |||
295 | # Get the notebook_id to preserve the kernel/notebook association. |
|
277 | # Get the notebook_id to preserve the kernel/notebook association. | |
296 | notebook_id = self.notebook_for_kernel(kernel_id) |
|
278 | notebook_id = self.notebook_for_kernel(kernel_id) | |
297 | # Create the new kernel first so we can move the clients over. |
|
279 | # Create the new kernel first so we can move the clients over. |
@@ -233,7 +233,8 b' class IPythonNotebookApp(BaseIPythonApplication):' | |||||
233 |
|
233 | |||
234 | # Create a KernelManager and start a kernel. |
|
234 | # Create a KernelManager and start a kernel. | |
235 | self.kernel_manager = MappingKernelManager( |
|
235 | self.kernel_manager = MappingKernelManager( | |
236 | config=self.config, log=self.log, kernel_argv=self.kernel_argv |
|
236 | config=self.config, log=self.log, kernel_argv=self.kernel_argv, | |
|
237 | connection_dir = self.profile_dir.security_dir, | |||
237 | ) |
|
238 | ) | |
238 | self.notebook_manager = NotebookManager(config=self.config, log=self.log) |
|
239 | self.notebook_manager = NotebookManager(config=self.config, log=self.log) | |
239 | self.notebook_manager.list_notebooks() |
|
240 | self.notebook_manager.list_notebooks() |
@@ -2,12 +2,12 b'' | |||||
2 |
|
2 | |||
3 | from unittest import TestCase |
|
3 | from unittest import TestCase | |
4 |
|
4 | |||
5 | from IPython.frontend.html.notebook.kernelmanager import KernelManager |
|
5 | from IPython.frontend.html.notebook.kernelmanager import MultiKernelManager | |
6 |
|
6 | |||
7 | class TestKernelManager(TestCase): |
|
7 | class TestKernelManager(TestCase): | |
8 |
|
8 | |||
9 | def test_km_lifecycle(self): |
|
9 | def test_km_lifecycle(self): | |
10 | km = KernelManager() |
|
10 | km = MultiKernelManager() | |
11 | kid = km.start_kernel() |
|
11 | kid = km.start_kernel() | |
12 | self.assert_(kid in km) |
|
12 | self.assert_(kid in km) | |
13 | self.assertEquals(len(km),1) |
|
13 | self.assertEquals(len(km),1) | |
@@ -21,6 +21,6 b' class TestKernelManager(TestCase):' | |||||
21 | self.assert_('iopub_port' in port_dict) |
|
21 | self.assert_('iopub_port' in port_dict) | |
22 | self.assert_('shell_port' in port_dict) |
|
22 | self.assert_('shell_port' in port_dict) | |
23 | self.assert_('hb_port' in port_dict) |
|
23 | self.assert_('hb_port' in port_dict) | |
24 |
km.get_kernel |
|
24 | km.get_kernel(kid) | |
25 |
|
25 | |||
26 |
|
26 |
General Comments 0
You need to be logged in to leave comments.
Login now