##// END OF EJS Templates
add tests for session api
Zachary Sailer -
Show More
@@ -0,0 +1,95 b''
1 """Test the sessions web service API."""
2
3
4 import os
5 import sys
6 import json
7 from zmq.utils import jsonapi
8
9 import requests
10
11 from IPython.html.tests.launchnotebook import NotebookTestBase
12
13
14 class SessionAPITest(NotebookTestBase):
15 """Test the sessions web service API"""
16
17 def notebook_url(self):
18 return super(SessionAPITest,self).base_url() + 'api/notebooks'
19
20 def session_url(self):
21 return super(SessionAPITest,self).base_url() + 'api/sessions'
22
23 def mknb(self, name='', path='/'):
24 url = self.notebook_url() + path
25 return url, requests.post(url)
26
27 def delnb(self, name, path='/'):
28 url = self.notebook_url() + path + name
29 r = requests.delete(url)
30 return r.status_code
31
32 def test_no_sessions(self):
33 """Make sure there are no sessions running at the start"""
34 url = self.session_url()
35 r = requests.get(url)
36 self.assertEqual(r.json(), [])
37
38 def test_session_root_handler(self):
39 # POST a session
40 url, nb = self.mknb()
41 notebook = nb.json()
42 param = {'notebook_path': notebook['path'] + notebook['name']}
43 r = requests.post(self.session_url(), params=param)
44 data = r.json()
45 assert isinstance(data, dict)
46 assert data.has_key('name')
47 self.assertEqual(data['name'], notebook['name'])
48
49 # GET sessions
50 r = requests.get(self.session_url())
51 assert isinstance(r.json(), list)
52 assert isinstance(r.json()[0], dict)
53 self.assertEqual(r.json()[0]['id'], data['id'])
54
55 # Clean up
56 self.delnb('Untitled0.ipynb')
57 sess_url = self.session_url() +'/'+data['id']
58 r = requests.delete(sess_url)
59 self.assertEqual(r.status_code, 204)
60
61 def test_session_handler(self):
62 # Create a session
63 url, nb = self.mknb()
64 notebook = nb.json()
65 param = {'notebook_path': notebook['path'] + notebook['name']}
66 r = requests.post(self.session_url(), params=param)
67 session = r.json()
68
69 # GET a session
70 sess_url = self.session_url() + '/' + session['id']
71 r = requests.get(sess_url)
72 assert isinstance(r.json(), dict)
73 self.assertEqual(r.json(), session)
74
75 # PATCH a session
76 data = {'notebook_path': 'test.ipynb'}
77 r = requests.patch(sess_url, data=jsonapi.dumps(data))
78 # Patching the notebook webservice too (just for consistency)
79 requests.patch(self.notebook_url() + '/Untitled0.ipynb',
80 data=jsonapi.dumps({'name':'test.ipynb'}))
81 assert isinstance(r.json(), dict)
82 assert r.json().has_key('name')
83 assert r.json().has_key('id')
84 self.assertEqual(r.json()['name'], 'test.ipynb')
85 self.assertEqual(r.json()['id'], session['id'])
86
87 # DELETE a session
88 r = requests.delete(sess_url)
89 self.assertEqual(r.status_code, 204)
90 r = requests.get(self.session_url())
91 assert r.json() == []
92
93 # Clean up
94 r = self.delnb('test.ipynb')
95 assert r == 204 No newline at end of file
@@ -1,187 +1,186 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
26 from ...base.handlers import IPythonHandler
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 def get(self):
37 def get(self):
38 km = self.kernel_manager
38 km = self.kernel_manager
39 self.finish(jsonapi.dumps(km.list_kernels()))
39 self.finish(jsonapi.dumps(km.list_kernels()))
40
40
41 @web.authenticated
41 @web.authenticated
42 def post(self):
42 def post(self):
43 km = self.kernel_manager
43 km = self.kernel_manager
44 nbm = self.notebook_manager
44 kernel_id = km.start_kernel()
45 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
46 model = km.kernel_model(kernel_id, self.ws_url)
45 model = km.kernel_model(kernel_id, self.ws_url)
47 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
46 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
48 self.finish(jsonapi.dumps(model))
47 self.finish(jsonapi.dumps(model))
49
48
50
49
51 class KernelHandler(IPythonHandler):
50 class KernelHandler(IPythonHandler):
52
51
53 SUPPORTED_METHODS = ('DELETE', 'GET')
52 SUPPORTED_METHODS = ('DELETE', 'GET')
54
53
55 @web.authenticated
54 @web.authenticated
56 def get(self, kernel_id):
55 def get(self, kernel_id):
57 km = self.kernel_manager
56 km = self.kernel_manager
58 model = km.kernel_model(kernel_id, self.ws_url)
57 model = km.kernel_model(kernel_id, self.ws_url)
59 self.finish(jsonapi.dumps(model))
58 self.finish(jsonapi.dumps(model))
60
59
61 @web.authenticated
60 @web.authenticated
62 def delete(self, kernel_id):
61 def delete(self, kernel_id):
63 km = self.kernel_manager
62 km = self.kernel_manager
64 km.shutdown_kernel(kernel_id)
63 km.shutdown_kernel(kernel_id)
65 self.set_status(204)
64 self.set_status(204)
66 self.finish()
65 self.finish()
67
66
68
67
69 class KernelActionHandler(IPythonHandler):
68 class KernelActionHandler(IPythonHandler):
70
69
71 @web.authenticated
70 @web.authenticated
72 def post(self, kernel_id, action):
71 def post(self, kernel_id, action):
73 km = self.kernel_manager
72 km = self.kernel_manager
74 if action == 'interrupt':
73 if action == 'interrupt':
75 km.interrupt_kernel(kernel_id)
74 km.interrupt_kernel(kernel_id)
76 self.set_status(204)
75 self.set_status(204)
77 if action == 'restart':
76 if action == 'restart':
78 km.restart_kernel(kernel_id)
77 km.restart_kernel(kernel_id)
79 model = km.kernel_model(kernel_id, self.ws_url)
78 model = km.kernel_model(kernel_id, self.ws_url)
80 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
79 self.set_header('Location', '{0}api/kernels/{1}'.format(self.base_kernel_url, kernel_id))
81 self.write(jsonapi.dumps(model))
80 self.write(jsonapi.dumps(model))
82 self.finish()
81 self.finish()
83
82
84
83
85 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
84 class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
86
85
87 def create_stream(self):
86 def create_stream(self):
88 km = self.kernel_manager
87 km = self.kernel_manager
89 meth = getattr(km, 'connect_%s' % self.channel)
88 meth = getattr(km, 'connect_%s' % self.channel)
90 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
89 self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
91
90
92 def initialize(self, *args, **kwargs):
91 def initialize(self, *args, **kwargs):
93 self.zmq_stream = None
92 self.zmq_stream = None
94
93
95 def on_first_message(self, msg):
94 def on_first_message(self, msg):
96 try:
95 try:
97 super(ZMQChannelHandler, self).on_first_message(msg)
96 super(ZMQChannelHandler, self).on_first_message(msg)
98 except web.HTTPError:
97 except web.HTTPError:
99 self.close()
98 self.close()
100 return
99 return
101 try:
100 try:
102 self.create_stream()
101 self.create_stream()
103 except web.HTTPError:
102 except web.HTTPError:
104 # WebSockets don't response to traditional error codes so we
103 # WebSockets don't response to traditional error codes so we
105 # close the connection.
104 # close the connection.
106 if not self.stream.closed():
105 if not self.stream.closed():
107 self.stream.close()
106 self.stream.close()
108 self.close()
107 self.close()
109 else:
108 else:
110 self.zmq_stream.on_recv(self._on_zmq_reply)
109 self.zmq_stream.on_recv(self._on_zmq_reply)
111
110
112 def on_message(self, msg):
111 def on_message(self, msg):
113 msg = jsonapi.loads(msg)
112 msg = jsonapi.loads(msg)
114 self.session.send(self.zmq_stream, msg)
113 self.session.send(self.zmq_stream, msg)
115
114
116 def on_close(self):
115 def on_close(self):
117 # This method can be called twice, once by self.kernel_died and once
116 # This method can be called twice, once by self.kernel_died and once
118 # from the WebSocket close event. If the WebSocket connection is
117 # from the WebSocket close event. If the WebSocket connection is
119 # closed before the ZMQ streams are setup, they could be None.
118 # closed before the ZMQ streams are setup, they could be None.
120 if self.zmq_stream is not None and not self.zmq_stream.closed():
119 if self.zmq_stream is not None and not self.zmq_stream.closed():
121 self.zmq_stream.on_recv(None)
120 self.zmq_stream.on_recv(None)
122 self.zmq_stream.close()
121 self.zmq_stream.close()
123
122
124
123
125 class IOPubHandler(ZMQChannelHandler):
124 class IOPubHandler(ZMQChannelHandler):
126 channel = 'iopub'
125 channel = 'iopub'
127
126
128 def create_stream(self):
127 def create_stream(self):
129 super(IOPubHandler, self).create_stream()
128 super(IOPubHandler, self).create_stream()
130 km = self.kernel_manager
129 km = self.kernel_manager
131 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
130 km.add_restart_callback(self.kernel_id, self.on_kernel_restarted)
132 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
131 km.add_restart_callback(self.kernel_id, self.on_restart_failed, 'dead')
133
132
134 def on_close(self):
133 def on_close(self):
135 km = self.kernel_manager
134 km = self.kernel_manager
136 if self.kernel_id in km:
135 if self.kernel_id in km:
137 km.remove_restart_callback(
136 km.remove_restart_callback(
138 self.kernel_id, self.on_kernel_restarted,
137 self.kernel_id, self.on_kernel_restarted,
139 )
138 )
140 km.remove_restart_callback(
139 km.remove_restart_callback(
141 self.kernel_id, self.on_restart_failed, 'dead',
140 self.kernel_id, self.on_restart_failed, 'dead',
142 )
141 )
143 super(IOPubHandler, self).on_close()
142 super(IOPubHandler, self).on_close()
144
143
145 def _send_status_message(self, status):
144 def _send_status_message(self, status):
146 msg = self.session.msg("status",
145 msg = self.session.msg("status",
147 {'execution_state': status}
146 {'execution_state': status}
148 )
147 )
149 self.write_message(jsonapi.dumps(msg, default=date_default))
148 self.write_message(jsonapi.dumps(msg, default=date_default))
150
149
151 def on_kernel_restarted(self):
150 def on_kernel_restarted(self):
152 logging.warn("kernel %s restarted", self.kernel_id)
151 logging.warn("kernel %s restarted", self.kernel_id)
153 self._send_status_message('restarting')
152 self._send_status_message('restarting')
154
153
155 def on_restart_failed(self):
154 def on_restart_failed(self):
156 logging.error("kernel %s restarted failed!", self.kernel_id)
155 logging.error("kernel %s restarted failed!", self.kernel_id)
157 self._send_status_message('dead')
156 self._send_status_message('dead')
158
157
159 def on_message(self, msg):
158 def on_message(self, msg):
160 """IOPub messages make no sense"""
159 """IOPub messages make no sense"""
161 pass
160 pass
162
161
163
162
164 class ShellHandler(ZMQChannelHandler):
163 class ShellHandler(ZMQChannelHandler):
165 channel = 'shell'
164 channel = 'shell'
166
165
167
166
168 class StdinHandler(ZMQChannelHandler):
167 class StdinHandler(ZMQChannelHandler):
169 channel = 'stdin'
168 channel = 'stdin'
170
169
171
170
172 #-----------------------------------------------------------------------------
171 #-----------------------------------------------------------------------------
173 # URL to handler mappings
172 # URL to handler mappings
174 #-----------------------------------------------------------------------------
173 #-----------------------------------------------------------------------------
175
174
176
175
177 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
176 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
178 _kernel_action_regex = r"(?P<action>restart|interrupt)"
177 _kernel_action_regex = r"(?P<action>restart|interrupt)"
179
178
180 default_handlers = [
179 default_handlers = [
181 (r"/api/kernels", MainKernelHandler),
180 (r"/api/kernels", MainKernelHandler),
182 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
181 (r"/api/kernels/%s" % _kernel_id_regex, KernelHandler),
183 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
182 (r"/api/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
184 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
183 (r"/api/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
185 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
184 (r"/api/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
186 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
185 (r"/api/kernels/%s/stdin" % _kernel_id_regex, StdinHandler)
187 ]
186 ]
@@ -1,23 +1,36 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.tests.launchnotebook import NotebookTestBase
10 from IPython.html.tests.launchnotebook import NotebookTestBase
11
11
12
12
13 class KernelAPITest(NotebookTestBase):
13 class KernelAPITest(NotebookTestBase):
14 """Test the kernels web service API"""
14 """Test the kernels web service API"""
15
15
16 def base_url(self):
16 def base_url(self):
17 return super(KernelAPITest,self).base_url() + 'api/kernels'
17 return super(KernelAPITest,self).base_url() + 'api/kernels'
18
18
19 def test_no_kernels(self):
19 def test_no_kernels(self):
20 """Make sure there are no kernels running at the start"""
20 """Make sure there are no kernels running at the start"""
21 url = self.base_url()
21 url = self.base_url()
22 r = requests.get(url)
22 r = requests.get(url)
23 assert r.json() == []
23 assert r.json() == []
24
25 def test_main_kernel_handler(self):
26 # POST request
27 r = requests.post(self.base_url())
28 data = r.json()
29 assert isinstance(data, dict)
30
31 # GET request
32 r = requests.get(self.base_url())
33 assert isinstance(r.json(), list)
34 self.assertEqual(r.json()[0], data['id'])
35
36 No newline at end of file
@@ -1,347 +1,347 b''
1 """A notebook manager that uses the local file system for storage.
1 """A notebook manager that uses the local file system for storage.
2
2
3 Authors:
3 Authors:
4
4
5 * Brian Granger
5 * Brian Granger
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 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 datetime
19 import datetime
20 import io
20 import io
21 import os
21 import os
22 import glob
22 import glob
23 import shutil
23 import shutil
24
24
25 from unicodedata import normalize
25 from unicodedata import normalize
26
26
27 from tornado import web
27 from tornado import web
28
28
29 from .nbmanager import NotebookManager
29 from .nbmanager import NotebookManager
30 from IPython.nbformat import current
30 from IPython.nbformat import current
31 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
31 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
32 from IPython.utils import tz
32 from IPython.utils import tz
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Classes
35 # Classes
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class FileNotebookManager(NotebookManager):
38 class FileNotebookManager(NotebookManager):
39
39
40 save_script = Bool(False, config=True,
40 save_script = Bool(False, config=True,
41 help="""Automatically create a Python script when saving the notebook.
41 help="""Automatically create a Python script when saving the notebook.
42
42
43 For easier use of import, %run and %load across notebooks, a
43 For easier use of import, %run and %load across notebooks, a
44 <notebook-name>.py script will be created next to any
44 <notebook-name>.py script will be created next to any
45 <notebook-name>.ipynb on each save. This can also be set with the
45 <notebook-name>.ipynb on each save. This can also be set with the
46 short `--script` flag.
46 short `--script` flag.
47 """
47 """
48 )
48 )
49
49
50 checkpoint_dir = Unicode(config=True,
50 checkpoint_dir = Unicode(config=True,
51 help="""The location in which to keep notebook checkpoints
51 help="""The location in which to keep notebook checkpoints
52
52
53 By default, it is notebook-dir/.ipynb_checkpoints
53 By default, it is notebook-dir/.ipynb_checkpoints
54 """
54 """
55 )
55 )
56 def _checkpoint_dir_default(self):
56 def _checkpoint_dir_default(self):
57 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
57 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
58
58
59 def _checkpoint_dir_changed(self, name, old, new):
59 def _checkpoint_dir_changed(self, name, old, new):
60 """do a bit of validation of the checkpoint dir"""
60 """do a bit of validation of the checkpoint dir"""
61 if not os.path.isabs(new):
61 if not os.path.isabs(new):
62 # If we receive a non-absolute path, make it absolute.
62 # If we receive a non-absolute path, make it absolute.
63 abs_new = os.path.abspath(new)
63 abs_new = os.path.abspath(new)
64 self.checkpoint_dir = abs_new
64 self.checkpoint_dir = abs_new
65 return
65 return
66 if os.path.exists(new) and not os.path.isdir(new):
66 if os.path.exists(new) and not os.path.isdir(new):
67 raise TraitError("checkpoint dir %r is not a directory" % new)
67 raise TraitError("checkpoint dir %r is not a directory" % new)
68 if not os.path.exists(new):
68 if not os.path.exists(new):
69 self.log.info("Creating checkpoint dir %s", new)
69 self.log.info("Creating checkpoint dir %s", new)
70 try:
70 try:
71 os.mkdir(new)
71 os.mkdir(new)
72 except:
72 except:
73 raise TraitError("Couldn't create checkpoint dir %r" % new)
73 raise TraitError("Couldn't create checkpoint dir %r" % new)
74
74
75 filename_ext = Unicode(u'.ipynb')
75 filename_ext = Unicode(u'.ipynb')
76
76
77
77
78 def get_notebook_names(self, path):
78 def get_notebook_names(self, path):
79 """List all notebook names in the notebook dir."""
79 """List all notebook names in the notebook dir."""
80 names = glob.glob(self.get_os_path('*'+self.filename_ext, path))
80 names = glob.glob(self.get_os_path('*'+self.filename_ext, path))
81 names = [os.path.basename(name)
81 names = [os.path.basename(name)
82 for name in names]
82 for name in names]
83 return names
83 return names
84
84
85 def list_notebooks(self, path):
85 def list_notebooks(self, path):
86 """List all notebooks in the notebook dir."""
86 """List all notebooks in the notebook dir."""
87 notebook_names = self.get_notebook_names(path)
87 notebook_names = self.get_notebook_names(path)
88 notebooks = []
88 notebooks = []
89 for name in notebook_names:
89 for name in notebook_names:
90 model = self.notebook_model(name, path, content=False)
90 model = self.notebook_model(name, path, content=False)
91 notebooks.append(model)
91 notebooks.append(model)
92 return notebooks
92 return notebooks
93
93
94 def change_notebook(self, data, notebook_name, notebook_path='/'):
94 def change_notebook(self, data, notebook_name, notebook_path='/'):
95 """Changes notebook"""
95 """Changes notebook"""
96 changes = data.keys()
96 changes = data.keys()
97 for change in changes:
97 for change in changes:
98 full_path = self.get_os_path(notebook_name, notebook_path)
98 full_path = self.get_os_path(notebook_name, notebook_path)
99 if change == "name":
99 if change == "name":
100 new_path = self.get_os_path(data['name'], notebook_path)
100 new_path = self.get_os_path(data['name'], notebook_path)
101 try:
101 if not os.path.isfile(new_path):
102 os.rename(full_path,
102 os.rename(full_path,
103 self.get_os_path(data['name'], notebook_path))
103 self.get_os_path(data['name'], notebook_path))
104 notebook_name = data['name']
104 notebook_name = data['name']
105 except OSError as e:
105 else:
106 raise web.HTTPError(409, u'Notebook name already exists.')
106 raise web.HTTPError(409, u'Notebook name already exists.')
107 if change == "path":
107 if change == "path":
108 new_path = self.get_os_path(data['name'], data['path'])
108 new_path = self.get_os_path(data['name'], data['path'])
109 stutil.move(full_path, new_path)
109 stutil.move(full_path, new_path)
110 notebook_path = data['path']
110 notebook_path = data['path']
111 if change == "content":
111 if change == "content":
112 self.save_notebook(data, notebook_name, notebook_path)
112 self.save_notebook(data, notebook_name, notebook_path)
113 model = self.notebook_model(notebook_name, notebook_path)
113 model = self.notebook_model(notebook_name, notebook_path)
114 return model
114 return model
115
115
116 def notebook_exists(self, name, path):
116 def notebook_exists(self, name, path):
117 """Returns a True if the notebook exists. Else, returns False.
117 """Returns a True if the notebook exists. Else, returns False.
118
118
119 Parameters
119 Parameters
120 ----------
120 ----------
121 name : string
121 name : string
122 The name of the notebook you are checking.
122 The name of the notebook you are checking.
123 path : string
123 path : string
124 The relative path to the notebook (with '/' as separator)
124 The relative path to the notebook (with '/' as separator)
125
125
126 Returns
126 Returns
127 -------
127 -------
128 bool
128 bool
129 """
129 """
130 path = self.get_os_path(name, path)
130 path = self.get_os_path(name, path)
131 return os.path.isfile(path)
131 return os.path.isfile(path)
132
132
133 def read_notebook_object_from_path(self, path):
133 def read_notebook_object_from_path(self, path):
134 """read a notebook object from a path"""
134 """read a notebook object from a path"""
135 info = os.stat(path)
135 info = os.stat(path)
136 last_modified = tz.utcfromtimestamp(info.st_mtime)
136 last_modified = tz.utcfromtimestamp(info.st_mtime)
137 with open(path,'r') as f:
137 with open(path,'r') as f:
138 s = f.read()
138 s = f.read()
139 try:
139 try:
140 # v1 and v2 and json in the .ipynb files.
140 # v1 and v2 and json in the .ipynb files.
141 nb = current.reads(s, u'json')
141 nb = current.reads(s, u'json')
142 except ValueError as e:
142 except ValueError as e:
143 msg = u"Unreadable Notebook: %s" % e
143 msg = u"Unreadable Notebook: %s" % e
144 raise web.HTTPError(400, msg, reason=msg)
144 raise web.HTTPError(400, msg, reason=msg)
145 return last_modified, nb
145 return last_modified, nb
146
146
147 def read_notebook_object(self, notebook_name, notebook_path='/'):
147 def read_notebook_object(self, notebook_name, notebook_path='/'):
148 """Get the Notebook representation of a notebook by notebook_name."""
148 """Get the Notebook representation of a notebook by notebook_name."""
149 path = self.get_os_path(notebook_name, notebook_path)
149 path = self.get_os_path(notebook_name, notebook_path)
150 if not os.path.isfile(path):
150 if not os.path.isfile(path):
151 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name)
151 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name)
152 last_modified, nb = self.read_notebook_object_from_path(path)
152 last_modified, nb = self.read_notebook_object_from_path(path)
153 # Always use the filename as the notebook name.
153 # Always use the filename as the notebook name.
154 # Eventually we will get rid of the notebook name in the metadata
154 # Eventually we will get rid of the notebook name in the metadata
155 # but for now, that name is just an empty string. Until the notebooks
155 # but for now, that name is just an empty string. Until the notebooks
156 # web service knows about names in URLs we still pass the name
156 # web service knows about names in URLs we still pass the name
157 # back to the web app using the metadata though.
157 # back to the web app using the metadata though.
158 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
158 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
159 return last_modified, nb
159 return last_modified, nb
160
160
161 def write_notebook_object(self, nb, notebook_name=None, notebook_path='/', new_name= None):
161 def write_notebook_object(self, nb, notebook_name=None, notebook_path='/', new_name= None):
162 """Save an existing notebook object by notebook_name."""
162 """Save an existing notebook object by notebook_name."""
163 if new_name == None:
163 if new_name == None:
164 try:
164 try:
165 new_name = normalize('NFC', nb.metadata.name)
165 new_name = normalize('NFC', nb.metadata.name)
166 except AttributeError:
166 except AttributeError:
167 raise web.HTTPError(400, u'Missing notebook name')
167 raise web.HTTPError(400, u'Missing notebook name')
168
168
169 new_path = notebook_path
169 new_path = notebook_path
170 old_name = notebook_name
170 old_name = notebook_name
171 old_checkpoints = self.list_checkpoints(old_name)
171 old_checkpoints = self.list_checkpoints(old_name)
172
172
173 path = self.get_os_path(new_name, new_path)
173 path = self.get_os_path(new_name, new_path)
174
174
175 # Right before we save the notebook, we write an empty string as the
175 # Right before we save the notebook, we write an empty string as the
176 # notebook name in the metadata. This is to prepare for removing
176 # notebook name in the metadata. This is to prepare for removing
177 # this attribute entirely post 1.0. The web app still uses the metadata
177 # this attribute entirely post 1.0. The web app still uses the metadata
178 # name for now.
178 # name for now.
179 nb.metadata.name = u''
179 nb.metadata.name = u''
180
180
181 try:
181 try:
182 self.log.debug("Autosaving notebook %s", path)
182 self.log.debug("Autosaving notebook %s", path)
183 with open(path,'w') as f:
183 with open(path,'w') as f:
184 current.write(nb, f, u'json')
184 current.write(nb, f, u'json')
185 except Exception as e:
185 except Exception as e:
186 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s' % e)
186 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s' % e)
187
187
188 # save .py script as well
188 # save .py script as well
189 if self.save_script:
189 if self.save_script:
190 pypath = os.path.splitext(path)[0] + '.py'
190 pypath = os.path.splitext(path)[0] + '.py'
191 self.log.debug("Writing script %s", pypath)
191 self.log.debug("Writing script %s", pypath)
192 try:
192 try:
193 with io.open(pypath,'w', encoding='utf-8') as f:
193 with io.open(pypath,'w', encoding='utf-8') as f:
194 current.write(nb, f, u'py')
194 current.write(nb, f, u'py')
195 except Exception as e:
195 except Exception as e:
196 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
196 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
197
197
198 if old_name != None:
198 if old_name != None:
199 # remove old files if the name changed
199 # remove old files if the name changed
200 if old_name != new_name:
200 if old_name != new_name:
201 # remove renamed original, if it exists
201 # remove renamed original, if it exists
202 old_path = self.get_os_path(old_name, notebook_path)
202 old_path = self.get_os_path(old_name, notebook_path)
203 if os.path.isfile(old_path):
203 if os.path.isfile(old_path):
204 self.log.debug("unlinking notebook %s", old_path)
204 self.log.debug("unlinking notebook %s", old_path)
205 os.unlink(old_path)
205 os.unlink(old_path)
206
206
207 # cleanup old script, if it exists
207 # cleanup old script, if it exists
208 if self.save_script:
208 if self.save_script:
209 old_pypath = os.path.splitext(old_path)[0] + '.py'
209 old_pypath = os.path.splitext(old_path)[0] + '.py'
210 if os.path.isfile(old_pypath):
210 if os.path.isfile(old_pypath):
211 self.log.debug("unlinking script %s", old_pypath)
211 self.log.debug("unlinking script %s", old_pypath)
212 os.unlink(old_pypath)
212 os.unlink(old_pypath)
213
213
214 # rename checkpoints to follow file
214 # rename checkpoints to follow file
215 for cp in old_checkpoints:
215 for cp in old_checkpoints:
216 checkpoint_id = cp['checkpoint_id']
216 checkpoint_id = cp['checkpoint_id']
217 old_cp_path = self.get_checkpoint_path_by_name(old_name, checkpoint_id)
217 old_cp_path = self.get_checkpoint_path_by_name(old_name, checkpoint_id)
218 new_cp_path = self.get_checkpoint_path_by_name(new_name, checkpoint_id)
218 new_cp_path = self.get_checkpoint_path_by_name(new_name, checkpoint_id)
219 if os.path.isfile(old_cp_path):
219 if os.path.isfile(old_cp_path):
220 self.log.debug("renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
220 self.log.debug("renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
221 os.rename(old_cp_path, new_cp_path)
221 os.rename(old_cp_path, new_cp_path)
222
222
223 return new_name
223 return new_name
224
224
225 def delete_notebook(self, notebook_name, notebook_path):
225 def delete_notebook(self, notebook_name, notebook_path):
226 """Delete notebook by notebook_name."""
226 """Delete notebook by notebook_name."""
227 nb_path = self.get_os_path(notebook_name, notebook_path)
227 nb_path = self.get_os_path(notebook_name, notebook_path)
228 if not os.path.isfile(nb_path):
228 if not os.path.isfile(nb_path):
229 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name)
229 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_name)
230
230
231 # clear checkpoints
231 # clear checkpoints
232 for checkpoint in self.list_checkpoints(notebook_name):
232 for checkpoint in self.list_checkpoints(notebook_name):
233 checkpoint_id = checkpoint['checkpoint_id']
233 checkpoint_id = checkpoint['checkpoint_id']
234 path = self.get_checkpoint_path(notebook_name, checkpoint_id)
234 path = self.get_checkpoint_path(notebook_name, checkpoint_id)
235 self.log.debug(path)
235 self.log.debug(path)
236 if os.path.isfile(path):
236 if os.path.isfile(path):
237 self.log.debug("unlinking checkpoint %s", path)
237 self.log.debug("unlinking checkpoint %s", path)
238 os.unlink(path)
238 os.unlink(path)
239
239
240 self.log.debug("unlinking notebook %s", nb_path)
240 self.log.debug("unlinking notebook %s", nb_path)
241 os.unlink(nb_path)
241 os.unlink(nb_path)
242
242
243 def increment_filename(self, basename, notebook_path='/'):
243 def increment_filename(self, basename, notebook_path='/'):
244 """Return a non-used filename of the form basename<int>.
244 """Return a non-used filename of the form basename<int>.
245
245
246 This searches through the filenames (basename0, basename1, ...)
246 This searches through the filenames (basename0, basename1, ...)
247 until is find one that is not already being used. It is used to
247 until is find one that is not already being used. It is used to
248 create Untitled and Copy names that are unique.
248 create Untitled and Copy names that are unique.
249 """
249 """
250 i = 0
250 i = 0
251 while True:
251 while True:
252 name = u'%s%i.ipynb' % (basename,i)
252 name = u'%s%i.ipynb' % (basename,i)
253 path = self.get_os_path(name, notebook_path)
253 path = self.get_os_path(name, notebook_path)
254 if not os.path.isfile(path):
254 if not os.path.isfile(path):
255 break
255 break
256 else:
256 else:
257 i = i+1
257 i = i+1
258 return name
258 return name
259
259
260 # Checkpoint-related utilities
260 # Checkpoint-related utilities
261
261
262 def get_checkpoint_path_by_name(self, name, checkpoint_id, notebook_path='/'):
262 def get_checkpoint_path_by_name(self, name, checkpoint_id, notebook_path='/'):
263 """Return a full path to a notebook checkpoint, given its name and checkpoint id."""
263 """Return a full path to a notebook checkpoint, given its name and checkpoint id."""
264 filename = u"{name}-{checkpoint_id}{ext}".format(
264 filename = u"{name}-{checkpoint_id}{ext}".format(
265 name=name,
265 name=name,
266 checkpoint_id=checkpoint_id,
266 checkpoint_id=checkpoint_id,
267 ext=self.filename_ext,
267 ext=self.filename_ext,
268 )
268 )
269 if notebook_path ==None:
269 if notebook_path ==None:
270 path = os.path.join(self.checkpoint_dir, filename)
270 path = os.path.join(self.checkpoint_dir, filename)
271 else:
271 else:
272 path = os.path.join(notebook_path, self.checkpoint_dir, filename)
272 path = os.path.join(notebook_path, self.checkpoint_dir, filename)
273 return path
273 return path
274
274
275 def get_checkpoint_path(self, notebook_name, checkpoint_id, notebook_path='/'):
275 def get_checkpoint_path(self, notebook_name, checkpoint_id, notebook_path='/'):
276 """find the path to a checkpoint"""
276 """find the path to a checkpoint"""
277 name = notebook_name
277 name = notebook_name
278 return self.get_checkpoint_path_by_name(name, checkpoint_id, notebook_path)
278 return self.get_checkpoint_path_by_name(name, checkpoint_id, notebook_path)
279
279
280 def get_checkpoint_info(self, notebook_name, checkpoint_id, notebook_path='/'):
280 def get_checkpoint_info(self, notebook_name, checkpoint_id, notebook_path='/'):
281 """construct the info dict for a given checkpoint"""
281 """construct the info dict for a given checkpoint"""
282 path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
282 path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
283 stats = os.stat(path)
283 stats = os.stat(path)
284 last_modified = tz.utcfromtimestamp(stats.st_mtime)
284 last_modified = tz.utcfromtimestamp(stats.st_mtime)
285 info = dict(
285 info = dict(
286 checkpoint_id = checkpoint_id,
286 checkpoint_id = checkpoint_id,
287 last_modified = last_modified,
287 last_modified = last_modified,
288 )
288 )
289
289
290 return info
290 return info
291
291
292 # public checkpoint API
292 # public checkpoint API
293
293
294 def create_checkpoint(self, notebook_name, notebook_path='/'):
294 def create_checkpoint(self, notebook_name, notebook_path='/'):
295 """Create a checkpoint from the current state of a notebook"""
295 """Create a checkpoint from the current state of a notebook"""
296 nb_path = self.get_os_path(notebook_name, notebook_path)
296 nb_path = self.get_os_path(notebook_name, notebook_path)
297 # only the one checkpoint ID:
297 # only the one checkpoint ID:
298 checkpoint_id = u"checkpoint"
298 checkpoint_id = u"checkpoint"
299 cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
299 cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
300 self.log.debug("creating checkpoint for notebook %s", notebook_name)
300 self.log.debug("creating checkpoint for notebook %s", notebook_name)
301 if not os.path.exists(self.checkpoint_dir):
301 if not os.path.exists(self.checkpoint_dir):
302 os.mkdir(self.checkpoint_dir)
302 os.mkdir(self.checkpoint_dir)
303 shutil.copy2(nb_path, cp_path)
303 shutil.copy2(nb_path, cp_path)
304
304
305 # return the checkpoint info
305 # return the checkpoint info
306 return self.get_checkpoint_info(notebook_name, checkpoint_id, notebook_path)
306 return self.get_checkpoint_info(notebook_name, checkpoint_id, notebook_path)
307
307
308 def list_checkpoints(self, notebook_name, notebook_path='/'):
308 def list_checkpoints(self, notebook_name, notebook_path='/'):
309 """list the checkpoints for a given notebook
309 """list the checkpoints for a given notebook
310
310
311 This notebook manager currently only supports one checkpoint per notebook.
311 This notebook manager currently only supports one checkpoint per notebook.
312 """
312 """
313 checkpoint_id = "checkpoint"
313 checkpoint_id = "checkpoint"
314 path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
314 path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
315 if not os.path.exists(path):
315 if not os.path.exists(path):
316 return []
316 return []
317 else:
317 else:
318 return [self.get_checkpoint_info(notebook_name, checkpoint_id, notebook_path)]
318 return [self.get_checkpoint_info(notebook_name, checkpoint_id, notebook_path)]
319
319
320
320
321 def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path='/'):
321 def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path='/'):
322 """restore a notebook to a checkpointed state"""
322 """restore a notebook to a checkpointed state"""
323 self.log.info("restoring Notebook %s from checkpoint %s", notebook_name, checkpoint_id)
323 self.log.info("restoring Notebook %s from checkpoint %s", notebook_name, checkpoint_id)
324 nb_path = self.get_os_path(notebook_name, notebook_path)
324 nb_path = self.get_os_path(notebook_name, notebook_path)
325 cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
325 cp_path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
326 if not os.path.isfile(cp_path):
326 if not os.path.isfile(cp_path):
327 self.log.debug("checkpoint file does not exist: %s", cp_path)
327 self.log.debug("checkpoint file does not exist: %s", cp_path)
328 raise web.HTTPError(404,
328 raise web.HTTPError(404,
329 u'Notebook checkpoint does not exist: %s-%s' % (notebook_name, checkpoint_id)
329 u'Notebook checkpoint does not exist: %s-%s' % (notebook_name, checkpoint_id)
330 )
330 )
331 # ensure notebook is readable (never restore from an unreadable notebook)
331 # ensure notebook is readable (never restore from an unreadable notebook)
332 last_modified, nb = self.read_notebook_object_from_path(cp_path)
332 last_modified, nb = self.read_notebook_object_from_path(cp_path)
333 shutil.copy2(cp_path, nb_path)
333 shutil.copy2(cp_path, nb_path)
334 self.log.debug("copying %s -> %s", cp_path, nb_path)
334 self.log.debug("copying %s -> %s", cp_path, nb_path)
335
335
336 def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path='/'):
336 def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path='/'):
337 """delete a notebook's checkpoint"""
337 """delete a notebook's checkpoint"""
338 path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
338 path = self.get_checkpoint_path(notebook_name, checkpoint_id, notebook_path)
339 if not os.path.isfile(path):
339 if not os.path.isfile(path):
340 raise web.HTTPError(404,
340 raise web.HTTPError(404,
341 u'Notebook checkpoint does not exist: %s-%s' % (notebook_name, checkpoint_id)
341 u'Notebook checkpoint does not exist: %s-%s' % (notebook_name, checkpoint_id)
342 )
342 )
343 self.log.debug("unlinking %s", path)
343 self.log.debug("unlinking %s", path)
344 os.unlink(path)
344 os.unlink(path)
345
345
346 def info_string(self):
346 def info_string(self):
347 return "Serving notebooks from local directory: %s" % self.notebook_dir
347 return "Serving notebooks from local directory: %s" % self.notebook_dir
@@ -1,216 +1,217 b''
1 """Tornado handlers for the notebooks web service.
1 """Tornado handlers for the notebooks web service.
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 from tornado import web
19 from tornado import web
20
20
21 from zmq.utils import jsonapi
21 from zmq.utils import jsonapi
22
22
23 from IPython.utils.jsonutil import date_default
23 from IPython.utils.jsonutil import date_default
24
24
25 from ...base.handlers import IPythonHandler
25 from ...base.handlers import IPythonHandler
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Notebook web service handlers
28 # Notebook web service handlers
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31
31
32 class NotebookRootHandler(IPythonHandler):
32 class NotebookRootHandler(IPythonHandler):
33
33
34 @web.authenticated
34 @web.authenticated
35 def get(self):
35 def get(self):
36 """get returns a list of notebooks from the location
36 """get returns a list of notebooks from the location
37 where the server was started."""
37 where the server was started."""
38 nbm = self.notebook_manager
38 nbm = self.notebook_manager
39 notebooks = nbm.list_notebooks("/")
39 notebooks = nbm.list_notebooks("/")
40 self.finish(jsonapi.dumps(notebooks))
40 self.finish(jsonapi.dumps(notebooks))
41
41
42 @web.authenticated
42 @web.authenticated
43 def post(self):
43 def post(self):
44 """post creates a notebooks in the directory where the
44 """post creates a notebooks in the directory where the
45 server was started"""
45 server was started"""
46 nbm = self.notebook_manager
46 nbm = self.notebook_manager
47 self.log.info(nbm.notebook_dir)
47 body = self.request.body.strip()
48 body = self.request.body.strip()
48 format = self.get_argument('format', default='json')
49 format = self.get_argument('format', default='json')
49 name = self.get_argument('name', default=None)
50 name = self.get_argument('name', default=None)
50 if body:
51 if body:
51 fname = nbm.save_new_notebook(body, notebook_path='/', name=name, format=format)
52 fname = nbm.save_new_notebook(body, notebook_path='/', name=name, format=format)
52 else:
53 else:
53 fname = nbm.new_notebook(notebook_path='/')
54 fname = nbm.new_notebook(notebook_path='/')
54 self.set_header('Location', nbm.notebook_dir + fname)
55 self.set_header('Location', nbm.notebook_dir + fname)
55 model = nbm.notebook_model(fname)
56 model = nbm.notebook_model(fname)
56 self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, fname))
57 self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, fname))
57 self.finish(jsonapi.dumps(model))
58 self.finish(jsonapi.dumps(model))
58
59
59 class NotebookHandler(IPythonHandler):
60 class NotebookHandler(IPythonHandler):
60
61
61 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE')
62 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE')
62
63
63 @web.authenticated
64 @web.authenticated
64 def get(self, notebook_path):
65 def get(self, notebook_path):
65 """get checks if a notebook is not named, an returns a list of notebooks
66 """get checks if a notebook is not named, an returns a list of notebooks
66 in the notebook path given. If a name is given, return
67 in the notebook path given. If a name is given, return
67 the notebook representation"""
68 the notebook representation"""
68 nbm = self.notebook_manager
69 nbm = self.notebook_manager
69 name, path = nbm.named_notebook_path(notebook_path)
70 name, path = nbm.named_notebook_path(notebook_path)
70
71
71 # Check to see if a notebook name was given
72 # Check to see if a notebook name was given
72 if name is None:
73 if name is None:
73 # List notebooks in 'notebook_path'
74 # List notebooks in 'notebook_path'
74 notebooks = nbm.list_notebooks(path)
75 notebooks = nbm.list_notebooks(path)
75 self.finish(jsonapi.dumps(notebooks))
76 self.finish(jsonapi.dumps(notebooks))
76 else:
77 else:
77 # get and return notebook representation
78 # get and return notebook representation
78 format = self.get_argument('format', default='json')
79 format = self.get_argument('format', default='json')
79 download = self.get_argument('download', default='False')
80 download = self.get_argument('download', default='False')
80 model = nbm.notebook_model(name, path)
81 model = nbm.notebook_model(name, path)
81 last_mod, representation, name = nbm.get_notebook(name, path, format)
82 last_mod, representation, name = nbm.get_notebook(name, path, format)
82 self.set_header('Last-Modified', last_mod)
83 self.set_header('Last-Modified', last_mod)
83
84
84 if download == 'True':
85 if download == 'True':
85 if format == u'json':
86 if format == u'json':
86 self.set_header('Content-Type', 'application/json')
87 self.set_header('Content-Type', 'application/json')
87 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
88 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
88 self.finish(representation)
89 self.finish(representation)
89 elif format == u'py':
90 elif format == u'py':
90 self.set_header('Content-Type', 'application/x-python')
91 self.set_header('Content-Type', 'application/x-python')
91 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
92 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
92 self.finish(representation)
93 self.finish(representation)
93 else:
94 else:
94 self.finish(jsonapi.dumps(model))
95 self.finish(jsonapi.dumps(model))
95
96
96 @web.authenticated
97 @web.authenticated
97 def patch(self, notebook_path):
98 def patch(self, notebook_path):
98 """patch is currently used strictly for notebook renaming.
99 """patch is currently used strictly for notebook renaming.
99 Changes the notebook name to the name given in data."""
100 Changes the notebook name to the name given in data."""
100 nbm = self.notebook_manager
101 nbm = self.notebook_manager
101 name, path = nbm.named_notebook_path(notebook_path)
102 name, path = nbm.named_notebook_path(notebook_path)
102 data = jsonapi.loads(self.request.body)
103 data = jsonapi.loads(self.request.body)
103 model = nbm.change_notebook(data, name, path)
104 model = nbm.change_notebook(data, name, path)
104 self.finish(jsonapi.dumps(model))
105 self.finish(jsonapi.dumps(model))
105
106
106 @web.authenticated
107 @web.authenticated
107 def post(self,notebook_path):
108 def post(self,notebook_path):
108 """Create a new notebook in the location given by 'notebook_path'."""
109 """Create a new notebook in the location given by 'notebook_path'."""
109 nbm = self.notebook_manager
110 nbm = self.notebook_manager
110 fname, path = nbm.named_notebook_path(notebook_path)
111 fname, path = nbm.named_notebook_path(notebook_path)
111 body = self.request.body.strip()
112 body = self.request.body.strip()
112 format = self.get_argument('format', default='json')
113 format = self.get_argument('format', default='json')
113 name = self.get_argument('name', default=None)
114 name = self.get_argument('name', default=None)
114 if body:
115 if body:
115 fname = nbm.save_new_notebook(body, notebook_path=path, name=name, format=format)
116 fname = nbm.save_new_notebook(body, notebook_path=path, name=name, format=format)
116 else:
117 else:
117 fname = nbm.new_notebook(notebook_path=path)
118 fname = nbm.new_notebook(notebook_path=path)
118 self.set_header('Location', nbm.notebook_dir + path + fname)
119 self.set_header('Location', nbm.notebook_dir + path + fname)
119 model = nbm.notebook_model(fname, path)
120 model = nbm.notebook_model(fname, path)
120 self.finish(jsonapi.dumps(model))
121 self.finish(jsonapi.dumps(model))
121
122
122 @web.authenticated
123 @web.authenticated
123 def put(self, notebook_path):
124 def put(self, notebook_path):
124 """saves the notebook in the location given by 'notebook_path'."""
125 """saves the notebook in the location given by 'notebook_path'."""
125 nbm = self.notebook_manager
126 nbm = self.notebook_manager
126 name, path = nbm.named_notebook_path(notebook_path)
127 name, path = nbm.named_notebook_path(notebook_path)
127 format = self.get_argument('format', default='json')
128 format = self.get_argument('format', default='json')
128 nbm.save_notebook(self.request.body, notebook_path=path, name=name, format=format)
129 nbm.save_notebook(self.request.body, notebook_path=path, name=name, format=format)
129 model = nbm.notebook_model(name, path)
130 model = nbm.notebook_model(name, path)
130 self.set_status(204)
131 self.set_status(204)
131 self.finish(jsonapi.dumps(model))
132 self.finish(jsonapi.dumps(model))
132
133
133 @web.authenticated
134 @web.authenticated
134 def delete(self, notebook_path):
135 def delete(self, notebook_path):
135 """delete rmoves the notebook in the given notebook path"""
136 """delete rmoves the notebook in the given notebook path"""
136 nbm = self.notebook_manager
137 nbm = self.notebook_manager
137 name, path = nbm.named_notebook_path(notebook_path)
138 name, path = nbm.named_notebook_path(notebook_path)
138 nbm.delete_notebook(name, path)
139 nbm.delete_notebook(name, path)
139 self.set_status(204)
140 self.set_status(204)
140 self.finish()
141 self.finish()
141
142
142
143
143 class NotebookCheckpointsHandler(IPythonHandler):
144 class NotebookCheckpointsHandler(IPythonHandler):
144
145
145 SUPPORTED_METHODS = ('GET', 'POST')
146 SUPPORTED_METHODS = ('GET', 'POST')
146
147
147 @web.authenticated
148 @web.authenticated
148 def get(self, notebook_path):
149 def get(self, notebook_path):
149 """get lists checkpoints for a notebook"""
150 """get lists checkpoints for a notebook"""
150 nbm = self.notebook_manager
151 nbm = self.notebook_manager
151 name, path = nbm.named_notebook_path(notebook_path)
152 name, path = nbm.named_notebook_path(notebook_path)
152 checkpoints = nbm.list_checkpoints(name, path)
153 checkpoints = nbm.list_checkpoints(name, path)
153 data = jsonapi.dumps(checkpoints, default=date_default)
154 data = jsonapi.dumps(checkpoints, default=date_default)
154 self.finish(data)
155 self.finish(data)
155
156
156 @web.authenticated
157 @web.authenticated
157 def post(self, notebook_path):
158 def post(self, notebook_path):
158 """post creates a new checkpoint"""
159 """post creates a new checkpoint"""
159 nbm = self.notebook_manager
160 nbm = self.notebook_manager
160 name, path = nbm.named_notebook_path(notebook_path)
161 name, path = nbm.named_notebook_path(notebook_path)
161 checkpoint = nbm.create_checkpoint(name, path)
162 checkpoint = nbm.create_checkpoint(name, path)
162 data = jsonapi.dumps(checkpoint, default=date_default)
163 data = jsonapi.dumps(checkpoint, default=date_default)
163 if path == None:
164 if path == None:
164 self.set_header('Location', '{0}notebooks/{1}/checkpoints/{2}'.format(
165 self.set_header('Location', '{0}notebooks/{1}/checkpoints/{2}'.format(
165 self.base_project_url, name, checkpoint['checkpoint_id']
166 self.base_project_url, name, checkpoint['checkpoint_id']
166 ))
167 ))
167 else:
168 else:
168 self.set_header('Location', '{0}notebooks/{1}/{2}/checkpoints/{3}'.format(
169 self.set_header('Location', '{0}notebooks/{1}/{2}/checkpoints/{3}'.format(
169 self.base_project_url, path, name, checkpoint['checkpoint_id']
170 self.base_project_url, path, name, checkpoint['checkpoint_id']
170 ))
171 ))
171 self.finish(data)
172 self.finish(data)
172
173
173
174
174 class ModifyNotebookCheckpointsHandler(IPythonHandler):
175 class ModifyNotebookCheckpointsHandler(IPythonHandler):
175
176
176 SUPPORTED_METHODS = ('POST', 'DELETE')
177 SUPPORTED_METHODS = ('POST', 'DELETE')
177
178
178 @web.authenticated
179 @web.authenticated
179 def post(self, notebook_path, checkpoint_id):
180 def post(self, notebook_path, checkpoint_id):
180 """post restores a notebook from a checkpoint"""
181 """post restores a notebook from a checkpoint"""
181 nbm = self.notebook_manager
182 nbm = self.notebook_manager
182 name, path = nbm.named_notebook_path(notebook_path)
183 name, path = nbm.named_notebook_path(notebook_path)
183 nbm.restore_checkpoint(name, checkpoint_id, path)
184 nbm.restore_checkpoint(name, checkpoint_id, path)
184 self.set_status(204)
185 self.set_status(204)
185 self.finish()
186 self.finish()
186
187
187 @web.authenticated
188 @web.authenticated
188 def delete(self, notebook_path, checkpoint_id):
189 def delete(self, notebook_path, checkpoint_id):
189 """delete clears a checkpoint for a given notebook"""
190 """delete clears a checkpoint for a given notebook"""
190 nbm = self.notebook_manager
191 nbm = self.notebook_manager
191 name, path = nbm.named_notebook_path(notebook_path)
192 name, path = nbm.named_notebook_path(notebook_path)
192 nbm.delete_checkpoint(name, checkpoint_id, path)
193 nbm.delete_checkpoint(name, checkpoint_id, path)
193 self.set_status(204)
194 self.set_status(204)
194 self.finish()
195 self.finish()
195
196
196 #-----------------------------------------------------------------------------
197 #-----------------------------------------------------------------------------
197 # URL to handler mappings
198 # URL to handler mappings
198 #-----------------------------------------------------------------------------
199 #-----------------------------------------------------------------------------
199
200
200
201
201 _notebook_path_regex = r"(?P<notebook_path>.+)"
202 _notebook_path_regex = r"(?P<notebook_path>.+)"
202 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
203 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
203
204
204 default_handlers = [
205 default_handlers = [
205 (r"api/notebooks/%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler),
206 (r"api/notebooks/%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler),
206 (r"api/notebooks/%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex),
207 (r"api/notebooks/%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex),
207 ModifyNotebookCheckpointsHandler),
208 ModifyNotebookCheckpointsHandler),
208 (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler),
209 (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler),
209 (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler),
210 (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler),
210 (r"api/notebooks/", NotebookRootHandler),
211 (r"api/notebooks/", NotebookRootHandler),
211 (r"api/notebooks", NotebookRootHandler),
212 (r"api/notebooks", NotebookRootHandler),
212 ]
213 ]
213
214
214
215
215
216
216
217
@@ -1,118 +1,116 b''
1 """Test the all of the services API."""
1 """Test the notebooks webservice API."""
2
2
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import json
6 import json
7 import urllib
8 from zmq.utils import jsonapi
7 from zmq.utils import jsonapi
9
8
10 import requests
9 import requests
11
10
12 from IPython.html.tests.launchnotebook import NotebookTestBase
11 from IPython.html.tests.launchnotebook import NotebookTestBase
13
12
14 class APITest(NotebookTestBase):
13 class APITest(NotebookTestBase):
15 """Test the kernels web service API"""
14 """Test the kernels web service API"""
16
15
17 def base_url(self):
16 def notebook_url(self):
18 return super(APITest,self).base_url()
17 return super(APITest,self).base_url() + 'api/notebooks'
19
20 def notebooks_url(self):
21 return self.base_url() + 'api/notebooks'
22
18
23 def mknb(self, name='', path='/'):
19 def mknb(self, name='', path='/'):
24 url = self.notebooks_url() + path
20 url = self.notebook_url() + path
25 return url, requests.post(url)
21 return url, requests.post(url)
26
22
27 def delnb(self, name, path='/'):
23 def delnb(self, name, path='/'):
28 url = self.notebooks_url() + path + name
24 url = self.notebook_url() + path + name
29 r = requests.delete(url)
25 r = requests.delete(url)
30 return r.status_code
26 return r.status_code
31
27
32 def test_no_notebooks(self):
33 url = self.notebooks_url()
34 r = requests.get(url)
35 self.assertEqual(r.json(), [])
36
37 def test_notebook_root_handler(self):
28 def test_notebook_root_handler(self):
38 # POST a notebook and test the dict thats returned.
29 # POST a notebook and test the dict thats returned.
39 url, nb = self.mknb()
30 #url, nb = self.mknb()
31 url = self.notebook_url()
32 nb = requests.post(url)
40 data = nb.json()
33 data = nb.json()
41 assert isinstance(data, dict)
34 assert isinstance(data, dict)
42 assert data.has_key("name")
35 assert data.has_key("name")
43 assert data.has_key("path")
36 assert data.has_key("path")
44 self.assertEqual(data['name'], u'Untitled0.ipynb')
37 self.assertEqual(data['name'], u'Untitled0.ipynb')
45 self.assertEqual(data['path'], u'/')
38 self.assertEqual(data['path'], u'/')
46
39
47 # GET list of notebooks in directory.
40 # GET list of notebooks in directory.
48 r = requests.get(url)
41 r = requests.get(url)
49 assert isinstance(r.json(), list)
42 assert isinstance(r.json(), list)
50 assert isinstance(r.json()[0], dict)
43 assert isinstance(r.json()[0], dict)
44
45 self.delnb('Untitled0.ipynb')
51
46
52 def test_notebook_handler(self):
47 def test_notebook_handler(self):
53 # GET with a notebook name.
48 # GET with a notebook name.
54 url, nb = self.mknb()
49 url, nb = self.mknb()
55 data = nb.json()
50 data = nb.json()
56 url = self.notebooks_url() + '/Untitled0.ipynb'
51 url = self.notebook_url() + '/Untitled0.ipynb'
57 r = requests.get(url)
52 r = requests.get(url)
58 assert isinstance(data, dict)
53 assert isinstance(data, dict)
59 self.assertEqual(r.json(), data)
54 self.assertEqual(r.json(), data)
60
55
61 # PATCH (rename) request.
56 # PATCH (rename) request.
62 new_name = {'name':'test.ipynb'}
57 new_name = {'name':'test.ipynb'}
63 r = requests.patch(url, data=jsonapi.dumps(new_name))
58 r = requests.patch(url, data=jsonapi.dumps(new_name))
64 data = r.json()
59 data = r.json()
65 assert isinstance(data, dict)
60 assert isinstance(data, dict)
66
61
67 # make sure the patch worked.
62 # make sure the patch worked.
68 new_url = self.notebooks_url() + '/test.ipynb'
63 new_url = self.notebook_url() + '/test.ipynb'
69 r = requests.get(new_url)
64 r = requests.get(new_url)
70 assert isinstance(r.json(), dict)
65 assert isinstance(r.json(), dict)
71 self.assertEqual(r.json(), data)
66 self.assertEqual(r.json(), data)
72
67
73 # GET bad (old) notebook name.
68 # GET bad (old) notebook name.
74 r = requests.get(url)
69 r = requests.get(url)
75 self.assertEqual(r.status_code, 404)
70 self.assertEqual(r.status_code, 404)
76
71
77 # POST notebooks to folders one and two levels down.
72 # POST notebooks to folders one and two levels down.
78 os.makedirs(os.path.join(self.notebook_dir.name, 'foo'))
73 os.makedirs(os.path.join(self.notebook_dir.name, 'foo'))
79 os.makedirs(os.path.join(self.notebook_dir.name, 'foo','bar'))
74 os.makedirs(os.path.join(self.notebook_dir.name, 'foo','bar'))
75 assert os.path.isdir(os.path.join(self.notebook_dir.name, 'foo'))
80 url, nb = self.mknb(path='/foo/')
76 url, nb = self.mknb(path='/foo/')
81 url2, nb2 = self.mknb(path='/foo/bar/')
77 url2, nb2 = self.mknb(path='/foo/bar/')
82 data = nb.json()
78 data = nb.json()
83 data2 = nb2.json()
79 data2 = nb2.json()
84 assert isinstance(data, dict)
80 assert isinstance(data, dict)
85 assert isinstance(data2, dict)
81 assert isinstance(data2, dict)
86 assert data.has_key("name")
82 assert data.has_key("name")
87 assert data.has_key("path")
83 assert data.has_key("path")
88 self.assertEqual(data['name'], u'Untitled0.ipynb')
84 self.assertEqual(data['name'], u'Untitled0.ipynb')
89 self.assertEqual(data['path'], u'/foo/')
85 self.assertEqual(data['path'], u'/foo/')
90 assert data2.has_key("name")
86 assert data2.has_key("name")
91 assert data2.has_key("path")
87 assert data2.has_key("path")
92 self.assertEqual(data2['name'], u'Untitled0.ipynb')
88 self.assertEqual(data2['name'], u'Untitled0.ipynb')
93 self.assertEqual(data2['path'], u'/foo/bar/')
89 self.assertEqual(data2['path'], u'/foo/bar/')
94
90
95 # GET request on notebooks one and two levels down.
91 # GET request on notebooks one and two levels down.
96 r = requests.get(url+'Untitled0.ipynb')
92 r = requests.get(url+'Untitled0.ipynb')
97 r2 = requests.get(url2+'Untitled0.ipynb')
93 r2 = requests.get(url2+'Untitled0.ipynb')
98 assert isinstance(r.json(), dict)
94 assert isinstance(r.json(), dict)
99 self.assertEqual(r.json(), data)
95 self.assertEqual(r.json(), data)
100 assert isinstance(r2.json(), dict)
96 assert isinstance(r2.json(), dict)
101 self.assertEqual(r2.json(), data2)
97 self.assertEqual(r2.json(), data2)
102
98
103 # PATCH notebooks that are one and two levels down.
99 # PATCH notebooks that are one and two levels down.
104 new_name = {'name': 'testfoo.ipynb'}
100 new_name = {'name': 'testfoo.ipynb'}
105 r = requests.patch(url+'Untitled0.ipynb', data=jsonapi.dumps(new_name))
101 r = requests.patch(url+'Untitled0.ipynb', data=jsonapi.dumps(new_name))
106 r = requests.get(url+'testfoo.ipynb')
102 r = requests.get(url+'testfoo.ipynb')
107 data = r.json()
103 data = r.json()
108 assert isinstance(data, dict)
104 assert isinstance(data, dict)
109 assert data.has_key('name')
105 assert data.has_key('name')
110 self.assertEqual(data['name'], 'testfoo.ipynb')
106 self.assertEqual(data['name'], 'testfoo.ipynb')
111 r = requests.get(url+'Untitled0.ipynb')
107 r = requests.get(url+'Untitled0.ipynb')
112 self.assertEqual(r.status_code, 404)
108 self.assertEqual(r.status_code, 404)
113
109
114 # DELETE notebooks
110 # DELETE notebooks
115 r = self.delnb('testfoo.ipynb', '/foo/')
111 r0 = self.delnb('test.ipynb')
116 r2 = self.delnb('Untitled0.ipynb', '/foo/bar/')
112 r1 = self.delnb('testfoo.ipynb', '/foo/')
117 self.assertEqual(r, 204)
113 r2 = self.delnb('Untitled0.ipynb', '/foo/bar/')
118 self.assertEqual(r2, 204) No newline at end of file
114 self.assertEqual(r0, 204)
115 self.assertEqual(r1, 204)
116 self.assertEqual(r2, 204)
@@ -1,112 +1,114 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 from tornado import web
19 from tornado import web
20
20
21 from zmq.utils import jsonapi
21 from zmq.utils import jsonapi
22
22
23 from IPython.utils.jsonutil import date_default
23 from IPython.utils.jsonutil import date_default
24 from ...base.handlers import IPythonHandler
24 from ...base.handlers import IPythonHandler
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Session web service handlers
27 # Session web service handlers
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30
30
31 class SessionRootHandler(IPythonHandler):
31 class SessionRootHandler(IPythonHandler):
32
32
33 @web.authenticated
33 @web.authenticated
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 nbm = self.notebook_manager
37 nbm = self.notebook_manager
38 km = self.kernel_manager
38 km = self.kernel_manager
39 sessions = sm.list_sessions()
39 sessions = sm.list_sessions()
40 self.finish(jsonapi.dumps(sessions))
40 self.finish(jsonapi.dumps(sessions))
41
41
42 @web.authenticated
42 @web.authenticated
43 def post(self):
43 def post(self):
44 # Creates a new session
44 # Creates a new session
45 #(unless a session already exists for the named nb)
45 #(unless a session already exists for the named nb)
46 sm = self.session_manager
46 sm = self.session_manager
47 nbm = self.notebook_manager
47 nbm = self.notebook_manager
48 km = self.kernel_manager
48 km = self.kernel_manager
49 notebook_path = self.get_argument('notebook_path', default=None)
49 notebook_path = self.get_argument('notebook_path', default=None)
50 name, path = nbm.named_notebook_path(notebook_path)
50 name, path = nbm.named_notebook_path(notebook_path)
51 # Check to see if session exists
51 # Check to see if session exists
52 if sm.session_exists(name=name, path=path):
52 if sm.session_exists(name=name, path=path):
53 model = sm.get_session(name=name, path=path)
53 model = sm.get_session(name=name, path=path)
54 kernel_id = model['kernel']['id']
54 kernel_id = model['kernel']['id']
55 km.start_kernel(kernel_id, cwd=nbm.notebook_dir)
55 km.start_kernel(kernel_id, cwd=nbm.notebook_dir)
56 else:
56 else:
57 session_id = sm.get_session_id()
57 session_id = sm.get_session_id()
58 sm.save_session(session_id=session_id, name=name, path=path)
58 sm.save_session(session_id=session_id, name=name, path=path)
59 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
59 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
60 kernel = km.kernel_model(kernel_id, self.ws_url)
60 kernel = km.kernel_model(kernel_id, self.ws_url)
61 sm.update_session(session_id, kernel=kernel_id)
61 sm.update_session(session_id, kernel=kernel_id)
62 model = sm.get_session(id=session_id)
62 model = sm.get_session(id=session_id)
63 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
63 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
64 self.finish(jsonapi.dumps(model))
64 self.finish(jsonapi.dumps(model))
65
65
66 class SessionHandler(IPythonHandler):
66 class SessionHandler(IPythonHandler):
67
67
68 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
68 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
69
69
70 @web.authenticated
70 @web.authenticated
71 def get(self, session_id):
71 def get(self, session_id):
72 # Returns the JSON model for a single session
72 # Returns the JSON model for a single session
73 sm = self.session_manager
73 sm = self.session_manager
74 model = sm.get_session(id=session_id)
74 model = sm.get_session(id=session_id)
75 self.finish(jsonapi.dumps(model))
75 self.finish(jsonapi.dumps(model))
76
76
77 @web.authenticated
77 @web.authenticated
78 def patch(self, session_id):
78 def patch(self, session_id):
79 # Currently, this handler is strictly for renaming notebooks
79 # Currently, this handler is strictly for renaming notebooks
80 sm = self.session_manager
80 sm = self.session_manager
81 nbm = self.notebook_manager
81 nbm = self.notebook_manager
82 km = self.kernel_manager
82 km = self.kernel_manager
83 notebook_path = self.request.body
83 data = jsonapi.loads(self.request.body)
84 name, path = nbm.named_notebook_path(notebook_path)
84 name, path = nbm.named_notebook_path(data['notebook_path'])
85 sm.update_session(session_id, name=name)
85 sm.update_session(session_id, name=name)
86 model = sm.get_session(id=session_id)
86 model = sm.get_session(id=session_id)
87 self.finish(jsonapi.dumps(model))
87 self.finish(jsonapi.dumps(model))
88
88
89 @web.authenticated
89 @web.authenticated
90 def delete(self, session_id):
90 def delete(self, session_id):
91 # Deletes the session with given session_id
91 # Deletes the session with given session_id
92 sm = self.session_manager
92 sm = self.session_manager
93 nbm = self.notebook_manager
93 nbm = self.notebook_manager
94 km = self.kernel_manager
94 km = self.kernel_manager
95 session = sm.get_session(id=session_id)
95 session = sm.get_session(id=session_id)
96 sm.delete_session(session_id)
96 sm.delete_session(session_id)
97 km.shutdown_kernel(session['kernel']['id'])
97 km.shutdown_kernel(session['kernel']['id'])
98 self.set_status(204)
98 self.set_status(204)
99 self.finish()
99 self.finish()
100
100
101
101
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103 # URL to handler mappings
103 # URL to handler mappings
104 #-----------------------------------------------------------------------------
104 #-----------------------------------------------------------------------------
105
105
106 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
106 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
107
107
108 default_handlers = [
108 default_handlers = [
109 (r"api/sessions/%s/" % _session_id_regex, SessionHandler),
109 (r"api/sessions/%s" % _session_id_regex, SessionHandler),
110 (r"api/sessions/%s" % _session_id_regex, SessionHandler),
111 (r"api/sessions/", SessionRootHandler),
110 (r"api/sessions", SessionRootHandler)
112 (r"api/sessions", SessionRootHandler)
111 ]
113 ]
112
114
@@ -1,95 +1,96 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var Session = function(notebook_path, Notebook){
14 var Session = function(notebook_path, Notebook){
15 this.kernel = null;
15 this.kernel = null;
16 this.kernel_id = null;
16 this.kernel_id = null;
17 this.session_id = null;
17 this.session_id = null;
18 this.notebook_path = notebook_path;
18 this.notebook_path = notebook_path;
19 this.notebook = Notebook;
19 this.notebook = Notebook;
20 this._baseProjectUrl = Notebook.baseProjectUrl()
20 this._baseProjectUrl = Notebook.baseProjectUrl()
21 };
21 };
22
22
23 Session.prototype.start = function(){
23 Session.prototype.start = function(){
24 var that = this
24 var that = this
25 var qs = $.param({notebook_path:this.notebook_path});
25 var qs = $.param({notebook_path:this.notebook_path});
26 var url = '/api/sessions' + '?' + qs;
26 var url = '/api/sessions' + '?' + qs;
27 $.post(url,
27 $.post(url,
28 $.proxy(this.start_kernel, that),
28 $.proxy(this.start_kernel, that),
29 'json'
29 'json'
30 );
30 );
31 };
31 };
32
32
33 Session.prototype.notebook_rename = function (notebook_path) {
33 Session.prototype.notebook_rename = function (notebook_path) {
34 this.notebook_path = notebook_path;
34 this.notebook_path = notebook_path;
35 name = {'notebook_path': notebook_path}
35 var settings = {
36 var settings = {
36 processData : false,
37 processData : false,
37 cache : false,
38 cache : false,
38 type : "PATCH",
39 type : "PATCH",
39 data: notebook_path,
40 data: JSON.stringify(name),
40 dataType : "json",
41 dataType : "json",
41 };
42 };
42 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
43 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
43 $.ajax(url, settings);
44 $.ajax(url, settings);
44 }
45 }
45
46
46
47
47 Session.prototype.delete_session = function() {
48 Session.prototype.delete_session = function() {
48 var settings = {
49 var settings = {
49 processData : false,
50 processData : false,
50 cache : false,
51 cache : false,
51 type : "DELETE",
52 type : "DELETE",
52 dataType : "json",
53 dataType : "json",
53 };
54 };
54 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
55 var url = this._baseProjectUrl + 'api/sessions/' + this.session_id;
55 $.ajax(url, settings);
56 $.ajax(url, settings);
56 };
57 };
57
58
58 // Kernel related things
59 // Kernel related things
59 /**
60 /**
60 * Start a new kernel and set it on each code cell.
61 * Start a new kernel and set it on each code cell.
61 *
62 *
62 * @method start_kernel
63 * @method start_kernel
63 */
64 */
64 Session.prototype.start_kernel = function (json) {
65 Session.prototype.start_kernel = function (json) {
65 this.session_id = json.id;
66 this.session_id = json.id;
66 this.kernel_content = json.kernel;
67 this.kernel_content = json.kernel;
67 var base_url = $('body').data('baseKernelUrl') + "api/kernels";
68 var base_url = $('body').data('baseKernelUrl') + "api/kernels";
68 this.kernel = new IPython.Kernel(base_url, this.session_id);
69 this.kernel = new IPython.Kernel(base_url, this.session_id);
69 this.kernel._kernel_started(this.kernel_content);
70 this.kernel._kernel_started(this.kernel_content);
70 };
71 };
71
72
72 /**
73 /**
73 * Prompt the user to restart the IPython kernel.
74 * Prompt the user to restart the IPython kernel.
74 *
75 *
75 * @method restart_kernel
76 * @method restart_kernel
76 */
77 */
77 Session.prototype.restart_kernel = function () {
78 Session.prototype.restart_kernel = function () {
78 this.kernel.restart();
79 this.kernel.restart();
79 };
80 };
80
81
81 Session.prototype.interrupt_kernel = function() {
82 Session.prototype.interrupt_kernel = function() {
82 this.kernel.interrupt();
83 this.kernel.interrupt();
83 };
84 };
84
85
85
86
86 Session.prototype.kill_kernel = function() {
87 Session.prototype.kill_kernel = function() {
87 this.kernel.kill();
88 this.kernel.kill();
88 };
89 };
89
90
90 IPython.Session = Session;
91 IPython.Session = Session;
91
92
92
93
93 return IPython;
94 return IPython;
94
95
95 }(IPython));
96 }(IPython));
@@ -1,41 +1,41 b''
1 """Base class for notebook tests."""
1 """Base class for notebook tests."""
2
2
3 import sys
3 import sys
4 import time
4 import time
5 from subprocess import Popen, PIPE
5 from subprocess import Popen, PIPE
6 from unittest import TestCase
6 from unittest import TestCase
7
7
8 from IPython.utils.tempdir import TemporaryDirectory
8 from IPython.utils.tempdir import TemporaryDirectory
9
9
10
10
11 class NotebookTestBase(TestCase):
11 class NotebookTestBase(TestCase):
12 """A base class for tests that need a running notebook.
12 """A base class for tests that need a running notebook.
13
13
14 This creates an empty profile in a temp ipython_dir
14 This creates an empty profile in a temp ipython_dir
15 and then starts the notebook server with a separate temp notebook_dir.
15 and then starts the notebook server with a separate temp notebook_dir.
16 """
16 """
17
17
18 port = 12342
18 port = 1234
19
19
20 def setUp(self):
20 def setUp(self):
21 self.ipython_dir = TemporaryDirectory()
21 self.ipython_dir = TemporaryDirectory()
22 self.notebook_dir = TemporaryDirectory()
22 self.notebook_dir = TemporaryDirectory()
23 notebook_args = [
23 notebook_args = [
24 sys.executable, '-c',
24 sys.executable, '-c',
25 'from IPython.html.notebookapp import launch_new_instance; launch_new_instance()',
25 'from IPython.html.notebookapp import launch_new_instance; launch_new_instance()',
26 '--port=%d' % self.port,
26 '--port=%d' % self.port,
27 '--no-browser',
27 '--no-browser',
28 '--ipython-dir=%s' % self.ipython_dir.name,
28 '--ipython-dir=%s' % self.ipython_dir.name,
29 '--notebook-dir=%s' % self.notebook_dir.name
29 '--notebook-dir=%s' % self.notebook_dir.name
30 ]
30 ]
31 #self.notebook = Popen(notebook_args)
32 self.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE)
31 self.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE)
33 time.sleep(3.0)
32 time.sleep(3.0)
34
33
35 def tearDown(self):
34 def tearDown(self):
36 self.notebook.terminate()
35 self.notebook.terminate()
37 self.ipython_dir.cleanup()
36 self.ipython_dir.cleanup()
38 self.notebook_dir.cleanup()
37 self.notebook_dir.cleanup()
38 time.sleep(3.0)
39
39
40 def base_url(self):
40 def base_url(self):
41 return 'http://localhost:%i/' % self.port
41 return 'http://localhost:%i/' % self.port
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now