##// END OF EJS Templates
adding to test_kernels_api.py...
Zachary Sailer -
Show More
@@ -1,191 +1,193 b''
1 """Tornado handlers for the notebook.
1 """Tornado handlers for the notebook.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 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 import logging
19 import logging
20 from tornado import web
20 from tornado import web
21
21
22 from zmq.utils import jsonapi
22 from zmq.utils import jsonapi
23
23
24 from IPython.utils.jsonutil import date_default
24 from IPython.utils.jsonutil import date_default
25
25
26 from ...base.handlers import IPythonHandler, json_errors
26 from ...base.handlers import IPythonHandler, json_errors
27 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
27 from ...base.zmqhandlers import AuthenticatedZMQStreamHandler
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Kernel handlers
30 # Kernel handlers
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33
33
34 class MainKernelHandler(IPythonHandler):
34 class MainKernelHandler(IPythonHandler):
35
35
36 @web.authenticated
36 @web.authenticated
37 @json_errors
37 @json_errors
38 def get(self):
38 def get(self):
39 km = self.kernel_manager
39 km = self.kernel_manager
40 self.finish(jsonapi.dumps(km.list_kernels(self.ws_url)))
40 self.finish(jsonapi.dumps(km.list_kernels(self.ws_url)))
41
41
42 @web.authenticated
42 @web.authenticated
43 @json_errors
43 @json_errors
44 def post(self):
44 def post(self):
45 km = self.kernel_manager
45 km = self.kernel_manager
46 kernel_id = km.start_kernel()
46 kernel_id = km.start_kernel()
47 model = km.kernel_model(kernel_id, self.ws_url)
47 model = km.kernel_model(kernel_id, self.ws_url)
48 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
48 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
49 self.set_status(201)
49 self.finish(jsonapi.dumps(model))
50 self.finish(jsonapi.dumps(model))
50
51
51
52
52 class KernelHandler(IPythonHandler):
53 class KernelHandler(IPythonHandler):
53
54
54 SUPPORTED_METHODS = ('DELETE', 'GET')
55 SUPPORTED_METHODS = ('DELETE', 'GET')
55
56
56 @web.authenticated
57 @web.authenticated
57 @json_errors
58 @json_errors
58 def get(self, kernel_id):
59 def get(self, kernel_id):
59 km = self.kernel_manager
60 km = self.kernel_manager
61 km._check_kernel_id(kernel_id)
60 model = km.kernel_model(kernel_id, self.ws_url)
62 model = km.kernel_model(kernel_id, self.ws_url)
61 self.finish(jsonapi.dumps(model))
63 self.finish(jsonapi.dumps(model))
62
64
63 @web.authenticated
65 @web.authenticated
64 @json_errors
66 @json_errors
65 def delete(self, kernel_id):
67 def delete(self, kernel_id):
66 km = self.kernel_manager
68 km = self.kernel_manager
67 km.shutdown_kernel(kernel_id)
69 km.shutdown_kernel(kernel_id)
68 self.set_status(204)
70 self.set_status(204)
69 self.finish()
71 self.finish()
70
72
71
73
72 class KernelActionHandler(IPythonHandler):
74 class KernelActionHandler(IPythonHandler):
73
75
74 @web.authenticated
76 @web.authenticated
75 @json_errors
77 @json_errors
76 def post(self, kernel_id, action):
78 def post(self, kernel_id, action):
77 km = self.kernel_manager
79 km = self.kernel_manager
78 if action == 'interrupt':
80 if action == 'interrupt':
79 km.interrupt_kernel(kernel_id)
81 km.interrupt_kernel(kernel_id)
80 self.set_status(204)
82 self.set_status(204)
81 if action == 'restart':
83 if action == 'restart':
82 km.restart_kernel(kernel_id)
84 km.restart_kernel(kernel_id)
83 model = km.kernel_model(kernel_id, self.ws_url)
85 model = km.kernel_model(kernel_id, self.ws_url)
84 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
86 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
85 self.write(jsonapi.dumps(model))
87 self.write(jsonapi.dumps(model))
86 self.finish()
88 self.finish()
87
89
88
90
89 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
91 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
90
92
91 def create_stream(self):
93 def create_stream(self):
92 km = self.kernel_manager
94 km = self.kernel_manager
93 meth = getattr(km, 'connect_%s' % self.channel)
95 meth = getattr(km, 'connect_%s' % self.channel)
94 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
96 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
95
97
96 def initialize(self, *args, **kwargs):
98 def initialize(self, *args, **kwargs):
97 self.zmq_stream = None
99 self.zmq_stream = None
98
100
99 def on_first_message(self, msg):
101 def on_first_message(self, msg):
100 try:
102 try:
101 super(ZMQChannelHandler, self).on_first_message(msg)
103 super(ZMQChannelHandler, self).on_first_message(msg)
102 except web.HTTPError:
104 except web.HTTPError:
103 self.close()
105 self.close()
104 return
106 return
105 try:
107 try:
106 self.create_stream()
108 self.create_stream()
107 except web.HTTPError:
109 except web.HTTPError:
108 # WebSockets don't response to traditional error codes so we
110 # WebSockets don't response to traditional error codes so we
109 # close the connection.
111 # close the connection.
110 if not self.stream.closed():
112 if not self.stream.closed():
111 self.stream.close()
113 self.stream.close()
112 self.close()
114 self.close()
113 else:
115 else:
114 self.zmq_stream.on_recv(self._on_zmq_reply)
116 self.zmq_stream.on_recv(self._on_zmq_reply)
115
117
116 def on_message(self, msg):
118 def on_message(self, msg):
117 msg = jsonapi.loads(msg)
119 msg = jsonapi.loads(msg)
118 self.session.send(self.zmq_stream, msg)
120 self.session.send(self.zmq_stream, msg)
119
121
120 def on_close(self):
122 def on_close(self):
121 # This method can be called twice, once by self.kernel_died and once
123 # This method can be called twice, once by self.kernel_died and once
122 # from the WebSocket close event. If the WebSocket connection is
124 # from the WebSocket close event. If the WebSocket connection is
123 # closed before the ZMQ streams are setup, they could be None.
125 # closed before the ZMQ streams are setup, they could be None.
124 if self.zmq_stream is not None and not self.zmq_stream.closed():
126 if self.zmq_stream is not None and not self.zmq_stream.closed():
125 self.zmq_stream.on_recv(None)
127 self.zmq_stream.on_recv(None)
126 self.zmq_stream.close()
128 self.zmq_stream.close()
127
129
128
130
129 class IOPubHandler(ZMQChannelHandler):
131 class IOPubHandler(ZMQChannelHandler):
130 channel = 'iopub'
132 channel = 'iopub'
131
133
132 def create_stream(self):
134 def create_stream(self):
133 super(IOPubHandler, self).create_stream()
135 super(IOPubHandler, self).create_stream()
134 km = self.kernel_manager
136 km = self.kernel_manager
135 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
137 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
136 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
138 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
137
139
138 def on_close(self):
140 def on_close(self):
139 km = self.kernel_manager
141 km = self.kernel_manager
140 if self.kernel_id in km:
142 if self.kernel_id in km:
141 km.remove_restart_callback(
143 km.remove_restart_callback(
142 self.kernel_id, self.on_kernel_restarted,
144 self.kernel_id, self.on_kernel_restarted,
143 )
145 )
144 km.remove_restart_callback(
146 km.remove_restart_callback(
145 self.kernel_id, self.on_restart_failed, 'dead',
147 self.kernel_id, self.on_restart_failed, 'dead',
146 )
148 )
147 super(IOPubHandler, self).on_close()
149 super(IOPubHandler, self).on_close()
148
150
149 def _send_status_message(self, status):
151 def _send_status_message(self, status):
150 msg = self.session.msg("status",
152 msg = self.session.msg("status",
151 {'execution_state': status}
153 {'execution_state': status}
152 )
154 )
153 self.write_message(jsonapi.dumps(msg, default=date_default))
155 self.write_message(jsonapi.dumps(msg, default=date_default))
154
156
155 def on_kernel_restarted(self):
157 def on_kernel_restarted(self):
156 logging.warn("kernel %s restarted", self.kernel_id)
158 logging.warn("kernel %s restarted", self.kernel_id)
157 self._send_status_message('restarting')
159 self._send_status_message('restarting')
158
160
159 def on_restart_failed(self):
161 def on_restart_failed(self):
160 logging.error("kernel %s restarted failed!", self.kernel_id)
162 logging.error("kernel %s restarted failed!", self.kernel_id)
161 self._send_status_message('dead')
163 self._send_status_message('dead')
162
164
163 def on_message(self, msg):
165 def on_message(self, msg):
164 """IOPub messages make no sense"""
166 """IOPub messages make no sense"""
165 pass
167 pass
166
168
167
169
168 class ShellHandler(ZMQChannelHandler):
170 class ShellHandler(ZMQChannelHandler):
169 channel = 'shell'
171 channel = 'shell'
170
172
171
173
172 class StdinHandler(ZMQChannelHandler):
174 class StdinHandler(ZMQChannelHandler):
173 channel = 'stdin'
175 channel = 'stdin'
174
176
175
177
176 #-----------------------------------------------------------------------------
178 #-----------------------------------------------------------------------------
177 # URL to handler mappings
179 # URL to handler mappings
178 #-----------------------------------------------------------------------------
180 #-----------------------------------------------------------------------------
179
181
180
182
181 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
183 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
182 _kernel_action_regex = r"(?P<action>restart|interrupt)"
184 _kernel_action_regex = r"(?P<action>restart|interrupt)"
183
185
184 default_handlers = [
186 default_handlers = [
185 (r"/api/kernels", MainKernelHandler),
187 (r"/api/kernels", MainKernelHandler),
186 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
188 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
187 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
189 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
188 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
190 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
189 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
191 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
190 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
192 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
191 ]
193 ]
@@ -1,95 +1,98 b''
1 """A kernel manager relating notebooks and kernels
1 """A kernel manager relating notebooks and 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 tornado import web
19 from tornado import web
20
20
21 from IPython.kernel.multikernelmanager import MultiKernelManager
21 from IPython.kernel.multikernelmanager import MultiKernelManager
22 from IPython.utils.traitlets import (
22 from IPython.utils.traitlets import (
23 Dict, List, Unicode,
23 Dict, List, Unicode,
24 )
24 )
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes
27 # Classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30
30
31 class MappingKernelManager(MultiKernelManager):
31 class MappingKernelManager(MultiKernelManager):
32 """A KernelManager that handles notebook mapping and HTTP error handling"""
32 """A KernelManager that handles notebook mapping and HTTP error handling"""
33
33
34 def _kernel_manager_class_default(self):
34 def _kernel_manager_class_default(self):
35 return "IPython.kernel.ioloop.IOLoopKernelManager"
35 return "IPython.kernel.ioloop.IOLoopKernelManager"
36
36
37 kernel_argv = List(Unicode)
37 kernel_argv = List(Unicode)
38
38
39 #-------------------------------------------------------------------------
39 #-------------------------------------------------------------------------
40 # Methods for managing kernels and sessions
40 # Methods for managing kernels and sessions
41 #-------------------------------------------------------------------------
41 #-------------------------------------------------------------------------
42
42
43 def _handle_kernel_died(self, kernel_id):
43 def _handle_kernel_died(self, kernel_id):
44 """notice that a kernel died"""
44 """notice that a kernel died"""
45 self.log.warn("Kernel %s died, removing from map.", kernel_id)
45 self.log.warn("Kernel %s died, removing from map.", kernel_id)
46 self.remove_kernel(kernel_id)
46 self.remove_kernel(kernel_id)
47
47
48 def start_kernel(self, kernel_id=None, **kwargs):
48 def start_kernel(self, kernel_id=None, **kwargs):
49 """Start a kernel for a session an return its kernel_id.
49 """Start a kernel for a session an return its kernel_id.
50
50
51 Parameters
51 Parameters
52 ----------
52 ----------
53 kernel_id : uuid
53 kernel_id : uuid
54 The uuid to associate the new kernel with. If this
54 The uuid to associate the new kernel with. If this
55 is not None, this kernel will be persistent whenever it is
55 is not None, this kernel will be persistent whenever it is
56 requested.
56 requested.
57 """
57 """
58 if kernel_id is None:
58 if kernel_id is None:
59 kwargs['extra_arguments'] = self.kernel_argv
59 kwargs['extra_arguments'] = self.kernel_argv
60 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
60 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
61 self.log.info("Kernel started: %s" % kernel_id)
61 self.log.info("Kernel started: %s" % kernel_id)
62 self.log.debug("Kernel args: %r" % kwargs)
62 self.log.debug("Kernel args: %r" % kwargs)
63 # register callback for failed auto-restart
63 # register callback for failed auto-restart
64 self.add_restart_callback(kernel_id,
64 self.add_restart_callback(kernel_id,
65 lambda : self._handle_kernel_died(kernel_id),
65 lambda : self._handle_kernel_died(kernel_id),
66 'dead',
66 'dead',
67 )
67 )
68 else:
68 else:
69 self._check_kernel_id(kernel_id)
69 self.log.info("Using existing kernel: %s" % kernel_id)
70 self.log.info("Using existing kernel: %s" % kernel_id)
70 return kernel_id
71 return kernel_id
71
72
72 def shutdown_kernel(self, kernel_id, now=False):
73 def shutdown_kernel(self, kernel_id, now=False):
73 """Shutdown a kernel by kernel_id"""
74 """Shutdown a kernel by kernel_id"""
75 self._check_kernel_id(kernel_id)
74 super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now)
76 super(MappingKernelManager, self).shutdown_kernel(kernel_id, now=now)
75
77
76 def kernel_model(self, kernel_id, ws_url):
78 def kernel_model(self, kernel_id, ws_url):
77 """Return a dictionary of kernel information described in the
79 """Return a dictionary of kernel information described in the
78 JSON standard model."""
80 JSON standard model."""
81 self._check_kernel_id(kernel_id)
79 model = {"id":kernel_id, "ws_url": ws_url}
82 model = {"id":kernel_id, "ws_url": ws_url}
80 return model
83 return model
81
84
82 def list_kernels(self, ws_url):
85 def list_kernels(self, ws_url):
83 """Returns a list of kernel_id's of kernels running."""
86 """Returns a list of kernel_id's of kernels running."""
84 kernels = []
87 kernels = []
85 kernel_ids = super(MappingKernelManager, self).list_kernel_ids()
88 kernel_ids = super(MappingKernelManager, self).list_kernel_ids()
86 for kernel_id in kernel_ids:
89 for kernel_id in kernel_ids:
87 model = self.kernel_model(kernel_id, ws_url)
90 model = self.kernel_model(kernel_id, ws_url)
88 kernels.append(model)
91 kernels.append(model)
89 return kernels
92 return kernels
90
93
91 # override _check_kernel_id to raise 404 instead of KeyError
94 # override _check_kernel_id to raise 404 instead of KeyError
92 def _check_kernel_id(self, kernel_id):
95 def _check_kernel_id(self, kernel_id):
93 """Check a that a kernel_id exists and raise 404 if not."""
96 """Check a that a kernel_id exists and raise 404 if not."""
94 if kernel_id not in self:
97 if kernel_id not in self:
95 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
98 raise web.HTTPError(404, u'Kernel does not exist: %s' % kernel_id)
@@ -1,54 +1,100 b''
1 """Test the kernels service API."""
1 """Test the kernels service API."""
2
2
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import json
6 import json
7
7
8 import requests
8 import requests
9
9
10 from IPython.html.utils import url_path_join
10 from IPython.html.utils import url_path_join
11 from IPython.html.tests.launchnotebook import NotebookTestBase
11 from IPython.html.tests.launchnotebook import NotebookTestBase
12
12
13
13
14 class KernelAPITest(NotebookTestBase):
14 class KernelAPITest(NotebookTestBase):
15 """Test the kernels web service API"""
15 """Test the kernels web service API"""
16
16
17 def base_url(self):
17 def base_url(self):
18 return url_path_join(super(KernelAPITest,self).base_url(), 'api/kernels')
18 return url_path_join(super(KernelAPITest,self).base_url(), 'api/kernels')
19
19
20 def mkkernel(self):
20 def mkkernel(self):
21 r = requests.post(self.base_url())
21 r = requests.post(self.base_url())
22 return r.json()
22 return r.json()
23
23
24 def test__no_kernels(self):
24 def test__no_kernels(self):
25 """Make sure there are no kernels running at the start"""
25 """Make sure there are no kernels running at the start"""
26 url = self.base_url()
26 url = self.base_url()
27 r = requests.get(url)
27 r = requests.get(url)
28 self.assertEqual(r.json(), [])
28 self.assertEqual(r.json(), [])
29
29
30 def test_main_kernel_handler(self):
30 def test_main_kernel_handler(self):
31 # POST request
31 # POST request
32 r = requests.post(self.base_url())
32 r = requests.post(self.base_url())
33 data = r.json()
33 data = r.json()
34 status = r.status_code
35 header = r.headers
36 self.assertIn('location', header)
37 self.assertEquals(header['location'], '/api/kernels/' + data['id'])
38 self.assertEquals(status, 201)
34 assert isinstance(data, dict)
39 assert isinstance(data, dict)
35
40
36 # GET request
41 # GET request
37 r = requests.get(self.base_url())
42 r = requests.get(self.base_url())
43 status = r.status_code
44 self.assertEquals(status, 200)
38 assert isinstance(r.json(), list)
45 assert isinstance(r.json(), list)
39 self.assertEqual(r.json()[0]['id'], data['id'])
46 self.assertEqual(r.json()[0]['id'], data['id'])
47
48 # create another kernel and check that they both are added to the
49 # list of kernels from a GET request
50 data2 = self.mkkernel()
51 assert isinstance(data2, dict)
52 r = requests.get(self.base_url())
53 kernels = r.json()
54 status = r.status_code
55 self.assertEquals(status, 200)
56 assert isinstance(kernels, list)
57 self.assertEquals(len(kernels), 2)
40
58
41 def test_kernel_handler(self):
59 def test_kernel_handler(self):
42 # GET kernel with id
60 # GET kernel with given id
43 data = self.mkkernel()
61 data = self.mkkernel()
44 url = self.base_url() +'/' + data['id']
62 url = self.base_url() +'/' + data['id']
45 r = requests.get(url)
63 r = requests.get(url)
46 assert isinstance(r.json(), dict)
64 data1 = r.json()
47 self.assertIn('id', r.json())
65 status = r.status_code
48 self.assertEqual(r.json()['id'], data['id'])
66 self.assertEquals(status, 200)
67 assert isinstance(data1, dict)
68 self.assertIn('id', data1)
69 self.assertIn('ws_url', data1)
70 self.assertEqual(data1['id'], data['id'])
71
72 # Request a bad kernel id and check that a JSON
73 # message is returned!
74 bad_id = '111-111-111-111-111'
75 bad_url = self.base_url() + '/' + bad_id
76 r = requests.get(bad_url)
77 status = r.status_code
78 message = r.json()
79 self.assertEquals(status, 404)
80 assert isinstance(message, dict)
81 self.assertIn('message', message)
82 self.assertEquals(message['message'], 'Kernel does not exist: ' + bad_id)
49
83
50 # DELETE kernel with id
84 # DELETE kernel with id
51 r = requests.delete(url)
85 r = requests.delete(url)
52 self.assertEqual(r.status_code, 204)
86 self.assertEqual(r.status_code, 204)
53 r = requests.get(self.base_url())
87 r = requests.get(self.base_url())
54 self.assertEqual(r.json(), [])
88 self.assertEqual(r.json(), [])
89
90 # Request to delete a non-existent kernel id
91 bad_id = '111-111-111-111-111'
92 bad_url = self.base_url() + '/' + bad_id
93 r = requests.delete(bad_url)
94 status = r.status_code
95 message = r.json()
96 self.assertEquals(status, 404)
97 assert isinstance(message, dict)
98 self.assertIn('message', message)
99 self.assertEquals(message['message'], 'Kernel does not exist: ' + bad_id)
100 No newline at end of file
@@ -1,125 +1,125 b''
1 """Tornado handlers for the sessions web service.
1 """Tornado handlers for the sessions web service.
2
2
3 Authors:
3 Authors:
4
4
5 * Zach Sailer
5 * Zach Sailer
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 import json
19 import json
20
20
21 from tornado import web
21 from tornado import web
22 from IPython.utils.jsonutil import date_default
22 from IPython.utils.jsonutil import date_default
23 from ...base.handlers import IPythonHandler, json_errors
23 from ...base.handlers import IPythonHandler, json_errors
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Session web service handlers
26 # Session web service handlers
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29
29
30 class SessionRootHandler(IPythonHandler):
30 class SessionRootHandler(IPythonHandler):
31
31
32 @web.authenticated
32 @web.authenticated
33 @json_errors
33 @json_errors
34 def get(self):
34 def get(self):
35 # Return a list of running sessions
35 # Return a list of running sessions
36 sm = self.session_manager
36 sm = self.session_manager
37 sessions = sm.list_sessions()
37 sessions = sm.list_sessions()
38 self.finish(json.dumps(sessions, default=date_default))
38 self.finish(json.dumps(sessions, default=date_default))
39
39
40 @web.authenticated
40 @web.authenticated
41 @json_errors
41 @json_errors
42 def post(self):
42 def post(self):
43 # Creates a new session
43 # Creates a new session
44 #(unless a session already exists for the named nb)
44 #(unless a session already exists for the named nb)
45 sm = self.session_manager
45 sm = self.session_manager
46 nbm = self.notebook_manager
46 nbm = self.notebook_manager
47 km = self.kernel_manager
47 km = self.kernel_manager
48 model = self.get_json_body()
48 model = self.get_json_body()
49 if model is None:
49 if model is None:
50 raise HTTPError(400, "No JSON data provided")
50 raise web.HTTPError(400, "No JSON data provided")
51 try:
51 try:
52 name = model['notebook']['name']
52 name = model['notebook']['name']
53 except KeyError:
53 except KeyError:
54 raise HTTPError(400, "Missing field in JSON data: name")
54 raise web.HTTPError(400, "Missing field in JSON data: name")
55 try:
55 try:
56 path = model['notebook']['path']
56 path = model['notebook']['path']
57 except KeyError:
57 except KeyError:
58 raise HTTPError(400, "Missing field in JSON data: path")
58 raise web.HTTPError(400, "Missing field in JSON data: path")
59 # Check to see if session exists
59 # Check to see if session exists
60 if sm.session_exists(name=name, path=path):
60 if sm.session_exists(name=name, path=path):
61 model = sm.get_session(name=name, path=path)
61 model = sm.get_session(name=name, path=path)
62 else:
62 else:
63 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
63 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
64 model = sm.create_session(name=name, path=path, kernel_id=kernel_id, ws_url=self.ws_url)
64 model = sm.create_session(name=name, path=path, kernel_id=kernel_id, ws_url=self.ws_url)
65 self.set_header('Location', '{0}/api/sessions/{1}'.format(self.base_project_url, model['id']))
65 self.set_header('Location', '{0}/api/sessions/{1}'.format(self.base_project_url, model['id']))
66 self.finish(json.dumps(model, default=date_default))
66 self.finish(json.dumps(model, default=date_default))
67
67
68 class SessionHandler(IPythonHandler):
68 class SessionHandler(IPythonHandler):
69
69
70 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
70 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
71
71
72 @web.authenticated
72 @web.authenticated
73 @json_errors
73 @json_errors
74 def get(self, session_id):
74 def get(self, session_id):
75 # Returns the JSON model for a single session
75 # Returns the JSON model for a single session
76 sm = self.session_manager
76 sm = self.session_manager
77 model = sm.get_session(id=session_id)
77 model = sm.get_session(id=session_id)
78 self.finish(json.dumps(model, default=date_default))
78 self.finish(json.dumps(model, default=date_default))
79
79
80 @web.authenticated
80 @web.authenticated
81 @json_errors
81 @json_errors
82 def patch(self, session_id):
82 def patch(self, session_id):
83 # Currently, this handler is strictly for renaming notebooks
83 # Currently, this handler is strictly for renaming notebooks
84 sm = self.session_manager
84 sm = self.session_manager
85 nbm = self.notebook_manager
85 nbm = self.notebook_manager
86 km = self.kernel_manager
86 km = self.kernel_manager
87 model = self.get_json_body()
87 model = self.get_json_body()
88 if model is None:
88 if model is None:
89 raise HTTPError(400, "No JSON data provided")
89 raise HTTPError(400, "No JSON data provided")
90 changes = {}
90 changes = {}
91 if 'notebook' in model:
91 if 'notebook' in model:
92 notebook = model['notebook']
92 notebook = model['notebook']
93 if 'name' in notebook:
93 if 'name' in notebook:
94 changes['name'] = notebook['name']
94 changes['name'] = notebook['name']
95 if 'path' in notebook:
95 if 'path' in notebook:
96 changes['path'] = notebook['path']
96 changes['path'] = notebook['path']
97 sm.update_session(session_id, **changes)
97 sm.update_session(session_id, **changes)
98 model = sm.get_session(id=session_id)
98 model = sm.get_session(id=session_id)
99 self.finish(json.dumps(model, default=date_default))
99 self.finish(json.dumps(model, default=date_default))
100
100
101 @web.authenticated
101 @web.authenticated
102 @json_errors
102 @json_errors
103 def delete(self, session_id):
103 def delete(self, session_id):
104 # Deletes the session with given session_id
104 # Deletes the session with given session_id
105 sm = self.session_manager
105 sm = self.session_manager
106 nbm = self.notebook_manager
106 nbm = self.notebook_manager
107 km = self.kernel_manager
107 km = self.kernel_manager
108 session = sm.get_session(id=session_id)
108 session = sm.get_session(id=session_id)
109 sm.delete_session(session_id)
109 sm.delete_session(session_id)
110 km.shutdown_kernel(session['kernel']['id'])
110 km.shutdown_kernel(session['kernel']['id'])
111 self.set_status(204)
111 self.set_status(204)
112 self.finish()
112 self.finish()
113
113
114
114
115 #-----------------------------------------------------------------------------
115 #-----------------------------------------------------------------------------
116 # URL to handler mappings
116 # URL to handler mappings
117 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
118
118
119 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
119 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
120
120
121 default_handlers = [
121 default_handlers = [
122 (r"api/sessions/%s" % _session_id_regex, SessionHandler),
122 (r"api/sessions/%s" % _session_id_regex, SessionHandler),
123 (r"api/sessions", SessionRootHandler)
123 (r"api/sessions", SessionRootHandler)
124 ]
124 ]
125
125
General Comments 0
You need to be logged in to leave comments. Login now