Show More
@@ -0,0 +1,167 b'' | |||||
|
1 | """utilities for testing IPython kernels""" | |||
|
2 | ||||
|
3 | #------------------------------------------------------------------------------- | |||
|
4 | # Copyright (C) 2013 The IPython Development Team | |||
|
5 | # | |||
|
6 | # Distributed under the terms of the BSD License. The full license is in | |||
|
7 | # the file COPYING, distributed as part of this software. | |||
|
8 | #------------------------------------------------------------------------------- | |||
|
9 | ||||
|
10 | #------------------------------------------------------------------------------- | |||
|
11 | # Imports | |||
|
12 | #------------------------------------------------------------------------------- | |||
|
13 | ||||
|
14 | import atexit | |||
|
15 | ||||
|
16 | from contextlib import contextmanager | |||
|
17 | from subprocess import PIPE | |||
|
18 | from Queue import Empty | |||
|
19 | ||||
|
20 | import nose.tools as nt | |||
|
21 | ||||
|
22 | from IPython.kernel import KernelManager | |||
|
23 | ||||
|
24 | #------------------------------------------------------------------------------- | |||
|
25 | # Globals | |||
|
26 | #------------------------------------------------------------------------------- | |||
|
27 | ||||
|
28 | STARTUP_TIMEOUT = 60 | |||
|
29 | TIMEOUT = 15 | |||
|
30 | ||||
|
31 | KM = None | |||
|
32 | KC = None | |||
|
33 | ||||
|
34 | #------------------------------------------------------------------------------- | |||
|
35 | # code | |||
|
36 | #------------------------------------------------------------------------------- | |||
|
37 | ||||
|
38 | ||||
|
39 | def start_new_kernel(): | |||
|
40 | """start a new kernel, and return its Manager and Client""" | |||
|
41 | km = KernelManager() | |||
|
42 | km.start_kernel(stdout=PIPE, stderr=PIPE) | |||
|
43 | kc = km.client() | |||
|
44 | kc.start_channels() | |||
|
45 | ||||
|
46 | msg_id = kc.kernel_info() | |||
|
47 | kc.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT) | |||
|
48 | flush_channels(kc) | |||
|
49 | return km, kc | |||
|
50 | ||||
|
51 | def flush_channels(kc=None): | |||
|
52 | """flush any messages waiting on the queue""" | |||
|
53 | from .test_message_spec import validate_message | |||
|
54 | ||||
|
55 | if kc is None: | |||
|
56 | kc = KC | |||
|
57 | for channel in (kc.shell_channel, kc.iopub_channel): | |||
|
58 | while True: | |||
|
59 | try: | |||
|
60 | msg = channel.get_msg(block=True, timeout=0.1) | |||
|
61 | except Empty: | |||
|
62 | break | |||
|
63 | else: | |||
|
64 | validate_message(msg) | |||
|
65 | ||||
|
66 | ||||
|
67 | def execute(code='', kc=None, **kwargs): | |||
|
68 | """wrapper for doing common steps for validating an execution request""" | |||
|
69 | from .test_message_spec import validate_message | |||
|
70 | if kc is None: | |||
|
71 | kc = KC | |||
|
72 | msg_id = kc.execute(code=code, **kwargs) | |||
|
73 | reply = kc.get_shell_msg(timeout=TIMEOUT) | |||
|
74 | validate_message(reply, 'execute_reply', msg_id) | |||
|
75 | busy = kc.get_iopub_msg(timeout=TIMEOUT) | |||
|
76 | validate_message(busy, 'status', msg_id) | |||
|
77 | nt.assert_equal(busy['content']['execution_state'], 'busy') | |||
|
78 | ||||
|
79 | if not kwargs.get('silent'): | |||
|
80 | pyin = kc.get_iopub_msg(timeout=TIMEOUT) | |||
|
81 | validate_message(pyin, 'pyin', msg_id) | |||
|
82 | nt.assert_equal(pyin['content']['code'], code) | |||
|
83 | ||||
|
84 | return msg_id, reply['content'] | |||
|
85 | ||||
|
86 | def start_global_kernel(): | |||
|
87 | """start the global kernel (if it isn't running) and return its client""" | |||
|
88 | global KM, KC | |||
|
89 | if KM is None: | |||
|
90 | KM, KC = start_new_kernel() | |||
|
91 | atexit.register(stop_global_kernel) | |||
|
92 | return KC | |||
|
93 | ||||
|
94 | @contextmanager | |||
|
95 | def kernel(): | |||
|
96 | """Context manager for the global kernel instance | |||
|
97 | ||||
|
98 | Should be used for most kernel tests | |||
|
99 | ||||
|
100 | Returns | |||
|
101 | ------- | |||
|
102 | kernel_client: connected KernelClient instance | |||
|
103 | """ | |||
|
104 | yield start_global_kernel() | |||
|
105 | ||||
|
106 | def uses_kernel(test_f): | |||
|
107 | """Decorator for tests that use the global kernel""" | |||
|
108 | def wrapped_test(): | |||
|
109 | with kernel() as kc: | |||
|
110 | test_f(kc) | |||
|
111 | wrapped_test.__doc__ = test_f.__doc__ | |||
|
112 | wrapped_test.__name__ = test_f.__name__ | |||
|
113 | return wrapped_test | |||
|
114 | ||||
|
115 | def stop_global_kernel(): | |||
|
116 | """Stop the global shared kernel instance, if it exists""" | |||
|
117 | global KM, KC | |||
|
118 | KC.stop_channels() | |||
|
119 | KC = None | |||
|
120 | if KM is None: | |||
|
121 | return | |||
|
122 | KM.shutdown_kernel(now=True) | |||
|
123 | KM = None | |||
|
124 | ||||
|
125 | @contextmanager | |||
|
126 | def new_kernel(): | |||
|
127 | """Context manager for a new kernel in a subprocess | |||
|
128 | ||||
|
129 | Should only be used for tests where the kernel must not be re-used. | |||
|
130 | ||||
|
131 | Returns | |||
|
132 | ------- | |||
|
133 | kernel_client: connected KernelClient instance | |||
|
134 | """ | |||
|
135 | km, kc = start_new_kernel() | |||
|
136 | try: | |||
|
137 | yield kc | |||
|
138 | finally: | |||
|
139 | kc.stop_channels() | |||
|
140 | km.shutdown_kernel(now=True) | |||
|
141 | ||||
|
142 | ||||
|
143 | def assemble_output(iopub): | |||
|
144 | """assemble stdout/err from an execution""" | |||
|
145 | stdout = '' | |||
|
146 | stderr = '' | |||
|
147 | while True: | |||
|
148 | msg = iopub.get_msg(block=True, timeout=1) | |||
|
149 | msg_type = msg['msg_type'] | |||
|
150 | content = msg['content'] | |||
|
151 | if msg_type == 'status' and content['execution_state'] == 'idle': | |||
|
152 | # idle message signals end of output | |||
|
153 | break | |||
|
154 | elif msg['msg_type'] == 'stream': | |||
|
155 | if content['name'] == 'stdout': | |||
|
156 | stdout += content['data'] | |||
|
157 | elif content['name'] == 'stderr': | |||
|
158 | stderr += content['data'] | |||
|
159 | else: | |||
|
160 | raise KeyError("bad stream: %r" % content['name']) | |||
|
161 | else: | |||
|
162 | # other output, ignored | |||
|
163 | pass | |||
|
164 | return stdout, stderr | |||
|
165 | ||||
|
166 | ||||
|
167 |
@@ -1,301 +1,301 b'' | |||||
1 | """A kernel manager for multiple kernels |
|
1 | """A kernel manager for multiple kernels | |
2 |
|
2 | |||
3 | Authors: |
|
3 | Authors: | |
4 |
|
4 | |||
5 | * Brian Granger |
|
5 | * Brian Granger | |
6 | """ |
|
6 | """ | |
7 |
|
7 | |||
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 | # Copyright (C) 2013 The IPython Development Team |
|
9 | # Copyright (C) 2013 The IPython Development Team | |
10 | # |
|
10 | # | |
11 | # Distributed under the terms of the BSD License. The full license is in |
|
11 | # Distributed under the terms of the BSD License. The full license is in | |
12 | # the file COPYING, distributed as part of this software. |
|
12 | # the file COPYING, distributed as part of this software. | |
13 | #----------------------------------------------------------------------------- |
|
13 | #----------------------------------------------------------------------------- | |
14 |
|
14 | |||
15 | #----------------------------------------------------------------------------- |
|
15 | #----------------------------------------------------------------------------- | |
16 | # Imports |
|
16 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 |
|
18 | |||
19 | from __future__ import absolute_import |
|
19 | from __future__ import absolute_import | |
20 |
|
20 | |||
21 | import os |
|
21 | import os | |
22 | import uuid |
|
22 | import uuid | |
23 |
|
23 | |||
24 | import zmq |
|
24 | import zmq | |
25 |
|
25 | |||
26 | from IPython.config.configurable import LoggingConfigurable |
|
26 | from IPython.config.configurable import LoggingConfigurable | |
27 | from IPython.utils.importstring import import_item |
|
27 | from IPython.utils.importstring import import_item | |
28 | from IPython.utils.traitlets import ( |
|
28 | from IPython.utils.traitlets import ( | |
29 | Instance, Dict, Unicode, Any, DottedObjectName |
|
29 | Instance, Dict, Unicode, Any, DottedObjectName | |
30 | ) |
|
30 | ) | |
31 |
|
31 | |||
32 | #----------------------------------------------------------------------------- |
|
32 | #----------------------------------------------------------------------------- | |
33 | # Classes |
|
33 | # Classes | |
34 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
35 |
|
35 | |||
36 | class DuplicateKernelError(Exception): |
|
36 | class DuplicateKernelError(Exception): | |
37 | pass |
|
37 | pass | |
38 |
|
38 | |||
39 |
|
39 | |||
40 |
|
40 | |||
41 | def kernel_method(f): |
|
41 | def kernel_method(f): | |
42 | """decorator for proxying MKM.method(kernel_id) to individual KMs by ID""" |
|
42 | """decorator for proxying MKM.method(kernel_id) to individual KMs by ID""" | |
43 | def wrapped(self, kernel_id, *args, **kwargs): |
|
43 | def wrapped(self, kernel_id, *args, **kwargs): | |
44 | # get the kernel |
|
44 | # get the kernel | |
45 | km = self.get_kernel(kernel_id) |
|
45 | km = self.get_kernel(kernel_id) | |
46 | method = getattr(km, f.__name__) |
|
46 | method = getattr(km, f.__name__) | |
47 | # call the kernel's method |
|
47 | # call the kernel's method | |
48 | r = method(*args, **kwargs) |
|
48 | r = method(*args, **kwargs) | |
49 | # last thing, call anything defined in the actual class method |
|
49 | # last thing, call anything defined in the actual class method | |
50 | # such as logging messages |
|
50 | # such as logging messages | |
51 | f(self, kernel_id, *args, **kwargs) |
|
51 | f(self, kernel_id, *args, **kwargs) | |
52 | # return the method result |
|
52 | # return the method result | |
53 | return r |
|
53 | return r | |
54 | return wrapped |
|
54 | return wrapped | |
55 |
|
55 | |||
56 |
|
56 | |||
57 | class MultiKernelManager(LoggingConfigurable): |
|
57 | class MultiKernelManager(LoggingConfigurable): | |
58 | """A class for managing multiple kernels.""" |
|
58 | """A class for managing multiple kernels.""" | |
59 |
|
59 | |||
60 | kernel_manager_class = DottedObjectName( |
|
60 | kernel_manager_class = DottedObjectName( | |
61 | "IPython.kernel.ioloop.IOLoopKernelManager", config=True, |
|
61 | "IPython.kernel.ioloop.IOLoopKernelManager", config=True, | |
62 | help="""The kernel manager class. This is configurable to allow |
|
62 | help="""The kernel manager class. This is configurable to allow | |
63 | subclassing of the KernelManager for customized behavior. |
|
63 | subclassing of the KernelManager for customized behavior. | |
64 | """ |
|
64 | """ | |
65 | ) |
|
65 | ) | |
66 | def _kernel_manager_class_changed(self, name, old, new): |
|
66 | def _kernel_manager_class_changed(self, name, old, new): | |
67 | self.kernel_manager_factory = import_item(new) |
|
67 | self.kernel_manager_factory = import_item(new) | |
68 |
|
68 | |||
69 | kernel_manager_factory = Any(help="this is kernel_manager_class after import") |
|
69 | kernel_manager_factory = Any(help="this is kernel_manager_class after import") | |
70 | def _kernel_manager_factory_default(self): |
|
70 | def _kernel_manager_factory_default(self): | |
71 | return import_item(self.kernel_manager_class) |
|
71 | return import_item(self.kernel_manager_class) | |
72 |
|
72 | |||
73 | context = Instance('zmq.Context') |
|
73 | context = Instance('zmq.Context') | |
74 | def _context_default(self): |
|
74 | def _context_default(self): | |
75 | return zmq.Context.instance() |
|
75 | return zmq.Context.instance() | |
76 |
|
76 | |||
77 | connection_dir = Unicode('') |
|
77 | connection_dir = Unicode('') | |
78 |
|
78 | |||
79 | _kernels = Dict() |
|
79 | _kernels = Dict() | |
80 |
|
80 | |||
81 | def list_kernel_ids(self): |
|
81 | def list_kernel_ids(self): | |
82 | """Return a list of the kernel ids of the active kernels.""" |
|
82 | """Return a list of the kernel ids of the active kernels.""" | |
83 | # Create a copy so we can iterate over kernels in operations |
|
83 | # Create a copy so we can iterate over kernels in operations | |
84 | # that delete keys. |
|
84 | # that delete keys. | |
85 | return list(self._kernels.keys()) |
|
85 | return list(self._kernels.keys()) | |
86 |
|
86 | |||
87 | def __len__(self): |
|
87 | def __len__(self): | |
88 | """Return the number of running kernels.""" |
|
88 | """Return the number of running kernels.""" | |
89 | return len(self.list_kernel_ids()) |
|
89 | return len(self.list_kernel_ids()) | |
90 |
|
90 | |||
91 | def __contains__(self, kernel_id): |
|
91 | def __contains__(self, kernel_id): | |
92 | return kernel_id in self._kernels |
|
92 | return kernel_id in self._kernels | |
93 |
|
93 | |||
94 | def start_kernel(self, **kwargs): |
|
94 | def start_kernel(self, **kwargs): | |
95 | """Start a new kernel. |
|
95 | """Start a new kernel. | |
96 |
|
96 | |||
97 | The caller can pick a kernel_id by passing one in as a keyword arg, |
|
97 | The caller can pick a kernel_id by passing one in as a keyword arg, | |
98 | otherwise one will be picked using a uuid. |
|
98 | otherwise one will be picked using a uuid. | |
99 |
|
99 | |||
100 | To silence the kernel's stdout/stderr, call this using:: |
|
100 | To silence the kernel's stdout/stderr, call this using:: | |
101 |
|
101 | |||
102 | km.start_kernel(stdout=PIPE, stderr=PIPE) |
|
102 | km.start_kernel(stdout=PIPE, stderr=PIPE) | |
103 |
|
103 | |||
104 | """ |
|
104 | """ | |
105 | kernel_id = kwargs.pop('kernel_id', unicode(uuid.uuid4())) |
|
105 | kernel_id = kwargs.pop('kernel_id', unicode(uuid.uuid4())) | |
106 | if kernel_id in self: |
|
106 | if kernel_id in self: | |
107 | raise DuplicateKernelError('Kernel already exists: %s' % kernel_id) |
|
107 | raise DuplicateKernelError('Kernel already exists: %s' % kernel_id) | |
108 | # kernel_manager_factory is the constructor for the KernelManager |
|
108 | # kernel_manager_factory is the constructor for the KernelManager | |
109 | # subclass we are using. It can be configured as any Configurable, |
|
109 | # subclass we are using. It can be configured as any Configurable, | |
110 | # including things like its transport and ip. |
|
110 | # including things like its transport and ip. | |
111 | km = self.kernel_manager_factory(connection_file=os.path.join( |
|
111 | km = self.kernel_manager_factory(connection_file=os.path.join( | |
112 | self.connection_dir, "kernel-%s.json" % kernel_id), |
|
112 | self.connection_dir, "kernel-%s.json" % kernel_id), | |
113 | parent=self, autorestart=True, log=self.log |
|
113 | parent=self, autorestart=True, log=self.log | |
114 | ) |
|
114 | ) | |
115 | km.start_kernel(**kwargs) |
|
115 | km.start_kernel(**kwargs) | |
116 | self._kernels[kernel_id] = km |
|
116 | self._kernels[kernel_id] = km | |
117 | return kernel_id |
|
117 | return kernel_id | |
118 |
|
118 | |||
119 | @kernel_method |
|
119 | @kernel_method | |
120 | def shutdown_kernel(self, kernel_id, now=False): |
|
120 | def shutdown_kernel(self, kernel_id, now=False): | |
121 | """Shutdown a kernel by its kernel uuid. |
|
121 | """Shutdown a kernel by its kernel uuid. | |
122 |
|
122 | |||
123 | Parameters |
|
123 | Parameters | |
124 | ========== |
|
124 | ========== | |
125 | kernel_id : uuid |
|
125 | kernel_id : uuid | |
126 | The id of the kernel to shutdown. |
|
126 | The id of the kernel to shutdown. | |
127 | now : bool |
|
127 | now : bool | |
128 | Should the kernel be shutdown forcibly using a signal. |
|
128 | Should the kernel be shutdown forcibly using a signal. | |
129 | """ |
|
129 | """ | |
130 | self.log.info("Kernel shutdown: %s" % kernel_id) |
|
130 | self.log.info("Kernel shutdown: %s" % kernel_id) | |
131 | self.remove_kernel(kernel_id) |
|
131 | self.remove_kernel(kernel_id) | |
132 |
|
132 | |||
133 | def remove_kernel(self, kernel_id): |
|
133 | def remove_kernel(self, kernel_id): | |
134 | """remove a kernel from our mapping. |
|
134 | """remove a kernel from our mapping. | |
135 |
|
135 | |||
136 | Mainly so that a kernel can be removed if it is already dead, |
|
136 | Mainly so that a kernel can be removed if it is already dead, | |
137 | without having to call shutdown_kernel. |
|
137 | without having to call shutdown_kernel. | |
138 |
|
138 | |||
139 | The kernel object is returned. |
|
139 | The kernel object is returned. | |
140 | """ |
|
140 | """ | |
141 | return self._kernels.pop(kernel_id) |
|
141 | return self._kernels.pop(kernel_id) | |
142 |
|
142 | |||
143 | def shutdown_all(self, now=False): |
|
143 | def shutdown_all(self, now=False): | |
144 | """Shutdown all kernels.""" |
|
144 | """Shutdown all kernels.""" | |
145 | for kid in self.list_kernel_ids(): |
|
145 | for kid in self.list_kernel_ids(): | |
146 | self.shutdown_kernel(kid, now=now) |
|
146 | self.shutdown_kernel(kid, now=now) | |
147 |
|
147 | |||
148 | @kernel_method |
|
148 | @kernel_method | |
149 | def interrupt_kernel(self, kernel_id): |
|
149 | def interrupt_kernel(self, kernel_id): | |
150 | """Interrupt (SIGINT) the kernel by its uuid. |
|
150 | """Interrupt (SIGINT) the kernel by its uuid. | |
151 |
|
151 | |||
152 | Parameters |
|
152 | Parameters | |
153 | ========== |
|
153 | ========== | |
154 | kernel_id : uuid |
|
154 | kernel_id : uuid | |
155 | The id of the kernel to interrupt. |
|
155 | The id of the kernel to interrupt. | |
156 | """ |
|
156 | """ | |
157 | self.log.info("Kernel interrupted: %s" % kernel_id) |
|
157 | self.log.info("Kernel interrupted: %s" % kernel_id) | |
158 |
|
158 | |||
159 | @kernel_method |
|
159 | @kernel_method | |
160 | def signal_kernel(self, kernel_id, signum): |
|
160 | def signal_kernel(self, kernel_id, signum): | |
161 | """Sends a signal to the kernel by its uuid. |
|
161 | """Sends a signal to the kernel by its uuid. | |
162 |
|
162 | |||
163 | Note that since only SIGTERM is supported on Windows, this function |
|
163 | Note that since only SIGTERM is supported on Windows, this function | |
164 | is only useful on Unix systems. |
|
164 | is only useful on Unix systems. | |
165 |
|
165 | |||
166 | Parameters |
|
166 | Parameters | |
167 | ========== |
|
167 | ========== | |
168 | kernel_id : uuid |
|
168 | kernel_id : uuid | |
169 | The id of the kernel to signal. |
|
169 | The id of the kernel to signal. | |
170 | """ |
|
170 | """ | |
171 | self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum)) |
|
171 | self.log.info("Signaled Kernel %s with %s" % (kernel_id, signum)) | |
172 |
|
172 | |||
173 | @kernel_method |
|
173 | @kernel_method | |
174 | def restart_kernel(self, kernel_id): |
|
174 | def restart_kernel(self, kernel_id, now=False): | |
175 | """Restart a kernel by its uuid, keeping the same ports. |
|
175 | """Restart a kernel by its uuid, keeping the same ports. | |
176 |
|
176 | |||
177 | Parameters |
|
177 | Parameters | |
178 | ========== |
|
178 | ========== | |
179 | kernel_id : uuid |
|
179 | kernel_id : uuid | |
180 | The id of the kernel to interrupt. |
|
180 | The id of the kernel to interrupt. | |
181 | """ |
|
181 | """ | |
182 | self.log.info("Kernel restarted: %s" % kernel_id) |
|
182 | self.log.info("Kernel restarted: %s" % kernel_id) | |
183 |
|
183 | |||
184 | @kernel_method |
|
184 | @kernel_method | |
185 | def is_alive(self, kernel_id): |
|
185 | def is_alive(self, kernel_id): | |
186 | """Is the kernel alive. |
|
186 | """Is the kernel alive. | |
187 |
|
187 | |||
188 | This calls KernelManager.is_alive() which calls Popen.poll on the |
|
188 | This calls KernelManager.is_alive() which calls Popen.poll on the | |
189 | actual kernel subprocess. |
|
189 | actual kernel subprocess. | |
190 |
|
190 | |||
191 | Parameters |
|
191 | Parameters | |
192 | ========== |
|
192 | ========== | |
193 | kernel_id : uuid |
|
193 | kernel_id : uuid | |
194 | The id of the kernel. |
|
194 | The id of the kernel. | |
195 | """ |
|
195 | """ | |
196 |
|
196 | |||
197 | def _check_kernel_id(self, kernel_id): |
|
197 | def _check_kernel_id(self, kernel_id): | |
198 | """check that a kernel id is valid""" |
|
198 | """check that a kernel id is valid""" | |
199 | if kernel_id not in self: |
|
199 | if kernel_id not in self: | |
200 | raise KeyError("Kernel with id not found: %s" % kernel_id) |
|
200 | raise KeyError("Kernel with id not found: %s" % kernel_id) | |
201 |
|
201 | |||
202 | def get_kernel(self, kernel_id): |
|
202 | def get_kernel(self, kernel_id): | |
203 | """Get the single KernelManager object for a kernel by its uuid. |
|
203 | """Get the single KernelManager object for a kernel by its uuid. | |
204 |
|
204 | |||
205 | Parameters |
|
205 | Parameters | |
206 | ========== |
|
206 | ========== | |
207 | kernel_id : uuid |
|
207 | kernel_id : uuid | |
208 | The id of the kernel. |
|
208 | The id of the kernel. | |
209 | """ |
|
209 | """ | |
210 | self._check_kernel_id(kernel_id) |
|
210 | self._check_kernel_id(kernel_id) | |
211 | return self._kernels[kernel_id] |
|
211 | return self._kernels[kernel_id] | |
212 |
|
212 | |||
213 | @kernel_method |
|
213 | @kernel_method | |
214 | def add_restart_callback(self, kernel_id, callback, event='restart'): |
|
214 | def add_restart_callback(self, kernel_id, callback, event='restart'): | |
215 | """add a callback for the KernelRestarter""" |
|
215 | """add a callback for the KernelRestarter""" | |
216 |
|
216 | |||
217 | @kernel_method |
|
217 | @kernel_method | |
218 | def remove_restart_callback(self, kernel_id, callback, event='restart'): |
|
218 | def remove_restart_callback(self, kernel_id, callback, event='restart'): | |
219 | """remove a callback for the KernelRestarter""" |
|
219 | """remove a callback for the KernelRestarter""" | |
220 |
|
220 | |||
221 | @kernel_method |
|
221 | @kernel_method | |
222 | def get_connection_info(self, kernel_id): |
|
222 | def get_connection_info(self, kernel_id): | |
223 | """Return a dictionary of connection data for a kernel. |
|
223 | """Return a dictionary of connection data for a kernel. | |
224 |
|
224 | |||
225 | Parameters |
|
225 | Parameters | |
226 | ========== |
|
226 | ========== | |
227 | kernel_id : uuid |
|
227 | kernel_id : uuid | |
228 | The id of the kernel. |
|
228 | The id of the kernel. | |
229 |
|
229 | |||
230 | Returns |
|
230 | Returns | |
231 | ======= |
|
231 | ======= | |
232 | connection_dict : dict |
|
232 | connection_dict : dict | |
233 | A dict of the information needed to connect to a kernel. |
|
233 | A dict of the information needed to connect to a kernel. | |
234 | This includes the ip address and the integer port |
|
234 | This includes the ip address and the integer port | |
235 | numbers of the different channels (stdin_port, iopub_port, |
|
235 | numbers of the different channels (stdin_port, iopub_port, | |
236 | shell_port, hb_port). |
|
236 | shell_port, hb_port). | |
237 | """ |
|
237 | """ | |
238 |
|
238 | |||
239 | @kernel_method |
|
239 | @kernel_method | |
240 | def connect_iopub(self, kernel_id, identity=None): |
|
240 | def connect_iopub(self, kernel_id, identity=None): | |
241 | """Return a zmq Socket connected to the iopub channel. |
|
241 | """Return a zmq Socket connected to the iopub channel. | |
242 |
|
242 | |||
243 | Parameters |
|
243 | Parameters | |
244 | ========== |
|
244 | ========== | |
245 | kernel_id : uuid |
|
245 | kernel_id : uuid | |
246 | The id of the kernel |
|
246 | The id of the kernel | |
247 | identity : bytes (optional) |
|
247 | identity : bytes (optional) | |
248 | The zmq identity of the socket |
|
248 | The zmq identity of the socket | |
249 |
|
249 | |||
250 | Returns |
|
250 | Returns | |
251 | ======= |
|
251 | ======= | |
252 | stream : zmq Socket or ZMQStream |
|
252 | stream : zmq Socket or ZMQStream | |
253 | """ |
|
253 | """ | |
254 |
|
254 | |||
255 | @kernel_method |
|
255 | @kernel_method | |
256 | def connect_shell(self, kernel_id, identity=None): |
|
256 | def connect_shell(self, kernel_id, identity=None): | |
257 | """Return a zmq Socket connected to the shell channel. |
|
257 | """Return a zmq Socket connected to the shell channel. | |
258 |
|
258 | |||
259 | Parameters |
|
259 | Parameters | |
260 | ========== |
|
260 | ========== | |
261 | kernel_id : uuid |
|
261 | kernel_id : uuid | |
262 | The id of the kernel |
|
262 | The id of the kernel | |
263 | identity : bytes (optional) |
|
263 | identity : bytes (optional) | |
264 | The zmq identity of the socket |
|
264 | The zmq identity of the socket | |
265 |
|
265 | |||
266 | Returns |
|
266 | Returns | |
267 | ======= |
|
267 | ======= | |
268 | stream : zmq Socket or ZMQStream |
|
268 | stream : zmq Socket or ZMQStream | |
269 | """ |
|
269 | """ | |
270 |
|
270 | |||
271 | @kernel_method |
|
271 | @kernel_method | |
272 | def connect_stdin(self, kernel_id, identity=None): |
|
272 | def connect_stdin(self, kernel_id, identity=None): | |
273 | """Return a zmq Socket connected to the stdin channel. |
|
273 | """Return a zmq Socket connected to the stdin channel. | |
274 |
|
274 | |||
275 | Parameters |
|
275 | Parameters | |
276 | ========== |
|
276 | ========== | |
277 | kernel_id : uuid |
|
277 | kernel_id : uuid | |
278 | The id of the kernel |
|
278 | The id of the kernel | |
279 | identity : bytes (optional) |
|
279 | identity : bytes (optional) | |
280 | The zmq identity of the socket |
|
280 | The zmq identity of the socket | |
281 |
|
281 | |||
282 | Returns |
|
282 | Returns | |
283 | ======= |
|
283 | ======= | |
284 | stream : zmq Socket or ZMQStream |
|
284 | stream : zmq Socket or ZMQStream | |
285 | """ |
|
285 | """ | |
286 |
|
286 | |||
287 | @kernel_method |
|
287 | @kernel_method | |
288 | def connect_hb(self, kernel_id, identity=None): |
|
288 | def connect_hb(self, kernel_id, identity=None): | |
289 | """Return a zmq Socket connected to the hb channel. |
|
289 | """Return a zmq Socket connected to the hb channel. | |
290 |
|
290 | |||
291 | Parameters |
|
291 | Parameters | |
292 | ========== |
|
292 | ========== | |
293 | kernel_id : uuid |
|
293 | kernel_id : uuid | |
294 | The id of the kernel |
|
294 | The id of the kernel | |
295 | identity : bytes (optional) |
|
295 | identity : bytes (optional) | |
296 | The zmq identity of the socket |
|
296 | The zmq identity of the socket | |
297 |
|
297 | |||
298 | Returns |
|
298 | Returns | |
299 | ======= |
|
299 | ======= | |
300 | stream : zmq Socket or ZMQStream |
|
300 | stream : zmq Socket or ZMQStream | |
301 | """ |
|
301 | """ |
@@ -1,256 +1,170 b'' | |||||
1 | """test the IPython Kernel""" |
|
1 | """test the IPython Kernel""" | |
2 |
|
2 | |||
3 | #------------------------------------------------------------------------------- |
|
3 | #------------------------------------------------------------------------------- | |
4 | # Copyright (C) 2013 The IPython Development Team |
|
4 | # Copyright (C) 2013 The IPython Development Team | |
5 | # |
|
5 | # | |
6 | # Distributed under the terms of the BSD License. The full license is in |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
7 | # the file COPYING, distributed as part of this software. |
|
7 | # the file COPYING, distributed as part of this software. | |
8 | #------------------------------------------------------------------------------- |
|
8 | #------------------------------------------------------------------------------- | |
9 |
|
9 | |||
10 | #------------------------------------------------------------------------------- |
|
10 | #------------------------------------------------------------------------------- | |
11 | # Imports |
|
11 | # Imports | |
12 | #------------------------------------------------------------------------------- |
|
12 | #------------------------------------------------------------------------------- | |
13 |
|
13 | |||
14 | import os |
|
|||
15 | import shutil |
|
|||
16 | import sys |
|
14 | import sys | |
17 | import tempfile |
|
|||
18 |
|
||||
19 | from contextlib import contextmanager |
|
|||
20 | from subprocess import PIPE |
|
|||
21 |
|
15 | |||
22 | import nose.tools as nt |
|
16 | import nose.tools as nt | |
23 |
|
17 | |||
24 | from IPython.kernel import KernelManager |
|
|||
25 | from IPython.kernel.tests.test_message_spec import execute, flush_channels |
|
|||
26 | from IPython.testing import decorators as dec, tools as tt |
|
18 | from IPython.testing import decorators as dec, tools as tt | |
27 |
from IPython.utils import |
|
19 | from IPython.utils import py3compat | |
|
20 | ||||
|
21 | from .utils import new_kernel, kernel, TIMEOUT, assemble_output, execute, flush_channels | |||
28 |
|
22 | |||
29 | #------------------------------------------------------------------------------- |
|
23 | #------------------------------------------------------------------------------- | |
30 | # Tests |
|
24 | # Tests | |
31 | #------------------------------------------------------------------------------- |
|
25 | #------------------------------------------------------------------------------- | |
32 | IPYTHONDIR = None |
|
|||
33 | save_env = None |
|
|||
34 | save_get_ipython_dir = None |
|
|||
35 |
|
||||
36 | STARTUP_TIMEOUT = 60 |
|
|||
37 | TIMEOUT = 15 |
|
|||
38 |
|
||||
39 | def setup(): |
|
|||
40 | """setup temporary IPYTHONDIR for tests""" |
|
|||
41 | global IPYTHONDIR |
|
|||
42 | global save_env |
|
|||
43 | global save_get_ipython_dir |
|
|||
44 |
|
||||
45 | IPYTHONDIR = tempfile.mkdtemp() |
|
|||
46 |
|
||||
47 | save_env = os.environ.copy() |
|
|||
48 | os.environ["IPYTHONDIR"] = IPYTHONDIR |
|
|||
49 |
|
||||
50 | save_get_ipython_dir = path.get_ipython_dir |
|
|||
51 | path.get_ipython_dir = lambda : IPYTHONDIR |
|
|||
52 |
|
||||
53 |
|
||||
54 | def teardown(): |
|
|||
55 | path.get_ipython_dir = save_get_ipython_dir |
|
|||
56 | os.environ = save_env |
|
|||
57 |
|
||||
58 | try: |
|
|||
59 | shutil.rmtree(IPYTHONDIR) |
|
|||
60 | except (OSError, IOError): |
|
|||
61 | # no such file |
|
|||
62 | pass |
|
|||
63 |
|
||||
64 |
|
||||
65 | @contextmanager |
|
|||
66 | def new_kernel(): |
|
|||
67 | """start a kernel in a subprocess, and wait for it to be ready |
|
|||
68 |
|
||||
69 | Returns |
|
|||
70 | ------- |
|
|||
71 | kernel_manager: connected KernelManager instance |
|
|||
72 | """ |
|
|||
73 | KM = KernelManager() |
|
|||
74 |
|
||||
75 | KM.start_kernel(stdout=PIPE, stderr=PIPE) |
|
|||
76 | KC = KM.client() |
|
|||
77 | KC.start_channels() |
|
|||
78 |
|
||||
79 | # wait for kernel to be ready |
|
|||
80 | KC.shell_channel.execute("import sys") |
|
|||
81 | KC.shell_channel.get_msg(block=True, timeout=STARTUP_TIMEOUT) |
|
|||
82 | flush_channels(KC) |
|
|||
83 | try: |
|
|||
84 | yield KC |
|
|||
85 | finally: |
|
|||
86 | KC.stop_channels() |
|
|||
87 | KM.shutdown_kernel() |
|
|||
88 |
|
||||
89 |
|
||||
90 | def assemble_output(iopub): |
|
|||
91 | """assemble stdout/err from an execution""" |
|
|||
92 | stdout = '' |
|
|||
93 | stderr = '' |
|
|||
94 | while True: |
|
|||
95 | msg = iopub.get_msg(block=True, timeout=1) |
|
|||
96 | msg_type = msg['msg_type'] |
|
|||
97 | content = msg['content'] |
|
|||
98 | if msg_type == 'status' and content['execution_state'] == 'idle': |
|
|||
99 | # idle message signals end of output |
|
|||
100 | break |
|
|||
101 | elif msg['msg_type'] == 'stream': |
|
|||
102 | if content['name'] == 'stdout': |
|
|||
103 | stdout = stdout + content['data'] |
|
|||
104 | elif content['name'] == 'stderr': |
|
|||
105 | stderr = stderr + content['data'] |
|
|||
106 | else: |
|
|||
107 | raise KeyError("bad stream: %r" % content['name']) |
|
|||
108 | else: |
|
|||
109 | # other output, ignored |
|
|||
110 | pass |
|
|||
111 | return stdout, stderr |
|
|||
112 |
|
26 | |||
113 |
|
27 | |||
114 | def _check_mp_mode(kc, expected=False, stream="stdout"): |
|
28 | def _check_mp_mode(kc, expected=False, stream="stdout"): | |
115 | execute(kc=kc, code="import sys") |
|
29 | execute(kc=kc, code="import sys") | |
116 | flush_channels(kc) |
|
30 | flush_channels(kc) | |
117 | msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream) |
|
31 | msg_id, content = execute(kc=kc, code="print (sys.%s._check_mp_mode())" % stream) | |
118 | stdout, stderr = assemble_output(kc.iopub_channel) |
|
32 | stdout, stderr = assemble_output(kc.iopub_channel) | |
119 | nt.assert_equal(eval(stdout.strip()), expected) |
|
33 | nt.assert_equal(eval(stdout.strip()), expected) | |
120 |
|
34 | |||
121 |
|
35 | |||
122 | # printing tests |
|
36 | # printing tests | |
123 |
|
37 | |||
124 | def test_simple_print(): |
|
38 | def test_simple_print(): | |
125 | """simple print statement in kernel""" |
|
39 | """simple print statement in kernel""" | |
126 |
with |
|
40 | with kernel() as kc: | |
127 | iopub = kc.iopub_channel |
|
41 | iopub = kc.iopub_channel | |
128 | msg_id, content = execute(kc=kc, code="print ('hi')") |
|
42 | msg_id, content = execute(kc=kc, code="print ('hi')") | |
129 | stdout, stderr = assemble_output(iopub) |
|
43 | stdout, stderr = assemble_output(iopub) | |
130 | nt.assert_equal(stdout, 'hi\n') |
|
44 | nt.assert_equal(stdout, 'hi\n') | |
131 | nt.assert_equal(stderr, '') |
|
45 | nt.assert_equal(stderr, '') | |
132 | _check_mp_mode(kc, expected=False) |
|
46 | _check_mp_mode(kc, expected=False) | |
133 |
|
47 | |||
134 |
|
48 | |||
135 | @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") |
|
49 | @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") | |
136 | def test_subprocess_print(): |
|
50 | def test_subprocess_print(): | |
137 | """printing from forked mp.Process""" |
|
51 | """printing from forked mp.Process""" | |
138 | with new_kernel() as kc: |
|
52 | with new_kernel() as kc: | |
139 | iopub = kc.iopub_channel |
|
53 | iopub = kc.iopub_channel | |
140 |
|
54 | |||
141 | _check_mp_mode(kc, expected=False) |
|
55 | _check_mp_mode(kc, expected=False) | |
142 | flush_channels(kc) |
|
56 | flush_channels(kc) | |
143 | np = 5 |
|
57 | np = 5 | |
144 | code = '\n'.join([ |
|
58 | code = '\n'.join([ | |
145 | "from __future__ import print_function", |
|
59 | "from __future__ import print_function", | |
146 | "import multiprocessing as mp", |
|
60 | "import multiprocessing as mp", | |
147 | "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np, |
|
61 | "pool = [mp.Process(target=print, args=('hello', i,)) for i in range(%i)]" % np, | |
148 | "for p in pool: p.start()", |
|
62 | "for p in pool: p.start()", | |
149 | "for p in pool: p.join()" |
|
63 | "for p in pool: p.join()" | |
150 | ]) |
|
64 | ]) | |
151 |
|
65 | |||
152 | expected = '\n'.join([ |
|
66 | expected = '\n'.join([ | |
153 | "hello %s" % i for i in range(np) |
|
67 | "hello %s" % i for i in range(np) | |
154 | ]) + '\n' |
|
68 | ]) + '\n' | |
155 |
|
69 | |||
156 | msg_id, content = execute(kc=kc, code=code) |
|
70 | msg_id, content = execute(kc=kc, code=code) | |
157 | stdout, stderr = assemble_output(iopub) |
|
71 | stdout, stderr = assemble_output(iopub) | |
158 | nt.assert_equal(stdout.count("hello"), np, stdout) |
|
72 | nt.assert_equal(stdout.count("hello"), np, stdout) | |
159 | for n in range(np): |
|
73 | for n in range(np): | |
160 | nt.assert_equal(stdout.count(str(n)), 1, stdout) |
|
74 | nt.assert_equal(stdout.count(str(n)), 1, stdout) | |
161 | nt.assert_equal(stderr, '') |
|
75 | nt.assert_equal(stderr, '') | |
162 | _check_mp_mode(kc, expected=False) |
|
76 | _check_mp_mode(kc, expected=False) | |
163 | _check_mp_mode(kc, expected=False, stream="stderr") |
|
77 | _check_mp_mode(kc, expected=False, stream="stderr") | |
164 |
|
78 | |||
165 |
|
79 | |||
166 | def test_subprocess_noprint(): |
|
80 | def test_subprocess_noprint(): | |
167 | """mp.Process without print doesn't trigger iostream mp_mode""" |
|
81 | """mp.Process without print doesn't trigger iostream mp_mode""" | |
168 |
with |
|
82 | with kernel() as kc: | |
169 | iopub = kc.iopub_channel |
|
83 | iopub = kc.iopub_channel | |
170 |
|
84 | |||
171 | np = 5 |
|
85 | np = 5 | |
172 | code = '\n'.join([ |
|
86 | code = '\n'.join([ | |
173 | "import multiprocessing as mp", |
|
87 | "import multiprocessing as mp", | |
174 | "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np, |
|
88 | "pool = [mp.Process(target=range, args=(i,)) for i in range(%i)]" % np, | |
175 | "for p in pool: p.start()", |
|
89 | "for p in pool: p.start()", | |
176 | "for p in pool: p.join()" |
|
90 | "for p in pool: p.join()" | |
177 | ]) |
|
91 | ]) | |
178 |
|
92 | |||
179 | msg_id, content = execute(kc=kc, code=code) |
|
93 | msg_id, content = execute(kc=kc, code=code) | |
180 | stdout, stderr = assemble_output(iopub) |
|
94 | stdout, stderr = assemble_output(iopub) | |
181 | nt.assert_equal(stdout, '') |
|
95 | nt.assert_equal(stdout, '') | |
182 | nt.assert_equal(stderr, '') |
|
96 | nt.assert_equal(stderr, '') | |
183 |
|
97 | |||
184 | _check_mp_mode(kc, expected=False) |
|
98 | _check_mp_mode(kc, expected=False) | |
185 | _check_mp_mode(kc, expected=False, stream="stderr") |
|
99 | _check_mp_mode(kc, expected=False, stream="stderr") | |
186 |
|
100 | |||
187 |
|
101 | |||
188 | @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") |
|
102 | @dec.knownfailureif(sys.platform == 'win32', "subprocess prints fail on Windows") | |
189 | def test_subprocess_error(): |
|
103 | def test_subprocess_error(): | |
190 | """error in mp.Process doesn't crash""" |
|
104 | """error in mp.Process doesn't crash""" | |
191 | with new_kernel() as kc: |
|
105 | with new_kernel() as kc: | |
192 | iopub = kc.iopub_channel |
|
106 | iopub = kc.iopub_channel | |
193 |
|
107 | |||
194 | code = '\n'.join([ |
|
108 | code = '\n'.join([ | |
195 | "import multiprocessing as mp", |
|
109 | "import multiprocessing as mp", | |
196 | "p = mp.Process(target=int, args=('hi',))", |
|
110 | "p = mp.Process(target=int, args=('hi',))", | |
197 | "p.start()", |
|
111 | "p.start()", | |
198 | "p.join()", |
|
112 | "p.join()", | |
199 | ]) |
|
113 | ]) | |
200 |
|
114 | |||
201 | msg_id, content = execute(kc=kc, code=code) |
|
115 | msg_id, content = execute(kc=kc, code=code) | |
202 | stdout, stderr = assemble_output(iopub) |
|
116 | stdout, stderr = assemble_output(iopub) | |
203 | nt.assert_equal(stdout, '') |
|
117 | nt.assert_equal(stdout, '') | |
204 | nt.assert_true("ValueError" in stderr, stderr) |
|
118 | nt.assert_true("ValueError" in stderr, stderr) | |
205 |
|
119 | |||
206 | _check_mp_mode(kc, expected=False) |
|
120 | _check_mp_mode(kc, expected=False) | |
207 | _check_mp_mode(kc, expected=False, stream="stderr") |
|
121 | _check_mp_mode(kc, expected=False, stream="stderr") | |
208 |
|
122 | |||
209 | # raw_input tests |
|
123 | # raw_input tests | |
210 |
|
124 | |||
211 | def test_raw_input(): |
|
125 | def test_raw_input(): | |
212 | """test [raw_]input""" |
|
126 | """test [raw_]input""" | |
213 |
with |
|
127 | with kernel() as kc: | |
214 | iopub = kc.iopub_channel |
|
128 | iopub = kc.iopub_channel | |
215 |
|
129 | |||
216 | input_f = "input" if py3compat.PY3 else "raw_input" |
|
130 | input_f = "input" if py3compat.PY3 else "raw_input" | |
217 | theprompt = "prompt> " |
|
131 | theprompt = "prompt> " | |
218 | code = 'print({input_f}("{theprompt}"))'.format(**locals()) |
|
132 | code = 'print({input_f}("{theprompt}"))'.format(**locals()) | |
219 | msg_id = kc.execute(code, allow_stdin=True) |
|
133 | msg_id = kc.execute(code, allow_stdin=True) | |
220 | msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) |
|
134 | msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) | |
221 | nt.assert_equal(msg['header']['msg_type'], u'input_request') |
|
135 | nt.assert_equal(msg['header']['msg_type'], u'input_request') | |
222 | content = msg['content'] |
|
136 | content = msg['content'] | |
223 | nt.assert_equal(content['prompt'], theprompt) |
|
137 | nt.assert_equal(content['prompt'], theprompt) | |
224 | text = "some text" |
|
138 | text = "some text" | |
225 | kc.input(text) |
|
139 | kc.input(text) | |
226 | reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) |
|
140 | reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) | |
227 | nt.assert_equal(reply['content']['status'], 'ok') |
|
141 | nt.assert_equal(reply['content']['status'], 'ok') | |
228 | stdout, stderr = assemble_output(iopub) |
|
142 | stdout, stderr = assemble_output(iopub) | |
229 | nt.assert_equal(stdout, text + "\n") |
|
143 | nt.assert_equal(stdout, text + "\n") | |
230 |
|
144 | |||
231 |
|
145 | |||
232 | @dec.skipif(py3compat.PY3) |
|
146 | @dec.skipif(py3compat.PY3) | |
233 | def test_eval_input(): |
|
147 | def test_eval_input(): | |
234 | """test input() on Python 2""" |
|
148 | """test input() on Python 2""" | |
235 |
with |
|
149 | with kernel() as kc: | |
236 | iopub = kc.iopub_channel |
|
150 | iopub = kc.iopub_channel | |
237 |
|
151 | |||
238 | input_f = "input" if py3compat.PY3 else "raw_input" |
|
152 | input_f = "input" if py3compat.PY3 else "raw_input" | |
239 | theprompt = "prompt> " |
|
153 | theprompt = "prompt> " | |
240 | code = 'print(input("{theprompt}"))'.format(**locals()) |
|
154 | code = 'print(input("{theprompt}"))'.format(**locals()) | |
241 | msg_id = kc.execute(code, allow_stdin=True) |
|
155 | msg_id = kc.execute(code, allow_stdin=True) | |
242 | msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) |
|
156 | msg = kc.get_stdin_msg(block=True, timeout=TIMEOUT) | |
243 | nt.assert_equal(msg['header']['msg_type'], u'input_request') |
|
157 | nt.assert_equal(msg['header']['msg_type'], u'input_request') | |
244 | content = msg['content'] |
|
158 | content = msg['content'] | |
245 | nt.assert_equal(content['prompt'], theprompt) |
|
159 | nt.assert_equal(content['prompt'], theprompt) | |
246 | kc.input("1+1") |
|
160 | kc.input("1+1") | |
247 | reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) |
|
161 | reply = kc.get_shell_msg(block=True, timeout=TIMEOUT) | |
248 | nt.assert_equal(reply['content']['status'], 'ok') |
|
162 | nt.assert_equal(reply['content']['status'], 'ok') | |
249 | stdout, stderr = assemble_output(iopub) |
|
163 | stdout, stderr = assemble_output(iopub) | |
250 | nt.assert_equal(stdout, "2\n") |
|
164 | nt.assert_equal(stdout, "2\n") | |
251 |
|
165 | |||
252 |
|
166 | |||
253 | def test_help_output(): |
|
167 | def test_help_output(): | |
254 | """ipython kernel --help-all works""" |
|
168 | """ipython kernel --help-all works""" | |
255 | tt.help_all_output_test('kernel') |
|
169 | tt.help_all_output_test('kernel') | |
256 |
|
170 |
@@ -1,61 +1,54 b'' | |||||
1 | """Tests for the notebook kernel and session manager""" |
|
1 | """Tests for the notebook kernel and session manager""" | |
2 |
|
2 | |||
3 | from subprocess import PIPE |
|
3 | from subprocess import PIPE | |
4 | import time |
|
4 | import time | |
5 | from unittest import TestCase |
|
5 | from unittest import TestCase | |
6 |
|
6 | |||
7 | from IPython.testing import decorators as dec |
|
7 | from IPython.testing import decorators as dec | |
8 |
|
8 | |||
9 | from IPython.config.loader import Config |
|
9 | from IPython.config.loader import Config | |
10 | from IPython.kernel import KernelManager |
|
10 | from IPython.kernel import KernelManager | |
11 |
|
11 | |||
12 | class TestKernelManager(TestCase): |
|
12 | class TestKernelManager(TestCase): | |
13 |
|
13 | |||
14 | def _get_tcp_km(self): |
|
14 | def _get_tcp_km(self): | |
15 | c = Config() |
|
15 | c = Config() | |
16 | km = KernelManager(config=c) |
|
16 | km = KernelManager(config=c) | |
17 | return km |
|
17 | return km | |
18 |
|
18 | |||
19 | def _get_ipc_km(self): |
|
19 | def _get_ipc_km(self): | |
20 | c = Config() |
|
20 | c = Config() | |
21 | c.KernelManager.transport = 'ipc' |
|
21 | c.KernelManager.transport = 'ipc' | |
22 | c.KernelManager.ip = 'test' |
|
22 | c.KernelManager.ip = 'test' | |
23 | km = KernelManager(config=c) |
|
23 | km = KernelManager(config=c) | |
24 | return km |
|
24 | return km | |
25 |
|
25 | |||
26 | def _run_lifecycle(self, km): |
|
26 | def _run_lifecycle(self, km): | |
27 | km.start_kernel(stdout=PIPE, stderr=PIPE) |
|
27 | km.start_kernel(stdout=PIPE, stderr=PIPE) | |
28 | self.assertTrue(km.is_alive()) |
|
28 | self.assertTrue(km.is_alive()) | |
29 | km.restart_kernel() |
|
29 | km.restart_kernel(now=True) | |
30 | self.assertTrue(km.is_alive()) |
|
30 | self.assertTrue(km.is_alive()) | |
31 | # We need a delay here to give the restarting kernel a chance to |
|
|||
32 | # restart. Otherwise, the interrupt will kill it, causing the test |
|
|||
33 | # suite to hang. The reason it *hangs* is that the shutdown |
|
|||
34 | # message for the restart sometimes hasn't been sent to the kernel. |
|
|||
35 | # Because linger is oo on the shell channel, the context can't |
|
|||
36 | # close until the message is sent to the kernel, which is not dead. |
|
|||
37 | time.sleep(1.0) |
|
|||
38 | km.interrupt_kernel() |
|
31 | km.interrupt_kernel() | |
39 | self.assertTrue(isinstance(km, KernelManager)) |
|
32 | self.assertTrue(isinstance(km, KernelManager)) | |
40 | km.shutdown_kernel() |
|
33 | km.shutdown_kernel(now=True) | |
41 |
|
34 | |||
42 | def test_tcp_lifecycle(self): |
|
35 | def test_tcp_lifecycle(self): | |
43 | km = self._get_tcp_km() |
|
36 | km = self._get_tcp_km() | |
44 | self._run_lifecycle(km) |
|
37 | self._run_lifecycle(km) | |
45 |
|
38 | |||
46 | @dec.skip_win32 |
|
39 | @dec.skip_win32 | |
47 | def test_ipc_lifecycle(self): |
|
40 | def test_ipc_lifecycle(self): | |
48 | km = self._get_ipc_km() |
|
41 | km = self._get_ipc_km() | |
49 | self._run_lifecycle(km) |
|
42 | self._run_lifecycle(km) | |
50 |
|
43 | |||
51 | def test_get_connect_info(self): |
|
44 | def test_get_connect_info(self): | |
52 | km = self._get_tcp_km() |
|
45 | km = self._get_tcp_km() | |
53 | cinfo = km.get_connection_info() |
|
46 | cinfo = km.get_connection_info() | |
54 | keys = sorted(cinfo.keys()) |
|
47 | keys = sorted(cinfo.keys()) | |
55 | expected = sorted([ |
|
48 | expected = sorted([ | |
56 | 'ip', 'transport', |
|
49 | 'ip', 'transport', | |
57 | 'hb_port', 'shell_port', 'stdin_port', 'iopub_port', 'control_port', |
|
50 | 'hb_port', 'shell_port', 'stdin_port', 'iopub_port', 'control_port', | |
58 | 'key', 'signature_scheme', |
|
51 | 'key', 'signature_scheme', | |
59 | ]) |
|
52 | ]) | |
60 | self.assertEqual(keys, expected) |
|
53 | self.assertEqual(keys, expected) | |
61 |
|
54 |
@@ -1,484 +1,433 b'' | |||||
1 |
"""Test suite for our zeromq-based messag |
|
1 | """Test suite for our zeromq-based message specification. | |
2 | """ |
|
2 | """ | |
3 | #----------------------------------------------------------------------------- |
|
3 | #----------------------------------------------------------------------------- | |
4 |
# Copyright (C) 2010 |
|
4 | # Copyright (C) 2010 The IPython Development Team | |
5 | # |
|
5 | # | |
6 | # Distributed under the terms of the BSD License. The full license is in |
|
6 | # Distributed under the terms of the BSD License. The full license is in | |
7 | # the file COPYING.txt, distributed as part of this software. |
|
7 | # the file COPYING.txt, distributed as part of this software. | |
8 | #----------------------------------------------------------------------------- |
|
8 | #----------------------------------------------------------------------------- | |
9 |
|
9 | |||
10 | import re |
|
10 | import re | |
11 | from subprocess import PIPE |
|
11 | from subprocess import PIPE | |
12 | from Queue import Empty |
|
12 | from Queue import Empty | |
13 |
|
13 | |||
14 | import nose.tools as nt |
|
14 | import nose.tools as nt | |
15 |
|
15 | |||
16 | from IPython.kernel import KernelManager |
|
16 | from IPython.kernel import KernelManager | |
17 |
|
17 | |||
18 | from IPython.utils.traitlets import ( |
|
18 | from IPython.utils.traitlets import ( | |
19 | HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any, |
|
19 | HasTraits, TraitError, Bool, Unicode, Dict, Integer, List, Enum, Any, | |
20 | ) |
|
20 | ) | |
21 |
|
21 | |||
|
22 | from .utils import TIMEOUT, start_global_kernel, flush_channels, execute | |||
|
23 | ||||
22 | #----------------------------------------------------------------------------- |
|
24 | #----------------------------------------------------------------------------- | |
23 | # Global setup and utilities |
|
25 | # Globals | |
24 | #----------------------------------------------------------------------------- |
|
26 | #----------------------------------------------------------------------------- | |
25 |
|
27 | KC = None | ||
26 | STARTUP_TIMEOUT = 60 |
|
|||
27 | TIMEOUT = 15 |
|
|||
28 |
|
28 | |||
29 | def setup(): |
|
29 | def setup(): | |
30 |
global |
|
30 | global KC | |
31 | KM = KernelManager() |
|
31 | KC = start_global_kernel() | |
32 | KM.start_kernel(stdout=PIPE, stderr=PIPE) |
|
|||
33 | KC = KM.client() |
|
|||
34 | KC.start_channels() |
|
|||
35 |
|
||||
36 | # wait for kernel to be ready |
|
|||
37 | try: |
|
|||
38 | msg = KC.iopub_channel.get_msg(block=True, timeout=STARTUP_TIMEOUT) |
|
|||
39 | except Empty: |
|
|||
40 | pass |
|
|||
41 | msg_id = KC.kernel_info() |
|
|||
42 | KC.get_shell_msg(block=True, timeout=STARTUP_TIMEOUT) |
|
|||
43 | flush_channels() |
|
|||
44 |
|
||||
45 |
|
||||
46 | def teardown(): |
|
|||
47 | KC.stop_channels() |
|
|||
48 | KM.shutdown_kernel() |
|
|||
49 |
|
||||
50 |
|
||||
51 | def flush_channels(kc=None): |
|
|||
52 | """flush any messages waiting on the queue""" |
|
|||
53 | if kc is None: |
|
|||
54 | kc = KC |
|
|||
55 | for channel in (kc.shell_channel, kc.iopub_channel): |
|
|||
56 | while True: |
|
|||
57 | try: |
|
|||
58 | msg = channel.get_msg(block=True, timeout=0.1) |
|
|||
59 | except Empty: |
|
|||
60 | break |
|
|||
61 | else: |
|
|||
62 | validate_message(msg) |
|
|||
63 |
|
||||
64 |
|
||||
65 | def execute(code='', kc=None, **kwargs): |
|
|||
66 | """wrapper for doing common steps for validating an execution request""" |
|
|||
67 | if kc is None: |
|
|||
68 | kc = KC |
|
|||
69 | msg_id = kc.execute(code=code, **kwargs) |
|
|||
70 | reply = kc.get_shell_msg(timeout=TIMEOUT) |
|
|||
71 | validate_message(reply, 'execute_reply', msg_id) |
|
|||
72 | busy = kc.get_iopub_msg(timeout=TIMEOUT) |
|
|||
73 | validate_message(busy, 'status', msg_id) |
|
|||
74 | nt.assert_equal(busy['content']['execution_state'], 'busy') |
|
|||
75 |
|
||||
76 | if not kwargs.get('silent'): |
|
|||
77 | pyin = kc.get_iopub_msg(timeout=TIMEOUT) |
|
|||
78 | validate_message(pyin, 'pyin', msg_id) |
|
|||
79 | nt.assert_equal(pyin['content']['code'], code) |
|
|||
80 |
|
||||
81 | return msg_id, reply['content'] |
|
|||
82 |
|
32 | |||
83 | #----------------------------------------------------------------------------- |
|
33 | #----------------------------------------------------------------------------- | |
84 |
# M |
|
34 | # Message Spec References | |
85 | #----------------------------------------------------------------------------- |
|
35 | #----------------------------------------------------------------------------- | |
86 |
|
36 | |||
87 |
|
||||
88 | class Reference(HasTraits): |
|
37 | class Reference(HasTraits): | |
89 |
|
38 | |||
90 | """ |
|
39 | """ | |
91 | Base class for message spec specification testing. |
|
40 | Base class for message spec specification testing. | |
92 |
|
41 | |||
93 | This class is the core of the message specification test. The |
|
42 | This class is the core of the message specification test. The | |
94 | idea is that child classes implement trait attributes for each |
|
43 | idea is that child classes implement trait attributes for each | |
95 | message keys, so that message keys can be tested against these |
|
44 | message keys, so that message keys can be tested against these | |
96 | traits using :meth:`check` method. |
|
45 | traits using :meth:`check` method. | |
97 |
|
46 | |||
98 | """ |
|
47 | """ | |
99 |
|
48 | |||
100 | def check(self, d): |
|
49 | def check(self, d): | |
101 | """validate a dict against our traits""" |
|
50 | """validate a dict against our traits""" | |
102 | for key in self.trait_names(): |
|
51 | for key in self.trait_names(): | |
103 | nt.assert_in(key, d) |
|
52 | nt.assert_in(key, d) | |
104 | # FIXME: always allow None, probably not a good idea |
|
53 | # FIXME: always allow None, probably not a good idea | |
105 | if d[key] is None: |
|
54 | if d[key] is None: | |
106 | continue |
|
55 | continue | |
107 | try: |
|
56 | try: | |
108 | setattr(self, key, d[key]) |
|
57 | setattr(self, key, d[key]) | |
109 | except TraitError as e: |
|
58 | except TraitError as e: | |
110 | nt.assert_true(False, str(e)) |
|
59 | nt.assert_true(False, str(e)) | |
111 |
|
60 | |||
112 |
|
61 | |||
113 | class RMessage(Reference): |
|
62 | class RMessage(Reference): | |
114 | msg_id = Unicode() |
|
63 | msg_id = Unicode() | |
115 | msg_type = Unicode() |
|
64 | msg_type = Unicode() | |
116 | header = Dict() |
|
65 | header = Dict() | |
117 | parent_header = Dict() |
|
66 | parent_header = Dict() | |
118 | content = Dict() |
|
67 | content = Dict() | |
119 |
|
68 | |||
120 | class RHeader(Reference): |
|
69 | class RHeader(Reference): | |
121 | msg_id = Unicode() |
|
70 | msg_id = Unicode() | |
122 | msg_type = Unicode() |
|
71 | msg_type = Unicode() | |
123 | session = Unicode() |
|
72 | session = Unicode() | |
124 | username = Unicode() |
|
73 | username = Unicode() | |
125 |
|
74 | |||
126 | class RContent(Reference): |
|
75 | class RContent(Reference): | |
127 | status = Enum((u'ok', u'error')) |
|
76 | status = Enum((u'ok', u'error')) | |
128 |
|
77 | |||
129 |
|
78 | |||
130 | class ExecuteReply(Reference): |
|
79 | class ExecuteReply(Reference): | |
131 | execution_count = Integer() |
|
80 | execution_count = Integer() | |
132 | status = Enum((u'ok', u'error')) |
|
81 | status = Enum((u'ok', u'error')) | |
133 |
|
82 | |||
134 | def check(self, d): |
|
83 | def check(self, d): | |
135 | Reference.check(self, d) |
|
84 | Reference.check(self, d) | |
136 | if d['status'] == 'ok': |
|
85 | if d['status'] == 'ok': | |
137 | ExecuteReplyOkay().check(d) |
|
86 | ExecuteReplyOkay().check(d) | |
138 | elif d['status'] == 'error': |
|
87 | elif d['status'] == 'error': | |
139 | ExecuteReplyError().check(d) |
|
88 | ExecuteReplyError().check(d) | |
140 |
|
89 | |||
141 |
|
90 | |||
142 | class ExecuteReplyOkay(Reference): |
|
91 | class ExecuteReplyOkay(Reference): | |
143 | payload = List(Dict) |
|
92 | payload = List(Dict) | |
144 | user_variables = Dict() |
|
93 | user_variables = Dict() | |
145 | user_expressions = Dict() |
|
94 | user_expressions = Dict() | |
146 |
|
95 | |||
147 |
|
96 | |||
148 | class ExecuteReplyError(Reference): |
|
97 | class ExecuteReplyError(Reference): | |
149 | ename = Unicode() |
|
98 | ename = Unicode() | |
150 | evalue = Unicode() |
|
99 | evalue = Unicode() | |
151 | traceback = List(Unicode) |
|
100 | traceback = List(Unicode) | |
152 |
|
101 | |||
153 |
|
102 | |||
154 | class OInfoReply(Reference): |
|
103 | class OInfoReply(Reference): | |
155 | name = Unicode() |
|
104 | name = Unicode() | |
156 | found = Bool() |
|
105 | found = Bool() | |
157 | ismagic = Bool() |
|
106 | ismagic = Bool() | |
158 | isalias = Bool() |
|
107 | isalias = Bool() | |
159 | namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive')) |
|
108 | namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive')) | |
160 | type_name = Unicode() |
|
109 | type_name = Unicode() | |
161 | string_form = Unicode() |
|
110 | string_form = Unicode() | |
162 | base_class = Unicode() |
|
111 | base_class = Unicode() | |
163 | length = Integer() |
|
112 | length = Integer() | |
164 | file = Unicode() |
|
113 | file = Unicode() | |
165 | definition = Unicode() |
|
114 | definition = Unicode() | |
166 | argspec = Dict() |
|
115 | argspec = Dict() | |
167 | init_definition = Unicode() |
|
116 | init_definition = Unicode() | |
168 | docstring = Unicode() |
|
117 | docstring = Unicode() | |
169 | init_docstring = Unicode() |
|
118 | init_docstring = Unicode() | |
170 | class_docstring = Unicode() |
|
119 | class_docstring = Unicode() | |
171 | call_def = Unicode() |
|
120 | call_def = Unicode() | |
172 | call_docstring = Unicode() |
|
121 | call_docstring = Unicode() | |
173 | source = Unicode() |
|
122 | source = Unicode() | |
174 |
|
123 | |||
175 | def check(self, d): |
|
124 | def check(self, d): | |
176 | Reference.check(self, d) |
|
125 | Reference.check(self, d) | |
177 | if d['argspec'] is not None: |
|
126 | if d['argspec'] is not None: | |
178 | ArgSpec().check(d['argspec']) |
|
127 | ArgSpec().check(d['argspec']) | |
179 |
|
128 | |||
180 |
|
129 | |||
181 | class ArgSpec(Reference): |
|
130 | class ArgSpec(Reference): | |
182 | args = List(Unicode) |
|
131 | args = List(Unicode) | |
183 | varargs = Unicode() |
|
132 | varargs = Unicode() | |
184 | varkw = Unicode() |
|
133 | varkw = Unicode() | |
185 | defaults = List() |
|
134 | defaults = List() | |
186 |
|
135 | |||
187 |
|
136 | |||
188 | class Status(Reference): |
|
137 | class Status(Reference): | |
189 | execution_state = Enum((u'busy', u'idle', u'starting')) |
|
138 | execution_state = Enum((u'busy', u'idle', u'starting')) | |
190 |
|
139 | |||
191 |
|
140 | |||
192 | class CompleteReply(Reference): |
|
141 | class CompleteReply(Reference): | |
193 | matches = List(Unicode) |
|
142 | matches = List(Unicode) | |
194 |
|
143 | |||
195 |
|
144 | |||
196 | def Version(num, trait=Integer): |
|
145 | def Version(num, trait=Integer): | |
197 | return List(trait, default_value=[0] * num, minlen=num, maxlen=num) |
|
146 | return List(trait, default_value=[0] * num, minlen=num, maxlen=num) | |
198 |
|
147 | |||
199 |
|
148 | |||
200 | class KernelInfoReply(Reference): |
|
149 | class KernelInfoReply(Reference): | |
201 |
|
150 | |||
202 | protocol_version = Version(2) |
|
151 | protocol_version = Version(2) | |
203 | ipython_version = Version(4, Any) |
|
152 | ipython_version = Version(4, Any) | |
204 | language_version = Version(3) |
|
153 | language_version = Version(3) | |
205 | language = Unicode() |
|
154 | language = Unicode() | |
206 |
|
155 | |||
207 | def _ipython_version_changed(self, name, old, new): |
|
156 | def _ipython_version_changed(self, name, old, new): | |
208 | for v in new: |
|
157 | for v in new: | |
209 | assert isinstance(v, int) or isinstance(v, basestring), \ |
|
158 | assert isinstance(v, int) or isinstance(v, basestring), \ | |
210 | 'expected int or string as version component, got {0!r}'.format(v) |
|
159 | 'expected int or string as version component, got {0!r}'.format(v) | |
211 |
|
160 | |||
212 |
|
161 | |||
213 | # IOPub messages |
|
162 | # IOPub messages | |
214 |
|
163 | |||
215 | class PyIn(Reference): |
|
164 | class PyIn(Reference): | |
216 | code = Unicode() |
|
165 | code = Unicode() | |
217 | execution_count = Integer() |
|
166 | execution_count = Integer() | |
218 |
|
167 | |||
219 |
|
168 | |||
220 | PyErr = ExecuteReplyError |
|
169 | PyErr = ExecuteReplyError | |
221 |
|
170 | |||
222 |
|
171 | |||
223 | class Stream(Reference): |
|
172 | class Stream(Reference): | |
224 | name = Enum((u'stdout', u'stderr')) |
|
173 | name = Enum((u'stdout', u'stderr')) | |
225 | data = Unicode() |
|
174 | data = Unicode() | |
226 |
|
175 | |||
227 |
|
176 | |||
228 | mime_pat = re.compile(r'\w+/\w+') |
|
177 | mime_pat = re.compile(r'\w+/\w+') | |
229 |
|
178 | |||
230 | class DisplayData(Reference): |
|
179 | class DisplayData(Reference): | |
231 | source = Unicode() |
|
180 | source = Unicode() | |
232 | metadata = Dict() |
|
181 | metadata = Dict() | |
233 | data = Dict() |
|
182 | data = Dict() | |
234 | def _data_changed(self, name, old, new): |
|
183 | def _data_changed(self, name, old, new): | |
235 | for k,v in new.iteritems(): |
|
184 | for k,v in new.iteritems(): | |
236 | assert mime_pat.match(k) |
|
185 | assert mime_pat.match(k) | |
237 | nt.assert_is_instance(v, basestring) |
|
186 | nt.assert_is_instance(v, basestring) | |
238 |
|
187 | |||
239 |
|
188 | |||
240 | class PyOut(Reference): |
|
189 | class PyOut(Reference): | |
241 | execution_count = Integer() |
|
190 | execution_count = Integer() | |
242 | data = Dict() |
|
191 | data = Dict() | |
243 | def _data_changed(self, name, old, new): |
|
192 | def _data_changed(self, name, old, new): | |
244 | for k,v in new.iteritems(): |
|
193 | for k,v in new.iteritems(): | |
245 | assert mime_pat.match(k) |
|
194 | assert mime_pat.match(k) | |
246 | nt.assert_is_instance(v, basestring) |
|
195 | nt.assert_is_instance(v, basestring) | |
247 |
|
196 | |||
248 |
|
197 | |||
249 | references = { |
|
198 | references = { | |
250 | 'execute_reply' : ExecuteReply(), |
|
199 | 'execute_reply' : ExecuteReply(), | |
251 | 'object_info_reply' : OInfoReply(), |
|
200 | 'object_info_reply' : OInfoReply(), | |
252 | 'status' : Status(), |
|
201 | 'status' : Status(), | |
253 | 'complete_reply' : CompleteReply(), |
|
202 | 'complete_reply' : CompleteReply(), | |
254 | 'kernel_info_reply': KernelInfoReply(), |
|
203 | 'kernel_info_reply': KernelInfoReply(), | |
255 | 'pyin' : PyIn(), |
|
204 | 'pyin' : PyIn(), | |
256 | 'pyout' : PyOut(), |
|
205 | 'pyout' : PyOut(), | |
257 | 'pyerr' : PyErr(), |
|
206 | 'pyerr' : PyErr(), | |
258 | 'stream' : Stream(), |
|
207 | 'stream' : Stream(), | |
259 | 'display_data' : DisplayData(), |
|
208 | 'display_data' : DisplayData(), | |
260 | } |
|
209 | } | |
261 | """ |
|
210 | """ | |
262 | Specifications of `content` part of the reply messages. |
|
211 | Specifications of `content` part of the reply messages. | |
263 | """ |
|
212 | """ | |
264 |
|
213 | |||
265 |
|
214 | |||
266 | def validate_message(msg, msg_type=None, parent=None): |
|
215 | def validate_message(msg, msg_type=None, parent=None): | |
267 | """validate a message |
|
216 | """validate a message | |
268 |
|
217 | |||
269 | This is a generator, and must be iterated through to actually |
|
218 | This is a generator, and must be iterated through to actually | |
270 | trigger each test. |
|
219 | trigger each test. | |
271 |
|
220 | |||
272 | If msg_type and/or parent are given, the msg_type and/or parent msg_id |
|
221 | If msg_type and/or parent are given, the msg_type and/or parent msg_id | |
273 | are compared with the given values. |
|
222 | are compared with the given values. | |
274 | """ |
|
223 | """ | |
275 | RMessage().check(msg) |
|
224 | RMessage().check(msg) | |
276 | if msg_type: |
|
225 | if msg_type: | |
277 | nt.assert_equal(msg['msg_type'], msg_type) |
|
226 | nt.assert_equal(msg['msg_type'], msg_type) | |
278 | if parent: |
|
227 | if parent: | |
279 | nt.assert_equal(msg['parent_header']['msg_id'], parent) |
|
228 | nt.assert_equal(msg['parent_header']['msg_id'], parent) | |
280 | content = msg['content'] |
|
229 | content = msg['content'] | |
281 | ref = references[msg['msg_type']] |
|
230 | ref = references[msg['msg_type']] | |
282 | ref.check(content) |
|
231 | ref.check(content) | |
283 |
|
232 | |||
284 |
|
233 | |||
285 | #----------------------------------------------------------------------------- |
|
234 | #----------------------------------------------------------------------------- | |
286 | # Tests |
|
235 | # Tests | |
287 | #----------------------------------------------------------------------------- |
|
236 | #----------------------------------------------------------------------------- | |
288 |
|
237 | |||
289 | # Shell channel |
|
238 | # Shell channel | |
290 |
|
239 | |||
291 | def test_execute(): |
|
240 | def test_execute(): | |
292 | flush_channels() |
|
241 | flush_channels() | |
293 |
|
242 | |||
294 | msg_id = KC.execute(code='x=1') |
|
243 | msg_id = KC.execute(code='x=1') | |
295 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
244 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
296 | validate_message(reply, 'execute_reply', msg_id) |
|
245 | validate_message(reply, 'execute_reply', msg_id) | |
297 |
|
246 | |||
298 |
|
247 | |||
299 | def test_execute_silent(): |
|
248 | def test_execute_silent(): | |
300 | flush_channels() |
|
249 | flush_channels() | |
301 | msg_id, reply = execute(code='x=1', silent=True) |
|
250 | msg_id, reply = execute(code='x=1', silent=True) | |
302 |
|
251 | |||
303 | # flush status=idle |
|
252 | # flush status=idle | |
304 | status = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
253 | status = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
305 | validate_message(status, 'status', msg_id) |
|
254 | validate_message(status, 'status', msg_id) | |
306 | nt.assert_equal(status['content']['execution_state'], 'idle') |
|
255 | nt.assert_equal(status['content']['execution_state'], 'idle') | |
307 |
|
256 | |||
308 | nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) |
|
257 | nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) | |
309 | count = reply['execution_count'] |
|
258 | count = reply['execution_count'] | |
310 |
|
259 | |||
311 | msg_id, reply = execute(code='x=2', silent=True) |
|
260 | msg_id, reply = execute(code='x=2', silent=True) | |
312 |
|
261 | |||
313 | # flush status=idle |
|
262 | # flush status=idle | |
314 | status = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
263 | status = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
315 | validate_message(status, 'status', msg_id) |
|
264 | validate_message(status, 'status', msg_id) | |
316 | nt.assert_equal(status['content']['execution_state'], 'idle') |
|
265 | nt.assert_equal(status['content']['execution_state'], 'idle') | |
317 |
|
266 | |||
318 | nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) |
|
267 | nt.assert_raises(Empty, KC.iopub_channel.get_msg, timeout=0.1) | |
319 | count_2 = reply['execution_count'] |
|
268 | count_2 = reply['execution_count'] | |
320 | nt.assert_equal(count_2, count) |
|
269 | nt.assert_equal(count_2, count) | |
321 |
|
270 | |||
322 |
|
271 | |||
323 | def test_execute_error(): |
|
272 | def test_execute_error(): | |
324 | flush_channels() |
|
273 | flush_channels() | |
325 |
|
274 | |||
326 | msg_id, reply = execute(code='1/0') |
|
275 | msg_id, reply = execute(code='1/0') | |
327 | nt.assert_equal(reply['status'], 'error') |
|
276 | nt.assert_equal(reply['status'], 'error') | |
328 | nt.assert_equal(reply['ename'], 'ZeroDivisionError') |
|
277 | nt.assert_equal(reply['ename'], 'ZeroDivisionError') | |
329 |
|
278 | |||
330 | pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
279 | pyerr = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
331 | validate_message(pyerr, 'pyerr', msg_id) |
|
280 | validate_message(pyerr, 'pyerr', msg_id) | |
332 |
|
281 | |||
333 |
|
282 | |||
334 | def test_execute_inc(): |
|
283 | def test_execute_inc(): | |
335 | """execute request should increment execution_count""" |
|
284 | """execute request should increment execution_count""" | |
336 | flush_channels() |
|
285 | flush_channels() | |
337 |
|
286 | |||
338 | msg_id, reply = execute(code='x=1') |
|
287 | msg_id, reply = execute(code='x=1') | |
339 | count = reply['execution_count'] |
|
288 | count = reply['execution_count'] | |
340 |
|
289 | |||
341 | flush_channels() |
|
290 | flush_channels() | |
342 |
|
291 | |||
343 | msg_id, reply = execute(code='x=2') |
|
292 | msg_id, reply = execute(code='x=2') | |
344 | count_2 = reply['execution_count'] |
|
293 | count_2 = reply['execution_count'] | |
345 | nt.assert_equal(count_2, count+1) |
|
294 | nt.assert_equal(count_2, count+1) | |
346 |
|
295 | |||
347 |
|
296 | |||
348 | def test_user_variables(): |
|
297 | def test_user_variables(): | |
349 | flush_channels() |
|
298 | flush_channels() | |
350 |
|
299 | |||
351 | msg_id, reply = execute(code='x=1', user_variables=['x']) |
|
300 | msg_id, reply = execute(code='x=1', user_variables=['x']) | |
352 | user_variables = reply['user_variables'] |
|
301 | user_variables = reply['user_variables'] | |
353 | nt.assert_equal(user_variables, {u'x': { |
|
302 | nt.assert_equal(user_variables, {u'x': { | |
354 | u'status': u'ok', |
|
303 | u'status': u'ok', | |
355 | u'data': {u'text/plain': u'1'}, |
|
304 | u'data': {u'text/plain': u'1'}, | |
356 | u'metadata': {}, |
|
305 | u'metadata': {}, | |
357 | }}) |
|
306 | }}) | |
358 |
|
307 | |||
359 |
|
308 | |||
360 | def test_user_variables_fail(): |
|
309 | def test_user_variables_fail(): | |
361 | flush_channels() |
|
310 | flush_channels() | |
362 |
|
311 | |||
363 | msg_id, reply = execute(code='x=1', user_variables=['nosuchname']) |
|
312 | msg_id, reply = execute(code='x=1', user_variables=['nosuchname']) | |
364 | user_variables = reply['user_variables'] |
|
313 | user_variables = reply['user_variables'] | |
365 | foo = user_variables['nosuchname'] |
|
314 | foo = user_variables['nosuchname'] | |
366 | nt.assert_equal(foo['status'], 'error') |
|
315 | nt.assert_equal(foo['status'], 'error') | |
367 | nt.assert_equal(foo['ename'], 'KeyError') |
|
316 | nt.assert_equal(foo['ename'], 'KeyError') | |
368 |
|
317 | |||
369 |
|
318 | |||
370 | def test_user_expressions(): |
|
319 | def test_user_expressions(): | |
371 | flush_channels() |
|
320 | flush_channels() | |
372 |
|
321 | |||
373 | msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1')) |
|
322 | msg_id, reply = execute(code='x=1', user_expressions=dict(foo='x+1')) | |
374 | user_expressions = reply['user_expressions'] |
|
323 | user_expressions = reply['user_expressions'] | |
375 | nt.assert_equal(user_expressions, {u'foo': { |
|
324 | nt.assert_equal(user_expressions, {u'foo': { | |
376 | u'status': u'ok', |
|
325 | u'status': u'ok', | |
377 | u'data': {u'text/plain': u'2'}, |
|
326 | u'data': {u'text/plain': u'2'}, | |
378 | u'metadata': {}, |
|
327 | u'metadata': {}, | |
379 | }}) |
|
328 | }}) | |
380 |
|
329 | |||
381 |
|
330 | |||
382 | def test_user_expressions_fail(): |
|
331 | def test_user_expressions_fail(): | |
383 | flush_channels() |
|
332 | flush_channels() | |
384 |
|
333 | |||
385 | msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname')) |
|
334 | msg_id, reply = execute(code='x=0', user_expressions=dict(foo='nosuchname')) | |
386 | user_expressions = reply['user_expressions'] |
|
335 | user_expressions = reply['user_expressions'] | |
387 | foo = user_expressions['foo'] |
|
336 | foo = user_expressions['foo'] | |
388 | nt.assert_equal(foo['status'], 'error') |
|
337 | nt.assert_equal(foo['status'], 'error') | |
389 | nt.assert_equal(foo['ename'], 'NameError') |
|
338 | nt.assert_equal(foo['ename'], 'NameError') | |
390 |
|
339 | |||
391 |
|
340 | |||
392 | def test_oinfo(): |
|
341 | def test_oinfo(): | |
393 | flush_channels() |
|
342 | flush_channels() | |
394 |
|
343 | |||
395 | msg_id = KC.object_info('a') |
|
344 | msg_id = KC.object_info('a') | |
396 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
345 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
397 | validate_message(reply, 'object_info_reply', msg_id) |
|
346 | validate_message(reply, 'object_info_reply', msg_id) | |
398 |
|
347 | |||
399 |
|
348 | |||
400 | def test_oinfo_found(): |
|
349 | def test_oinfo_found(): | |
401 | flush_channels() |
|
350 | flush_channels() | |
402 |
|
351 | |||
403 | msg_id, reply = execute(code='a=5') |
|
352 | msg_id, reply = execute(code='a=5') | |
404 |
|
353 | |||
405 | msg_id = KC.object_info('a') |
|
354 | msg_id = KC.object_info('a') | |
406 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
355 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
407 | validate_message(reply, 'object_info_reply', msg_id) |
|
356 | validate_message(reply, 'object_info_reply', msg_id) | |
408 | content = reply['content'] |
|
357 | content = reply['content'] | |
409 | assert content['found'] |
|
358 | assert content['found'] | |
410 | argspec = content['argspec'] |
|
359 | argspec = content['argspec'] | |
411 | nt.assert_is(argspec, None) |
|
360 | nt.assert_is(argspec, None) | |
412 |
|
361 | |||
413 |
|
362 | |||
414 | def test_oinfo_detail(): |
|
363 | def test_oinfo_detail(): | |
415 | flush_channels() |
|
364 | flush_channels() | |
416 |
|
365 | |||
417 | msg_id, reply = execute(code='ip=get_ipython()') |
|
366 | msg_id, reply = execute(code='ip=get_ipython()') | |
418 |
|
367 | |||
419 | msg_id = KC.object_info('ip.object_inspect', detail_level=2) |
|
368 | msg_id = KC.object_info('ip.object_inspect', detail_level=2) | |
420 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
369 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
421 | validate_message(reply, 'object_info_reply', msg_id) |
|
370 | validate_message(reply, 'object_info_reply', msg_id) | |
422 | content = reply['content'] |
|
371 | content = reply['content'] | |
423 | assert content['found'] |
|
372 | assert content['found'] | |
424 | argspec = content['argspec'] |
|
373 | argspec = content['argspec'] | |
425 | nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec) |
|
374 | nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec) | |
426 | nt.assert_equal(argspec['defaults'], [0]) |
|
375 | nt.assert_equal(argspec['defaults'], [0]) | |
427 |
|
376 | |||
428 |
|
377 | |||
429 | def test_oinfo_not_found(): |
|
378 | def test_oinfo_not_found(): | |
430 | flush_channels() |
|
379 | flush_channels() | |
431 |
|
380 | |||
432 | msg_id = KC.object_info('dne') |
|
381 | msg_id = KC.object_info('dne') | |
433 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
382 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
434 | validate_message(reply, 'object_info_reply', msg_id) |
|
383 | validate_message(reply, 'object_info_reply', msg_id) | |
435 | content = reply['content'] |
|
384 | content = reply['content'] | |
436 | nt.assert_false(content['found']) |
|
385 | nt.assert_false(content['found']) | |
437 |
|
386 | |||
438 |
|
387 | |||
439 | def test_complete(): |
|
388 | def test_complete(): | |
440 | flush_channels() |
|
389 | flush_channels() | |
441 |
|
390 | |||
442 | msg_id, reply = execute(code="alpha = albert = 5") |
|
391 | msg_id, reply = execute(code="alpha = albert = 5") | |
443 |
|
392 | |||
444 | msg_id = KC.complete('al', 'al', 2) |
|
393 | msg_id = KC.complete('al', 'al', 2) | |
445 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
394 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
446 | validate_message(reply, 'complete_reply', msg_id) |
|
395 | validate_message(reply, 'complete_reply', msg_id) | |
447 | matches = reply['content']['matches'] |
|
396 | matches = reply['content']['matches'] | |
448 | for name in ('alpha', 'albert'): |
|
397 | for name in ('alpha', 'albert'): | |
449 | nt.assert_in(name, matches) |
|
398 | nt.assert_in(name, matches) | |
450 |
|
399 | |||
451 |
|
400 | |||
452 | def test_kernel_info_request(): |
|
401 | def test_kernel_info_request(): | |
453 | flush_channels() |
|
402 | flush_channels() | |
454 |
|
403 | |||
455 | msg_id = KC.kernel_info() |
|
404 | msg_id = KC.kernel_info() | |
456 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
405 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
457 | validate_message(reply, 'kernel_info_reply', msg_id) |
|
406 | validate_message(reply, 'kernel_info_reply', msg_id) | |
458 |
|
407 | |||
459 |
|
408 | |||
460 | # IOPub channel |
|
409 | # IOPub channel | |
461 |
|
410 | |||
462 |
|
411 | |||
463 | def test_stream(): |
|
412 | def test_stream(): | |
464 | flush_channels() |
|
413 | flush_channels() | |
465 |
|
414 | |||
466 | msg_id, reply = execute("print('hi')") |
|
415 | msg_id, reply = execute("print('hi')") | |
467 |
|
416 | |||
468 | stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
417 | stdout = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
469 | validate_message(stdout, 'stream', msg_id) |
|
418 | validate_message(stdout, 'stream', msg_id) | |
470 | content = stdout['content'] |
|
419 | content = stdout['content'] | |
471 | nt.assert_equal(content['name'], u'stdout') |
|
420 | nt.assert_equal(content['name'], u'stdout') | |
472 | nt.assert_equal(content['data'], u'hi\n') |
|
421 | nt.assert_equal(content['data'], u'hi\n') | |
473 |
|
422 | |||
474 |
|
423 | |||
475 | def test_display_data(): |
|
424 | def test_display_data(): | |
476 | flush_channels() |
|
425 | flush_channels() | |
477 |
|
426 | |||
478 | msg_id, reply = execute("from IPython.core.display import display; display(1)") |
|
427 | msg_id, reply = execute("from IPython.core.display import display; display(1)") | |
479 |
|
428 | |||
480 | display = KC.iopub_channel.get_msg(timeout=TIMEOUT) |
|
429 | display = KC.iopub_channel.get_msg(timeout=TIMEOUT) | |
481 | validate_message(display, 'display_data', parent=msg_id) |
|
430 | validate_message(display, 'display_data', parent=msg_id) | |
482 | data = display['content']['data'] |
|
431 | data = display['content']['data'] | |
483 | nt.assert_equal(data['text/plain'], u'1') |
|
432 | nt.assert_equal(data['text/plain'], u'1') | |
484 |
|
433 |
@@ -1,85 +1,78 b'' | |||||
1 | """Tests for the notebook kernel and session manager.""" |
|
1 | """Tests for the notebook kernel and session manager.""" | |
2 |
|
2 | |||
3 | from subprocess import PIPE |
|
3 | from subprocess import PIPE | |
4 | import time |
|
4 | import time | |
5 | from unittest import TestCase |
|
5 | from unittest import TestCase | |
6 |
|
6 | |||
7 | from IPython.testing import decorators as dec |
|
7 | from IPython.testing import decorators as dec | |
8 |
|
8 | |||
9 | from IPython.config.loader import Config |
|
9 | from IPython.config.loader import Config | |
10 | from IPython.utils.localinterfaces import LOCALHOST |
|
10 | from IPython.utils.localinterfaces import LOCALHOST | |
11 | from IPython.kernel import KernelManager |
|
11 | from IPython.kernel import KernelManager | |
12 | from IPython.kernel.multikernelmanager import MultiKernelManager |
|
12 | from IPython.kernel.multikernelmanager import MultiKernelManager | |
13 |
|
13 | |||
14 | class TestKernelManager(TestCase): |
|
14 | class TestKernelManager(TestCase): | |
15 |
|
15 | |||
16 | def _get_tcp_km(self): |
|
16 | def _get_tcp_km(self): | |
17 | c = Config() |
|
17 | c = Config() | |
18 | km = MultiKernelManager(config=c) |
|
18 | km = MultiKernelManager(config=c) | |
19 | return km |
|
19 | return km | |
20 |
|
20 | |||
21 | def _get_ipc_km(self): |
|
21 | def _get_ipc_km(self): | |
22 | c = Config() |
|
22 | c = Config() | |
23 | c.KernelManager.transport = 'ipc' |
|
23 | c.KernelManager.transport = 'ipc' | |
24 | c.KernelManager.ip = 'test' |
|
24 | c.KernelManager.ip = 'test' | |
25 | km = MultiKernelManager(config=c) |
|
25 | km = MultiKernelManager(config=c) | |
26 | return km |
|
26 | return km | |
27 |
|
27 | |||
28 | def _run_lifecycle(self, km): |
|
28 | def _run_lifecycle(self, km): | |
29 | kid = km.start_kernel(stdout=PIPE, stderr=PIPE) |
|
29 | kid = km.start_kernel(stdout=PIPE, stderr=PIPE) | |
30 | self.assertTrue(km.is_alive(kid)) |
|
30 | self.assertTrue(km.is_alive(kid)) | |
31 | self.assertTrue(kid in km) |
|
31 | self.assertTrue(kid in km) | |
32 | self.assertTrue(kid in km.list_kernel_ids()) |
|
32 | self.assertTrue(kid in km.list_kernel_ids()) | |
33 | self.assertEqual(len(km),1) |
|
33 | self.assertEqual(len(km),1) | |
34 | km.restart_kernel(kid) |
|
34 | km.restart_kernel(kid, now=True) | |
35 | self.assertTrue(km.is_alive(kid)) |
|
35 | self.assertTrue(km.is_alive(kid)) | |
36 | self.assertTrue(kid in km.list_kernel_ids()) |
|
36 | self.assertTrue(kid in km.list_kernel_ids()) | |
37 | # We need a delay here to give the restarting kernel a chance to |
|
|||
38 | # restart. Otherwise, the interrupt will kill it, causing the test |
|
|||
39 | # suite to hang. The reason it *hangs* is that the shutdown |
|
|||
40 | # message for the restart sometimes hasn't been sent to the kernel. |
|
|||
41 | # Because linger is oo on the shell channel, the context can't |
|
|||
42 | # close until the message is sent to the kernel, which is not dead. |
|
|||
43 | time.sleep(1.0) |
|
|||
44 | km.interrupt_kernel(kid) |
|
37 | km.interrupt_kernel(kid) | |
45 | k = km.get_kernel(kid) |
|
38 | k = km.get_kernel(kid) | |
46 | self.assertTrue(isinstance(k, KernelManager)) |
|
39 | self.assertTrue(isinstance(k, KernelManager)) | |
47 | km.shutdown_kernel(kid) |
|
40 | km.shutdown_kernel(kid, now=True) | |
48 | self.assertTrue(not kid in km) |
|
41 | self.assertTrue(not kid in km) | |
49 |
|
42 | |||
50 | def _run_cinfo(self, km, transport, ip): |
|
43 | def _run_cinfo(self, km, transport, ip): | |
51 | kid = km.start_kernel(stdout=PIPE, stderr=PIPE) |
|
44 | kid = km.start_kernel(stdout=PIPE, stderr=PIPE) | |
52 | k = km.get_kernel(kid) |
|
45 | k = km.get_kernel(kid) | |
53 | cinfo = km.get_connection_info(kid) |
|
46 | cinfo = km.get_connection_info(kid) | |
54 | self.assertEqual(transport, cinfo['transport']) |
|
47 | self.assertEqual(transport, cinfo['transport']) | |
55 | self.assertEqual(ip, cinfo['ip']) |
|
48 | self.assertEqual(ip, cinfo['ip']) | |
56 | self.assertTrue('stdin_port' in cinfo) |
|
49 | self.assertTrue('stdin_port' in cinfo) | |
57 | self.assertTrue('iopub_port' in cinfo) |
|
50 | self.assertTrue('iopub_port' in cinfo) | |
58 | stream = km.connect_iopub(kid) |
|
51 | stream = km.connect_iopub(kid) | |
59 | stream.close() |
|
52 | stream.close() | |
60 | self.assertTrue('shell_port' in cinfo) |
|
53 | self.assertTrue('shell_port' in cinfo) | |
61 | stream = km.connect_shell(kid) |
|
54 | stream = km.connect_shell(kid) | |
62 | stream.close() |
|
55 | stream.close() | |
63 | self.assertTrue('hb_port' in cinfo) |
|
56 | self.assertTrue('hb_port' in cinfo) | |
64 | stream = km.connect_hb(kid) |
|
57 | stream = km.connect_hb(kid) | |
65 | stream.close() |
|
58 | stream.close() | |
66 | km.shutdown_kernel(kid) |
|
59 | km.shutdown_kernel(kid, now=True) | |
67 |
|
60 | |||
68 | def test_tcp_lifecycle(self): |
|
61 | def test_tcp_lifecycle(self): | |
69 | km = self._get_tcp_km() |
|
62 | km = self._get_tcp_km() | |
70 | self._run_lifecycle(km) |
|
63 | self._run_lifecycle(km) | |
71 |
|
64 | |||
72 | def test_tcp_cinfo(self): |
|
65 | def test_tcp_cinfo(self): | |
73 | km = self._get_tcp_km() |
|
66 | km = self._get_tcp_km() | |
74 | self._run_cinfo(km, 'tcp', LOCALHOST) |
|
67 | self._run_cinfo(km, 'tcp', LOCALHOST) | |
75 |
|
68 | |||
76 | @dec.skip_win32 |
|
69 | @dec.skip_win32 | |
77 | def test_ipc_lifecycle(self): |
|
70 | def test_ipc_lifecycle(self): | |
78 | km = self._get_ipc_km() |
|
71 | km = self._get_ipc_km() | |
79 | self._run_lifecycle(km) |
|
72 | self._run_lifecycle(km) | |
80 |
|
73 | |||
81 | @dec.skip_win32 |
|
74 | @dec.skip_win32 | |
82 | def test_ipc_cinfo(self): |
|
75 | def test_ipc_cinfo(self): | |
83 | km = self._get_ipc_km() |
|
76 | km = self._get_ipc_km() | |
84 | self._run_cinfo(km, 'ipc', 'test') |
|
77 | self._run_cinfo(km, 'ipc', 'test') | |
85 |
|
78 |
General Comments 0
You need to be logged in to leave comments.
Login now