Show More
@@ -1,230 +1,230 b'' | |||||
1 | """Tornado handlers for kernels.""" |
|
1 | """Tornado handlers for kernels.""" | |
2 |
|
2 | |||
3 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
4 | # Distributed under the terms of the Modified BSD License. |
|
4 | # Distributed under the terms of the Modified BSD License. | |
5 |
|
5 | |||
6 | import json |
|
6 | import json | |
7 | import logging |
|
7 | import logging | |
8 | from tornado import web |
|
8 | from tornado import web | |
9 |
|
9 | |||
10 | from IPython.utils.jsonutil import date_default |
|
10 | from IPython.utils.jsonutil import date_default | |
11 | from IPython.utils.py3compat import string_types |
|
11 | from IPython.utils.py3compat import string_types | |
12 | from IPython.html.utils import url_path_join, url_escape |
|
12 | from IPython.html.utils import url_path_join, url_escape | |
13 |
|
13 | |||
14 | from ...base.handlers import IPythonHandler, json_errors |
|
14 | from ...base.handlers import IPythonHandler, json_errors | |
15 | from ...base.zmqhandlers import AuthenticatedZMQStreamHandler |
|
15 | from ...base.zmqhandlers import AuthenticatedZMQStreamHandler | |
16 |
|
16 | |||
17 | from IPython.core.release import kernel_protocol_version |
|
17 | from IPython.core.release import kernel_protocol_version | |
18 |
|
18 | |||
19 | class MainKernelHandler(IPythonHandler): |
|
19 | class MainKernelHandler(IPythonHandler): | |
20 |
|
20 | |||
21 | @web.authenticated |
|
21 | @web.authenticated | |
22 | @json_errors |
|
22 | @json_errors | |
23 | def get(self): |
|
23 | def get(self): | |
24 | km = self.kernel_manager |
|
24 | km = self.kernel_manager | |
25 | self.finish(json.dumps(km.list_kernels())) |
|
25 | self.finish(json.dumps(km.list_kernels())) | |
26 |
|
26 | |||
27 | @web.authenticated |
|
27 | @web.authenticated | |
28 | @json_errors |
|
28 | @json_errors | |
29 | def post(self): |
|
29 | def post(self): | |
|
30 | km = self.kernel_manager | |||
30 | model = self.get_json_body() |
|
31 | model = self.get_json_body() | |
31 | if model is None: |
|
32 | if model is None: | |
32 | raise web.HTTPError(400, "No JSON data provided") |
|
33 | model = { | |
33 | try: |
|
34 | 'name': km.default_kernel_name | |
34 | name = model['name'] |
|
35 | } | |
35 | except KeyError: |
|
36 | else: | |
36 | raise web.HTTPError(400, "Missing field in JSON data: name") |
|
37 | model.setdefault('name', km.default_kernel_name) | |
37 |
|
38 | |||
38 | km = self.kernel_manager |
|
39 | kernel_id = km.start_kernel(kernel_name=model['name']) | |
39 | kernel_id = km.start_kernel(kernel_name=name) |
|
|||
40 | model = km.kernel_model(kernel_id) |
|
40 | model = km.kernel_model(kernel_id) | |
41 | location = url_path_join(self.base_url, 'api', 'kernels', kernel_id) |
|
41 | location = url_path_join(self.base_url, 'api', 'kernels', kernel_id) | |
42 | self.set_header('Location', url_escape(location)) |
|
42 | self.set_header('Location', url_escape(location)) | |
43 | self.set_status(201) |
|
43 | self.set_status(201) | |
44 | self.finish(json.dumps(model)) |
|
44 | self.finish(json.dumps(model)) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | class KernelHandler(IPythonHandler): |
|
47 | class KernelHandler(IPythonHandler): | |
48 |
|
48 | |||
49 | SUPPORTED_METHODS = ('DELETE', 'GET') |
|
49 | SUPPORTED_METHODS = ('DELETE', 'GET') | |
50 |
|
50 | |||
51 | @web.authenticated |
|
51 | @web.authenticated | |
52 | @json_errors |
|
52 | @json_errors | |
53 | def get(self, kernel_id): |
|
53 | def get(self, kernel_id): | |
54 | km = self.kernel_manager |
|
54 | km = self.kernel_manager | |
55 | km._check_kernel_id(kernel_id) |
|
55 | km._check_kernel_id(kernel_id) | |
56 | model = km.kernel_model(kernel_id) |
|
56 | model = km.kernel_model(kernel_id) | |
57 | self.finish(json.dumps(model)) |
|
57 | self.finish(json.dumps(model)) | |
58 |
|
58 | |||
59 | @web.authenticated |
|
59 | @web.authenticated | |
60 | @json_errors |
|
60 | @json_errors | |
61 | def delete(self, kernel_id): |
|
61 | def delete(self, kernel_id): | |
62 | km = self.kernel_manager |
|
62 | km = self.kernel_manager | |
63 | km.shutdown_kernel(kernel_id) |
|
63 | km.shutdown_kernel(kernel_id) | |
64 | self.set_status(204) |
|
64 | self.set_status(204) | |
65 | self.finish() |
|
65 | self.finish() | |
66 |
|
66 | |||
67 |
|
67 | |||
68 | class KernelActionHandler(IPythonHandler): |
|
68 | class KernelActionHandler(IPythonHandler): | |
69 |
|
69 | |||
70 | @web.authenticated |
|
70 | @web.authenticated | |
71 | @json_errors |
|
71 | @json_errors | |
72 | def post(self, kernel_id, action): |
|
72 | def post(self, kernel_id, action): | |
73 | km = self.kernel_manager |
|
73 | km = self.kernel_manager | |
74 | if action == 'interrupt': |
|
74 | if action == 'interrupt': | |
75 | km.interrupt_kernel(kernel_id) |
|
75 | km.interrupt_kernel(kernel_id) | |
76 | self.set_status(204) |
|
76 | self.set_status(204) | |
77 | if action == 'restart': |
|
77 | if action == 'restart': | |
78 | km.restart_kernel(kernel_id) |
|
78 | km.restart_kernel(kernel_id) | |
79 | model = km.kernel_model(kernel_id) |
|
79 | model = km.kernel_model(kernel_id) | |
80 | self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_url, kernel_id)) |
|
80 | self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_url, kernel_id)) | |
81 | self.write(json.dumps(model)) |
|
81 | self.write(json.dumps(model)) | |
82 | self.finish() |
|
82 | self.finish() | |
83 |
|
83 | |||
84 |
|
84 | |||
85 | class ZMQChannelHandler(AuthenticatedZMQStreamHandler): |
|
85 | class ZMQChannelHandler(AuthenticatedZMQStreamHandler): | |
86 |
|
86 | |||
87 | def __repr__(self): |
|
87 | def __repr__(self): | |
88 | return "%s(%s)" % (self.__class__.__name__, getattr(self, 'kernel_id', 'uninitialized')) |
|
88 | return "%s(%s)" % (self.__class__.__name__, getattr(self, 'kernel_id', 'uninitialized')) | |
89 |
|
89 | |||
90 | def create_stream(self): |
|
90 | def create_stream(self): | |
91 | km = self.kernel_manager |
|
91 | km = self.kernel_manager | |
92 | meth = getattr(km, 'connect_%s' % self.channel) |
|
92 | meth = getattr(km, 'connect_%s' % self.channel) | |
93 | self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession) |
|
93 | self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession) | |
94 | # Create a kernel_info channel to query the kernel protocol version. |
|
94 | # Create a kernel_info channel to query the kernel protocol version. | |
95 | # This channel will be closed after the kernel_info reply is received. |
|
95 | # This channel will be closed after the kernel_info reply is received. | |
96 | self.kernel_info_channel = None |
|
96 | self.kernel_info_channel = None | |
97 | self.kernel_info_channel = km.connect_shell(self.kernel_id) |
|
97 | self.kernel_info_channel = km.connect_shell(self.kernel_id) | |
98 | self.kernel_info_channel.on_recv(self._handle_kernel_info_reply) |
|
98 | self.kernel_info_channel.on_recv(self._handle_kernel_info_reply) | |
99 | self._request_kernel_info() |
|
99 | self._request_kernel_info() | |
100 |
|
100 | |||
101 | def _request_kernel_info(self): |
|
101 | def _request_kernel_info(self): | |
102 | """send a request for kernel_info""" |
|
102 | """send a request for kernel_info""" | |
103 | self.log.debug("requesting kernel info") |
|
103 | self.log.debug("requesting kernel info") | |
104 | self.session.send(self.kernel_info_channel, "kernel_info_request") |
|
104 | self.session.send(self.kernel_info_channel, "kernel_info_request") | |
105 |
|
105 | |||
106 | def _handle_kernel_info_reply(self, msg): |
|
106 | def _handle_kernel_info_reply(self, msg): | |
107 | """process the kernel_info_reply |
|
107 | """process the kernel_info_reply | |
108 |
|
108 | |||
109 | enabling msg spec adaptation, if necessary |
|
109 | enabling msg spec adaptation, if necessary | |
110 | """ |
|
110 | """ | |
111 | idents,msg = self.session.feed_identities(msg) |
|
111 | idents,msg = self.session.feed_identities(msg) | |
112 | try: |
|
112 | try: | |
113 | msg = self.session.unserialize(msg) |
|
113 | msg = self.session.unserialize(msg) | |
114 | except: |
|
114 | except: | |
115 | self.log.error("Bad kernel_info reply", exc_info=True) |
|
115 | self.log.error("Bad kernel_info reply", exc_info=True) | |
116 | self._request_kernel_info() |
|
116 | self._request_kernel_info() | |
117 | return |
|
117 | return | |
118 | else: |
|
118 | else: | |
119 | if msg['msg_type'] != 'kernel_info_reply' or 'protocol_version' not in msg['content']: |
|
119 | if msg['msg_type'] != 'kernel_info_reply' or 'protocol_version' not in msg['content']: | |
120 | self.log.error("Kernel info request failed, assuming current %s", msg['content']) |
|
120 | self.log.error("Kernel info request failed, assuming current %s", msg['content']) | |
121 | else: |
|
121 | else: | |
122 | protocol_version = msg['content']['protocol_version'] |
|
122 | protocol_version = msg['content']['protocol_version'] | |
123 | if protocol_version != kernel_protocol_version: |
|
123 | if protocol_version != kernel_protocol_version: | |
124 | self.session.adapt_version = int(protocol_version.split('.')[0]) |
|
124 | self.session.adapt_version = int(protocol_version.split('.')[0]) | |
125 | self.log.info("adapting kernel to %s" % protocol_version) |
|
125 | self.log.info("adapting kernel to %s" % protocol_version) | |
126 | self.kernel_info_channel.close() |
|
126 | self.kernel_info_channel.close() | |
127 | self.kernel_info_channel = None |
|
127 | self.kernel_info_channel = None | |
128 |
|
128 | |||
129 | def initialize(self): |
|
129 | def initialize(self): | |
130 | super(ZMQChannelHandler, self).initialize() |
|
130 | super(ZMQChannelHandler, self).initialize() | |
131 | self.zmq_stream = None |
|
131 | self.zmq_stream = None | |
132 |
|
132 | |||
133 | def open(self, kernel_id): |
|
133 | def open(self, kernel_id): | |
134 | super(ZMQChannelHandler, self).open(kernel_id) |
|
134 | super(ZMQChannelHandler, self).open(kernel_id) | |
135 | try: |
|
135 | try: | |
136 | self.create_stream() |
|
136 | self.create_stream() | |
137 | except web.HTTPError: |
|
137 | except web.HTTPError: | |
138 | # WebSockets don't response to traditional error codes so we |
|
138 | # WebSockets don't response to traditional error codes so we | |
139 | # close the connection. |
|
139 | # close the connection. | |
140 | if not self.stream.closed(): |
|
140 | if not self.stream.closed(): | |
141 | self.stream.close() |
|
141 | self.stream.close() | |
142 | self.close() |
|
142 | self.close() | |
143 | else: |
|
143 | else: | |
144 | self.zmq_stream.on_recv(self._on_zmq_reply) |
|
144 | self.zmq_stream.on_recv(self._on_zmq_reply) | |
145 |
|
145 | |||
146 | def on_message(self, msg): |
|
146 | def on_message(self, msg): | |
147 | if self.zmq_stream is None: |
|
147 | if self.zmq_stream is None: | |
148 | return |
|
148 | return | |
149 | elif self.zmq_stream.closed(): |
|
149 | elif self.zmq_stream.closed(): | |
150 | self.log.info("%s closed, closing websocket.", self) |
|
150 | self.log.info("%s closed, closing websocket.", self) | |
151 | self.close() |
|
151 | self.close() | |
152 | return |
|
152 | return | |
153 | msg = json.loads(msg) |
|
153 | msg = json.loads(msg) | |
154 | self.session.send(self.zmq_stream, msg) |
|
154 | self.session.send(self.zmq_stream, msg) | |
155 |
|
155 | |||
156 | def on_close(self): |
|
156 | def on_close(self): | |
157 | # This method can be called twice, once by self.kernel_died and once |
|
157 | # This method can be called twice, once by self.kernel_died and once | |
158 | # from the WebSocket close event. If the WebSocket connection is |
|
158 | # from the WebSocket close event. If the WebSocket connection is | |
159 | # closed before the ZMQ streams are setup, they could be None. |
|
159 | # closed before the ZMQ streams are setup, they could be None. | |
160 | if self.zmq_stream is not None and not self.zmq_stream.closed(): |
|
160 | if self.zmq_stream is not None and not self.zmq_stream.closed(): | |
161 | self.zmq_stream.on_recv(None) |
|
161 | self.zmq_stream.on_recv(None) | |
162 | # close the socket directly, don't wait for the stream |
|
162 | # close the socket directly, don't wait for the stream | |
163 | socket = self.zmq_stream.socket |
|
163 | socket = self.zmq_stream.socket | |
164 | self.zmq_stream.close() |
|
164 | self.zmq_stream.close() | |
165 | socket.close() |
|
165 | socket.close() | |
166 |
|
166 | |||
167 |
|
167 | |||
168 | class IOPubHandler(ZMQChannelHandler): |
|
168 | class IOPubHandler(ZMQChannelHandler): | |
169 | channel = 'iopub' |
|
169 | channel = 'iopub' | |
170 |
|
170 | |||
171 | def create_stream(self): |
|
171 | def create_stream(self): | |
172 | super(IOPubHandler, self).create_stream() |
|
172 | super(IOPubHandler, self).create_stream() | |
173 | km = self.kernel_manager |
|
173 | km = self.kernel_manager | |
174 | km.add_restart_callback(self.kernel_id, self.on_kernel_restarted) |
|
174 | km.add_restart_callback(self.kernel_id, self.on_kernel_restarted) | |
175 | km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead') |
|
175 | km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead') | |
176 |
|
176 | |||
177 | def on_close(self): |
|
177 | def on_close(self): | |
178 | km = self.kernel_manager |
|
178 | km = self.kernel_manager | |
179 | if self.kernel_id in km: |
|
179 | if self.kernel_id in km: | |
180 | km.remove_restart_callback( |
|
180 | km.remove_restart_callback( | |
181 | self.kernel_id, self.on_kernel_restarted, |
|
181 | self.kernel_id, self.on_kernel_restarted, | |
182 | ) |
|
182 | ) | |
183 | km.remove_restart_callback( |
|
183 | km.remove_restart_callback( | |
184 | self.kernel_id, self.on_restart_failed, 'dead', |
|
184 | self.kernel_id, self.on_restart_failed, 'dead', | |
185 | ) |
|
185 | ) | |
186 | super(IOPubHandler, self).on_close() |
|
186 | super(IOPubHandler, self).on_close() | |
187 |
|
187 | |||
188 | def _send_status_message(self, status): |
|
188 | def _send_status_message(self, status): | |
189 | msg = self.session.msg("status", |
|
189 | msg = self.session.msg("status", | |
190 | {'execution_state': status} |
|
190 | {'execution_state': status} | |
191 | ) |
|
191 | ) | |
192 | self.write_message(json.dumps(msg, default=date_default)) |
|
192 | self.write_message(json.dumps(msg, default=date_default)) | |
193 |
|
193 | |||
194 | def on_kernel_restarted(self): |
|
194 | def on_kernel_restarted(self): | |
195 | logging.warn("kernel %s restarted", self.kernel_id) |
|
195 | logging.warn("kernel %s restarted", self.kernel_id) | |
196 | self._send_status_message('restarting') |
|
196 | self._send_status_message('restarting') | |
197 |
|
197 | |||
198 | def on_restart_failed(self): |
|
198 | def on_restart_failed(self): | |
199 | logging.error("kernel %s restarted failed!", self.kernel_id) |
|
199 | logging.error("kernel %s restarted failed!", self.kernel_id) | |
200 | self._send_status_message('dead') |
|
200 | self._send_status_message('dead') | |
201 |
|
201 | |||
202 | def on_message(self, msg): |
|
202 | def on_message(self, msg): | |
203 | """IOPub messages make no sense""" |
|
203 | """IOPub messages make no sense""" | |
204 | pass |
|
204 | pass | |
205 |
|
205 | |||
206 |
|
206 | |||
207 | class ShellHandler(ZMQChannelHandler): |
|
207 | class ShellHandler(ZMQChannelHandler): | |
208 | channel = 'shell' |
|
208 | channel = 'shell' | |
209 |
|
209 | |||
210 |
|
210 | |||
211 | class StdinHandler(ZMQChannelHandler): |
|
211 | class StdinHandler(ZMQChannelHandler): | |
212 | channel = 'stdin' |
|
212 | channel = 'stdin' | |
213 |
|
213 | |||
214 |
|
214 | |||
215 | #----------------------------------------------------------------------------- |
|
215 | #----------------------------------------------------------------------------- | |
216 | # URL to handler mappings |
|
216 | # URL to handler mappings | |
217 | #----------------------------------------------------------------------------- |
|
217 | #----------------------------------------------------------------------------- | |
218 |
|
218 | |||
219 |
|
219 | |||
220 | _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)" |
|
220 | _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)" | |
221 | _kernel_action_regex = r"(?P<action>restart|interrupt)" |
|
221 | _kernel_action_regex = r"(?P<action>restart|interrupt)" | |
222 |
|
222 | |||
223 | default_handlers = [ |
|
223 | default_handlers = [ | |
224 | (r"/api/kernels", MainKernelHandler), |
|
224 | (r"/api/kernels", MainKernelHandler), | |
225 | (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler), |
|
225 | (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler), | |
226 | (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler), |
|
226 | (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler), | |
227 | (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler), |
|
227 | (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler), | |
228 | (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler), |
|
228 | (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler), | |
229 | (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler) |
|
229 | (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler) | |
230 | ] |
|
230 | ] |
@@ -1,123 +1,133 b'' | |||||
1 | """Test the kernels service API.""" |
|
1 | """Test the kernels service API.""" | |
2 |
|
2 | |||
3 | import json |
|
3 | import json | |
4 | import requests |
|
4 | import requests | |
5 |
|
5 | |||
6 | from IPython.html.utils import url_path_join |
|
6 | from IPython.html.utils import url_path_join | |
7 | from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error |
|
7 | from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error | |
8 |
|
8 | |||
9 | class KernelAPI(object): |
|
9 | class KernelAPI(object): | |
10 | """Wrapper for kernel REST API requests""" |
|
10 | """Wrapper for kernel REST API requests""" | |
11 | def __init__(self, base_url): |
|
11 | def __init__(self, base_url): | |
12 | self.base_url = base_url |
|
12 | self.base_url = base_url | |
13 |
|
13 | |||
14 | def _req(self, verb, path, body=None): |
|
14 | def _req(self, verb, path, body=None): | |
15 | response = requests.request(verb, |
|
15 | response = requests.request(verb, | |
16 | url_path_join(self.base_url, 'api/kernels', path), data=body) |
|
16 | url_path_join(self.base_url, 'api/kernels', path), data=body) | |
17 |
|
17 | |||
18 | if 400 <= response.status_code < 600: |
|
18 | if 400 <= response.status_code < 600: | |
19 | try: |
|
19 | try: | |
20 | response.reason = response.json()['message'] |
|
20 | response.reason = response.json()['message'] | |
21 | except: |
|
21 | except: | |
22 | pass |
|
22 | pass | |
23 | response.raise_for_status() |
|
23 | response.raise_for_status() | |
24 |
|
24 | |||
25 | return response |
|
25 | return response | |
26 |
|
26 | |||
27 | def list(self): |
|
27 | def list(self): | |
28 | return self._req('GET', '') |
|
28 | return self._req('GET', '') | |
29 |
|
29 | |||
30 | def get(self, id): |
|
30 | def get(self, id): | |
31 | return self._req('GET', id) |
|
31 | return self._req('GET', id) | |
32 |
|
32 | |||
33 | def start(self, name='python'): |
|
33 | def start(self, name='python'): | |
34 | body = json.dumps({'name': name}) |
|
34 | body = json.dumps({'name': name}) | |
35 | return self._req('POST', '', body) |
|
35 | return self._req('POST', '', body) | |
36 |
|
36 | |||
37 | def shutdown(self, id): |
|
37 | def shutdown(self, id): | |
38 | return self._req('DELETE', id) |
|
38 | return self._req('DELETE', id) | |
39 |
|
39 | |||
40 | def interrupt(self, id): |
|
40 | def interrupt(self, id): | |
41 | return self._req('POST', url_path_join(id, 'interrupt')) |
|
41 | return self._req('POST', url_path_join(id, 'interrupt')) | |
42 |
|
42 | |||
43 | def restart(self, id): |
|
43 | def restart(self, id): | |
44 | return self._req('POST', url_path_join(id, 'restart')) |
|
44 | return self._req('POST', url_path_join(id, 'restart')) | |
45 |
|
45 | |||
46 | class KernelAPITest(NotebookTestBase): |
|
46 | class KernelAPITest(NotebookTestBase): | |
47 | """Test the kernels web service API""" |
|
47 | """Test the kernels web service API""" | |
48 | def setUp(self): |
|
48 | def setUp(self): | |
49 | self.kern_api = KernelAPI(self.base_url()) |
|
49 | self.kern_api = KernelAPI(self.base_url()) | |
50 |
|
50 | |||
51 | def tearDown(self): |
|
51 | def tearDown(self): | |
52 | for k in self.kern_api.list().json(): |
|
52 | for k in self.kern_api.list().json(): | |
53 | self.kern_api.shutdown(k['id']) |
|
53 | self.kern_api.shutdown(k['id']) | |
54 |
|
54 | |||
55 | def test__no_kernels(self): |
|
55 | def test__no_kernels(self): | |
56 | """Make sure there are no kernels running at the start""" |
|
56 | """Make sure there are no kernels running at the start""" | |
57 | kernels = self.kern_api.list().json() |
|
57 | kernels = self.kern_api.list().json() | |
58 | self.assertEqual(kernels, []) |
|
58 | self.assertEqual(kernels, []) | |
59 |
|
59 | |||
|
60 | def test_default_kernel(self): | |||
|
61 | # POST request | |||
|
62 | r = self.kern_api._req('POST', '') | |||
|
63 | kern1 = r.json() | |||
|
64 | self.assertEqual(r.headers['location'], '/api/kernels/' + kern1['id']) | |||
|
65 | self.assertEqual(r.status_code, 201) | |||
|
66 | self.assertIsInstance(kern1, dict) | |||
|
67 | ||||
|
68 | self.assertEqual(r.headers['x-frame-options'], "SAMEORIGIN") | |||
|
69 | ||||
60 | def test_main_kernel_handler(self): |
|
70 | def test_main_kernel_handler(self): | |
61 | # POST request |
|
71 | # POST request | |
62 | r = self.kern_api.start() |
|
72 | r = self.kern_api.start() | |
63 | kern1 = r.json() |
|
73 | kern1 = r.json() | |
64 | self.assertEqual(r.headers['location'], '/api/kernels/' + kern1['id']) |
|
74 | self.assertEqual(r.headers['location'], '/api/kernels/' + kern1['id']) | |
65 | self.assertEqual(r.status_code, 201) |
|
75 | self.assertEqual(r.status_code, 201) | |
66 | self.assertIsInstance(kern1, dict) |
|
76 | self.assertIsInstance(kern1, dict) | |
67 |
|
77 | |||
68 | self.assertEqual(r.headers['x-frame-options'], "SAMEORIGIN") |
|
78 | self.assertEqual(r.headers['x-frame-options'], "SAMEORIGIN") | |
69 |
|
79 | |||
70 | # GET request |
|
80 | # GET request | |
71 | r = self.kern_api.list() |
|
81 | r = self.kern_api.list() | |
72 | self.assertEqual(r.status_code, 200) |
|
82 | self.assertEqual(r.status_code, 200) | |
73 | assert isinstance(r.json(), list) |
|
83 | assert isinstance(r.json(), list) | |
74 | self.assertEqual(r.json()[0]['id'], kern1['id']) |
|
84 | self.assertEqual(r.json()[0]['id'], kern1['id']) | |
75 | self.assertEqual(r.json()[0]['name'], kern1['name']) |
|
85 | self.assertEqual(r.json()[0]['name'], kern1['name']) | |
76 |
|
86 | |||
77 | # create another kernel and check that they both are added to the |
|
87 | # create another kernel and check that they both are added to the | |
78 | # list of kernels from a GET request |
|
88 | # list of kernels from a GET request | |
79 | kern2 = self.kern_api.start().json() |
|
89 | kern2 = self.kern_api.start().json() | |
80 | assert isinstance(kern2, dict) |
|
90 | assert isinstance(kern2, dict) | |
81 | r = self.kern_api.list() |
|
91 | r = self.kern_api.list() | |
82 | kernels = r.json() |
|
92 | kernels = r.json() | |
83 | self.assertEqual(r.status_code, 200) |
|
93 | self.assertEqual(r.status_code, 200) | |
84 | assert isinstance(kernels, list) |
|
94 | assert isinstance(kernels, list) | |
85 | self.assertEqual(len(kernels), 2) |
|
95 | self.assertEqual(len(kernels), 2) | |
86 |
|
96 | |||
87 | # Interrupt a kernel |
|
97 | # Interrupt a kernel | |
88 | r = self.kern_api.interrupt(kern2['id']) |
|
98 | r = self.kern_api.interrupt(kern2['id']) | |
89 | self.assertEqual(r.status_code, 204) |
|
99 | self.assertEqual(r.status_code, 204) | |
90 |
|
100 | |||
91 | # Restart a kernel |
|
101 | # Restart a kernel | |
92 | r = self.kern_api.restart(kern2['id']) |
|
102 | r = self.kern_api.restart(kern2['id']) | |
93 | self.assertEqual(r.headers['Location'], '/api/kernels/'+kern2['id']) |
|
103 | self.assertEqual(r.headers['Location'], '/api/kernels/'+kern2['id']) | |
94 | rekern = r.json() |
|
104 | rekern = r.json() | |
95 | self.assertEqual(rekern['id'], kern2['id']) |
|
105 | self.assertEqual(rekern['id'], kern2['id']) | |
96 | self.assertEqual(rekern['name'], kern2['name']) |
|
106 | self.assertEqual(rekern['name'], kern2['name']) | |
97 |
|
107 | |||
98 | def test_kernel_handler(self): |
|
108 | def test_kernel_handler(self): | |
99 | # GET kernel with given id |
|
109 | # GET kernel with given id | |
100 | kid = self.kern_api.start().json()['id'] |
|
110 | kid = self.kern_api.start().json()['id'] | |
101 | r = self.kern_api.get(kid) |
|
111 | r = self.kern_api.get(kid) | |
102 | kern1 = r.json() |
|
112 | kern1 = r.json() | |
103 | self.assertEqual(r.status_code, 200) |
|
113 | self.assertEqual(r.status_code, 200) | |
104 | assert isinstance(kern1, dict) |
|
114 | assert isinstance(kern1, dict) | |
105 | self.assertIn('id', kern1) |
|
115 | self.assertIn('id', kern1) | |
106 | self.assertEqual(kern1['id'], kid) |
|
116 | self.assertEqual(kern1['id'], kid) | |
107 |
|
117 | |||
108 | # Request a bad kernel id and check that a JSON |
|
118 | # Request a bad kernel id and check that a JSON | |
109 | # message is returned! |
|
119 | # message is returned! | |
110 | bad_id = '111-111-111-111-111' |
|
120 | bad_id = '111-111-111-111-111' | |
111 | with assert_http_error(404, 'Kernel does not exist: ' + bad_id): |
|
121 | with assert_http_error(404, 'Kernel does not exist: ' + bad_id): | |
112 | self.kern_api.get(bad_id) |
|
122 | self.kern_api.get(bad_id) | |
113 |
|
123 | |||
114 | # DELETE kernel with id |
|
124 | # DELETE kernel with id | |
115 | r = self.kern_api.shutdown(kid) |
|
125 | r = self.kern_api.shutdown(kid) | |
116 | self.assertEqual(r.status_code, 204) |
|
126 | self.assertEqual(r.status_code, 204) | |
117 | kernels = self.kern_api.list().json() |
|
127 | kernels = self.kern_api.list().json() | |
118 | self.assertEqual(kernels, []) |
|
128 | self.assertEqual(kernels, []) | |
119 |
|
129 | |||
120 | # Request to delete a non-existent kernel id |
|
130 | # Request to delete a non-existent kernel id | |
121 | bad_id = '111-111-111-111-111' |
|
131 | bad_id = '111-111-111-111-111' | |
122 | with assert_http_error(404, 'Kernel does not exist: ' + bad_id): |
|
132 | with assert_http_error(404, 'Kernel does not exist: ' + bad_id): | |
123 | self.kern_api.shutdown(bad_id) |
|
133 | self.kern_api.shutdown(bad_id) |
General Comments 0
You need to be logged in to leave comments.
Login now