##// END OF EJS Templates
mv services/notebooks services/contents
MinRK -
Show More
1 NO CONTENT: file renamed from IPython/html/services/notebooks/__init__.py to IPython/html/services/contents/__init__.py
1 NO CONTENT: file renamed from IPython/html/services/notebooks/filenbmanager.py to IPython/html/services/contents/filenbmanager.py, modified file
@@ -1,288 +1,287
1 1 """Tornado handlers for the notebooks web service.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import json
20 20
21 21 from tornado import web
22 22
23 23 from IPython.html.utils import url_path_join, url_escape
24 24 from IPython.utils.jsonutil import date_default
25 25
26 26 from IPython.html.base.handlers import (IPythonHandler, json_errors,
27 27 notebook_path_regex, path_regex,
28 28 notebook_name_regex)
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Notebook web service handlers
32 32 #-----------------------------------------------------------------------------
33 33
34 34
35 35 class NotebookHandler(IPythonHandler):
36 36
37 37 SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE')
38 38
39 39 def notebook_location(self, name, path=''):
40 40 """Return the full URL location of a notebook based.
41 41
42 42 Parameters
43 43 ----------
44 44 name : unicode
45 45 The base name of the notebook, such as "foo.ipynb".
46 46 path : unicode
47 47 The URL path of the notebook.
48 48 """
49 49 return url_escape(url_path_join(
50 50 self.base_url, 'api', 'notebooks', path, name
51 51 ))
52 52
53 53 def _finish_model(self, model, location=True):
54 54 """Finish a JSON request with a model, setting relevant headers, etc."""
55 55 if location:
56 56 location = self.notebook_location(model['name'], model['path'])
57 57 self.set_header('Location', location)
58 58 self.set_header('Last-Modified', model['last_modified'])
59 59 self.finish(json.dumps(model, default=date_default))
60 60
61 61 @web.authenticated
62 62 @json_errors
63 63 def get(self, path='', name=None):
64 64 """Return a Notebook or list of notebooks.
65 65
66 66 * GET with path and no notebook name lists notebooks in a directory
67 67 * GET with path and notebook name returns notebook JSON
68 68 """
69 69 nbm = self.notebook_manager
70 70 # Check to see if a notebook name was given
71 71 if name is None:
72 72 # TODO: Remove this after we create the contents web service and directories are
73 73 # no longer listed by the notebook web service. This should only handle notebooks
74 74 # and not directories.
75 75 dirs = nbm.list_dirs(path)
76 76 notebooks = []
77 77 index = []
78 78 for nb in nbm.list_notebooks(path):
79 79 if nb['name'].lower() == 'index.ipynb':
80 80 index.append(nb)
81 81 else:
82 82 notebooks.append(nb)
83 83 notebooks = index + dirs + notebooks
84 84 self.finish(json.dumps(notebooks, default=date_default))
85 85 return
86 86 # get and return notebook representation
87 87 model = nbm.get_notebook(name, path)
88 88 self._finish_model(model, location=False)
89 89
90 90 @web.authenticated
91 91 @json_errors
92 92 def patch(self, path='', name=None):
93 93 """PATCH renames a notebook without re-uploading content."""
94 94 nbm = self.notebook_manager
95 95 if name is None:
96 96 raise web.HTTPError(400, u'Notebook name missing')
97 97 model = self.get_json_body()
98 98 if model is None:
99 99 raise web.HTTPError(400, u'JSON body missing')
100 100 model = nbm.update_notebook(model, name, path)
101 101 self._finish_model(model)
102 102
103 103 def _copy_notebook(self, copy_from, path, copy_to=None):
104 104 """Copy a notebook in path, optionally specifying the new name.
105 105
106 106 Only support copying within the same directory.
107 107 """
108 108 self.log.info(u"Copying notebook from %s/%s to %s/%s",
109 109 path, copy_from,
110 110 path, copy_to or '',
111 111 )
112 112 model = self.notebook_manager.copy_notebook(copy_from, copy_to, path)
113 113 self.set_status(201)
114 114 self._finish_model(model)
115 115
116 116 def _upload_notebook(self, model, path, name=None):
117 117 """Upload a notebook
118 118
119 119 If name specified, create it in path/name.
120 120 """
121 121 self.log.info(u"Uploading notebook to %s/%s", path, name or '')
122 122 if name:
123 123 model['name'] = name
124 124
125 125 model = self.notebook_manager.create_notebook(model, path)
126 126 self.set_status(201)
127 127 self._finish_model(model)
128 128
129 129 def _create_empty_notebook(self, path, name=None):
130 130 """Create an empty notebook in path
131 131
132 132 If name specified, create it in path/name.
133 133 """
134 134 self.log.info(u"Creating new notebook in %s/%s", path, name or '')
135 135 model = {}
136 136 if name:
137 137 model['name'] = name
138 138 model = self.notebook_manager.create_notebook(model, path=path)
139 139 self.set_status(201)
140 140 self._finish_model(model)
141 141
142 142 def _save_notebook(self, model, path, name):
143 143 """Save an existing notebook."""
144 144 self.log.info(u"Saving notebook at %s/%s", path, name)
145 145 model = self.notebook_manager.save_notebook(model, name, path)
146 146 if model['path'] != path.strip('/') or model['name'] != name:
147 147 # a rename happened, set Location header
148 148 location = True
149 149 else:
150 150 location = False
151 151 self._finish_model(model, location)
152 152
153 153 @web.authenticated
154 154 @json_errors
155 155 def post(self, path='', name=None):
156 156 """Create a new notebook in the specified path.
157 157
158 158 POST creates new notebooks. The server always decides on the notebook name.
159 159
160 160 POST /api/notebooks/path
161 161 New untitled notebook in path. If content specified, upload a
162 162 notebook, otherwise start empty.
163 163 POST /api/notebooks/path?copy=OtherNotebook.ipynb
164 164 New copy of OtherNotebook in path
165 165 """
166 166
167 167 if name is not None:
168 168 raise web.HTTPError(400, "Only POST to directories. Use PUT for full names.")
169 169
170 170 model = self.get_json_body()
171 171
172 172 if model is not None:
173 173 copy_from = model.get('copy_from')
174 174 if copy_from:
175 175 if model.get('content'):
176 176 raise web.HTTPError(400, "Can't upload and copy at the same time.")
177 177 self._copy_notebook(copy_from, path)
178 178 else:
179 179 self._upload_notebook(model, path)
180 180 else:
181 181 self._create_empty_notebook(path)
182 182
183 183 @web.authenticated
184 184 @json_errors
185 185 def put(self, path='', name=None):
186 186 """Saves the notebook in the location specified by name and path.
187 187
188 188 PUT is very similar to POST, but the requester specifies the name,
189 189 whereas with POST, the server picks the name.
190 190
191 191 PUT /api/notebooks/path/Name.ipynb
192 192 Save notebook at ``path/Name.ipynb``. Notebook structure is specified
193 193 in `content` key of JSON request body. If content is not specified,
194 194 create a new empty notebook.
195 195 PUT /api/notebooks/path/Name.ipynb?copy=OtherNotebook.ipynb
196 196 Copy OtherNotebook to Name
197 197 """
198 198 if name is None:
199 199 raise web.HTTPError(400, "Only PUT to full names. Use POST for directories.")
200 200
201 201 model = self.get_json_body()
202 202 if model:
203 203 copy_from = model.get('copy_from')
204 204 if copy_from:
205 205 if model.get('content'):
206 206 raise web.HTTPError(400, "Can't upload and copy at the same time.")
207 207 self._copy_notebook(copy_from, path, name)
208 208 elif self.notebook_manager.notebook_exists(name, path):
209 209 self._save_notebook(model, path, name)
210 210 else:
211 211 self._upload_notebook(model, path, name)
212 212 else:
213 213 self._create_empty_notebook(path, name)
214 214
215 215 @web.authenticated
216 216 @json_errors
217 217 def delete(self, path='', name=None):
218 218 """delete the notebook in the given notebook path"""
219 219 nbm = self.notebook_manager
220 220 nbm.delete_notebook(name, path)
221 221 self.set_status(204)
222 222 self.finish()
223 223
224 224
225 225 class NotebookCheckpointsHandler(IPythonHandler):
226 226
227 227 SUPPORTED_METHODS = ('GET', 'POST')
228 228
229 229 @web.authenticated
230 230 @json_errors
231 231 def get(self, path='', name=None):
232 232 """get lists checkpoints for a notebook"""
233 233 nbm = self.notebook_manager
234 234 checkpoints = nbm.list_checkpoints(name, path)
235 235 data = json.dumps(checkpoints, default=date_default)
236 236 self.finish(data)
237 237
238 238 @web.authenticated
239 239 @json_errors
240 240 def post(self, path='', name=None):
241 241 """post creates a new checkpoint"""
242 242 nbm = self.notebook_manager
243 243 checkpoint = nbm.create_checkpoint(name, path)
244 244 data = json.dumps(checkpoint, default=date_default)
245 245 location = url_path_join(self.base_url, 'api/notebooks',
246 246 path, name, 'checkpoints', checkpoint['id'])
247 247 self.set_header('Location', url_escape(location))
248 248 self.set_status(201)
249 249 self.finish(data)
250 250
251 251
252 252 class ModifyNotebookCheckpointsHandler(IPythonHandler):
253 253
254 254 SUPPORTED_METHODS = ('POST', 'DELETE')
255 255
256 256 @web.authenticated
257 257 @json_errors
258 258 def post(self, path, name, checkpoint_id):
259 259 """post restores a notebook from a checkpoint"""
260 260 nbm = self.notebook_manager
261 261 nbm.restore_checkpoint(checkpoint_id, name, path)
262 262 self.set_status(204)
263 263 self.finish()
264 264
265 265 @web.authenticated
266 266 @json_errors
267 267 def delete(self, path, name, checkpoint_id):
268 268 """delete clears a checkpoint for a given notebook"""
269 269 nbm = self.notebook_manager
270 270 nbm.delete_checkpoint(checkpoint_id, name, path)
271 271 self.set_status(204)
272 272 self.finish()
273 273
274 274 #-----------------------------------------------------------------------------
275 275 # URL to handler mappings
276 276 #-----------------------------------------------------------------------------
277 277
278 278
279 279 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
280 280
281 281 default_handlers = [
282 282 (r"/api/notebooks%s/checkpoints" % notebook_path_regex, NotebookCheckpointsHandler),
283 283 (r"/api/notebooks%s/checkpoints/%s" % (notebook_path_regex, _checkpoint_id_regex),
284 284 ModifyNotebookCheckpointsHandler),
285 285 (r"/api/notebooks%s" % notebook_path_regex, NotebookHandler),
286 286 (r"/api/notebooks%s" % path_regex, NotebookHandler),
287 287 ]
288
1 NO CONTENT: file renamed from IPython/html/services/notebooks/nbmanager.py to IPython/html/services/contents/nbmanager.py, modified file
1 NO CONTENT: file renamed from IPython/html/services/notebooks/tests/__init__.py to IPython/html/services/contents/tests/__init__.py
1 NO CONTENT: file renamed from IPython/html/services/notebooks/tests/test_nbmanager.py to IPython/html/services/contents/tests/test_nbmanager.py, modified file
@@ -1,347 +1,346
1 1 # coding: utf-8
2 2 """Test the notebooks webservice API."""
3 3
4 4 import io
5 5 import json
6 6 import os
7 7 import shutil
8 8 from unicodedata import normalize
9 9
10 10 pjoin = os.path.join
11 11
12 12 import requests
13 13
14 14 from IPython.html.utils import url_path_join, url_escape
15 15 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
16 16 from IPython.nbformat import current
17 17 from IPython.nbformat.current import (new_notebook, write, read, new_worksheet,
18 18 new_heading_cell, to_notebook_json)
19 19 from IPython.nbformat import v2
20 20 from IPython.utils import py3compat
21 21 from IPython.utils.data import uniq_stable
22 22
23 23
24 24 # TODO: Remove this after we create the contents web service and directories are
25 25 # no longer listed by the notebook web service.
26 26 def notebooks_only(nb_list):
27 27 return [nb for nb in nb_list if nb['type']=='notebook']
28 28
29 29 def dirs_only(nb_list):
30 30 return [x for x in nb_list if x['type']=='directory']
31 31
32 32
33 33 class NBAPI(object):
34 34 """Wrapper for notebook API calls."""
35 35 def __init__(self, base_url):
36 36 self.base_url = base_url
37 37
38 38 def _req(self, verb, path, body=None):
39 39 response = requests.request(verb,
40 40 url_path_join(self.base_url, 'api/notebooks', path),
41 41 data=body,
42 42 )
43 43 response.raise_for_status()
44 44 return response
45 45
46 46 def list(self, path='/'):
47 47 return self._req('GET', path)
48 48
49 49 def read(self, name, path='/'):
50 50 return self._req('GET', url_path_join(path, name))
51 51
52 52 def create_untitled(self, path='/'):
53 53 return self._req('POST', path)
54 54
55 55 def upload_untitled(self, body, path='/'):
56 56 return self._req('POST', path, body)
57 57
58 58 def copy_untitled(self, copy_from, path='/'):
59 59 body = json.dumps({'copy_from':copy_from})
60 60 return self._req('POST', path, body)
61 61
62 62 def create(self, name, path='/'):
63 63 return self._req('PUT', url_path_join(path, name))
64 64
65 65 def upload(self, name, body, path='/'):
66 66 return self._req('PUT', url_path_join(path, name), body)
67 67
68 68 def copy(self, copy_from, copy_to, path='/'):
69 69 body = json.dumps({'copy_from':copy_from})
70 70 return self._req('PUT', url_path_join(path, copy_to), body)
71 71
72 72 def save(self, name, body, path='/'):
73 73 return self._req('PUT', url_path_join(path, name), body)
74 74
75 75 def delete(self, name, path='/'):
76 76 return self._req('DELETE', url_path_join(path, name))
77 77
78 78 def rename(self, name, path, new_name):
79 79 body = json.dumps({'name': new_name})
80 80 return self._req('PATCH', url_path_join(path, name), body)
81 81
82 82 def get_checkpoints(self, name, path):
83 83 return self._req('GET', url_path_join(path, name, 'checkpoints'))
84 84
85 85 def new_checkpoint(self, name, path):
86 86 return self._req('POST', url_path_join(path, name, 'checkpoints'))
87 87
88 88 def restore_checkpoint(self, name, path, checkpoint_id):
89 89 return self._req('POST', url_path_join(path, name, 'checkpoints', checkpoint_id))
90 90
91 91 def delete_checkpoint(self, name, path, checkpoint_id):
92 92 return self._req('DELETE', url_path_join(path, name, 'checkpoints', checkpoint_id))
93 93
94 94 class APITest(NotebookTestBase):
95 95 """Test the kernels web service API"""
96 96 dirs_nbs = [('', 'inroot'),
97 97 ('Directory with spaces in', 'inspace'),
98 98 (u'unicodΓ©', 'innonascii'),
99 99 ('foo', 'a'),
100 100 ('foo', 'b'),
101 101 ('foo', 'name with spaces'),
102 102 ('foo', u'unicodΓ©'),
103 103 ('foo/bar', 'baz'),
104 104 ('ordering', 'A'),
105 105 ('ordering', 'b'),
106 106 ('ordering', 'C'),
107 107 (u'Γ₯ b', u'Γ§ d'),
108 108 ]
109 109 hidden_dirs = ['.hidden', '__pycache__']
110 110
111 111 dirs = uniq_stable([py3compat.cast_unicode(d) for (d,n) in dirs_nbs])
112 112 del dirs[0] # remove ''
113 113 top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs}
114 114
115 115 def setUp(self):
116 116 nbdir = self.notebook_dir.name
117 117
118 118 for d in (self.dirs + self.hidden_dirs):
119 119 d.replace('/', os.sep)
120 120 if not os.path.isdir(pjoin(nbdir, d)):
121 121 os.mkdir(pjoin(nbdir, d))
122 122
123 123 for d, name in self.dirs_nbs:
124 124 d = d.replace('/', os.sep)
125 125 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w',
126 126 encoding='utf-8') as f:
127 127 nb = new_notebook(name=name)
128 128 write(nb, f, format='ipynb')
129 129
130 130 self.nb_api = NBAPI(self.base_url())
131 131
132 132 def tearDown(self):
133 133 nbdir = self.notebook_dir.name
134 134
135 135 for dname in (list(self.top_level_dirs) + self.hidden_dirs):
136 136 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
137 137
138 138 if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')):
139 139 os.unlink(pjoin(nbdir, 'inroot.ipynb'))
140 140
141 141 def test_list_notebooks(self):
142 142 nbs = notebooks_only(self.nb_api.list().json())
143 143 self.assertEqual(len(nbs), 1)
144 144 self.assertEqual(nbs[0]['name'], 'inroot.ipynb')
145 145
146 146 nbs = notebooks_only(self.nb_api.list('/Directory with spaces in/').json())
147 147 self.assertEqual(len(nbs), 1)
148 148 self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
149 149
150 150 nbs = notebooks_only(self.nb_api.list(u'/unicodΓ©/').json())
151 151 self.assertEqual(len(nbs), 1)
152 152 self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
153 153 self.assertEqual(nbs[0]['path'], u'unicodΓ©')
154 154
155 155 nbs = notebooks_only(self.nb_api.list('/foo/bar/').json())
156 156 self.assertEqual(len(nbs), 1)
157 157 self.assertEqual(nbs[0]['name'], 'baz.ipynb')
158 158 self.assertEqual(nbs[0]['path'], 'foo/bar')
159 159
160 160 nbs = notebooks_only(self.nb_api.list('foo').json())
161 161 self.assertEqual(len(nbs), 4)
162 162 nbnames = { normalize('NFC', n['name']) for n in nbs }
163 163 expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodΓ©.ipynb']
164 164 expected = { normalize('NFC', name) for name in expected }
165 165 self.assertEqual(nbnames, expected)
166 166
167 167 nbs = notebooks_only(self.nb_api.list('ordering').json())
168 168 nbnames = [n['name'] for n in nbs]
169 169 expected = ['A.ipynb', 'b.ipynb', 'C.ipynb']
170 170 self.assertEqual(nbnames, expected)
171 171
172 172 def test_list_dirs(self):
173 173 dirs = dirs_only(self.nb_api.list().json())
174 174 dir_names = {normalize('NFC', d['name']) for d in dirs}
175 175 self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs
176 176
177 177 def test_list_nonexistant_dir(self):
178 178 with assert_http_error(404):
179 179 self.nb_api.list('nonexistant')
180 180
181 181 def test_get_contents(self):
182 182 for d, name in self.dirs_nbs:
183 183 nb = self.nb_api.read('%s.ipynb' % name, d+'/').json()
184 184 self.assertEqual(nb['name'], u'%s.ipynb' % name)
185 185 self.assertIn('content', nb)
186 186 self.assertIn('metadata', nb['content'])
187 187 self.assertIsInstance(nb['content']['metadata'], dict)
188 188
189 189 # Name that doesn't exist - should be a 404
190 190 with assert_http_error(404):
191 191 self.nb_api.read('q.ipynb', 'foo')
192 192
193 193 def _check_nb_created(self, resp, name, path):
194 194 self.assertEqual(resp.status_code, 201)
195 195 location_header = py3compat.str_to_unicode(resp.headers['Location'])
196 196 self.assertEqual(location_header, url_escape(url_path_join(u'/api/notebooks', path, name)))
197 197 self.assertEqual(resp.json()['name'], name)
198 198 assert os.path.isfile(pjoin(
199 199 self.notebook_dir.name,
200 200 path.replace('/', os.sep),
201 201 name,
202 202 ))
203 203
204 204 def test_create_untitled(self):
205 205 resp = self.nb_api.create_untitled(path=u'Γ₯ b')
206 206 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
207 207
208 208 # Second time
209 209 resp = self.nb_api.create_untitled(path=u'Γ₯ b')
210 210 self._check_nb_created(resp, 'Untitled1.ipynb', u'Γ₯ b')
211 211
212 212 # And two directories down
213 213 resp = self.nb_api.create_untitled(path='foo/bar')
214 214 self._check_nb_created(resp, 'Untitled0.ipynb', 'foo/bar')
215 215
216 216 def test_upload_untitled(self):
217 217 nb = new_notebook(name='Upload test')
218 218 nbmodel = {'content': nb}
219 219 resp = self.nb_api.upload_untitled(path=u'Γ₯ b',
220 220 body=json.dumps(nbmodel))
221 221 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
222 222
223 223 def test_upload(self):
224 224 nb = new_notebook(name=u'ignored')
225 225 nbmodel = {'content': nb}
226 226 resp = self.nb_api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
227 227 body=json.dumps(nbmodel))
228 228 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
229 229
230 230 def test_upload_v2(self):
231 231 nb = v2.new_notebook()
232 232 ws = v2.new_worksheet()
233 233 nb.worksheets.append(ws)
234 234 ws.cells.append(v2.new_code_cell(input='print("hi")'))
235 235 nbmodel = {'content': nb}
236 236 resp = self.nb_api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
237 237 body=json.dumps(nbmodel))
238 238 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
239 239 resp = self.nb_api.read(u'Upload tΓ©st.ipynb', u'Γ₯ b')
240 240 data = resp.json()
241 241 self.assertEqual(data['content']['nbformat'], current.nbformat)
242 242 self.assertEqual(data['content']['orig_nbformat'], 2)
243 243
244 244 def test_copy_untitled(self):
245 245 resp = self.nb_api.copy_untitled(u'Γ§ d.ipynb', path=u'Γ₯ b')
246 246 self._check_nb_created(resp, u'Γ§ d-Copy0.ipynb', u'Γ₯ b')
247 247
248 248 def test_copy(self):
249 249 resp = self.nb_api.copy(u'Γ§ d.ipynb', u'cΓΈpy.ipynb', path=u'Γ₯ b')
250 250 self._check_nb_created(resp, u'cΓΈpy.ipynb', u'Γ₯ b')
251 251
252 252 def test_delete(self):
253 253 for d, name in self.dirs_nbs:
254 254 resp = self.nb_api.delete('%s.ipynb' % name, d)
255 255 self.assertEqual(resp.status_code, 204)
256 256
257 257 for d in self.dirs + ['/']:
258 258 nbs = notebooks_only(self.nb_api.list(d).json())
259 259 self.assertEqual(len(nbs), 0)
260 260
261 261 def test_rename(self):
262 262 resp = self.nb_api.rename('a.ipynb', 'foo', 'z.ipynb')
263 263 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
264 264 self.assertEqual(resp.json()['name'], 'z.ipynb')
265 265 assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb'))
266 266
267 267 nbs = notebooks_only(self.nb_api.list('foo').json())
268 268 nbnames = set(n['name'] for n in nbs)
269 269 self.assertIn('z.ipynb', nbnames)
270 270 self.assertNotIn('a.ipynb', nbnames)
271 271
272 272 def test_rename_existing(self):
273 273 with assert_http_error(409):
274 274 self.nb_api.rename('a.ipynb', 'foo', 'b.ipynb')
275 275
276 276 def test_save(self):
277 277 resp = self.nb_api.read('a.ipynb', 'foo')
278 278 nbcontent = json.loads(resp.text)['content']
279 279 nb = to_notebook_json(nbcontent)
280 280 ws = new_worksheet()
281 281 nb.worksheets = [ws]
282 282 ws.cells.append(new_heading_cell(u'Created by test Β³'))
283 283
284 284 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb}
285 285 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
286 286
287 287 nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')
288 288 with io.open(nbfile, 'r', encoding='utf-8') as f:
289 289 newnb = read(f, format='ipynb')
290 290 self.assertEqual(newnb.worksheets[0].cells[0].source,
291 291 u'Created by test Β³')
292 292 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
293 293 newnb = to_notebook_json(nbcontent)
294 294 self.assertEqual(newnb.worksheets[0].cells[0].source,
295 295 u'Created by test Β³')
296 296
297 297 # Save and rename
298 298 nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb}
299 299 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
300 300 saved = resp.json()
301 301 self.assertEqual(saved['name'], 'a2.ipynb')
302 302 self.assertEqual(saved['path'], 'foo/bar')
303 303 assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb'))
304 304 assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb'))
305 305 with assert_http_error(404):
306 306 self.nb_api.read('a.ipynb', 'foo')
307 307
308 308 def test_checkpoints(self):
309 309 resp = self.nb_api.read('a.ipynb', 'foo')
310 310 r = self.nb_api.new_checkpoint('a.ipynb', 'foo')
311 311 self.assertEqual(r.status_code, 201)
312 312 cp1 = r.json()
313 313 self.assertEqual(set(cp1), {'id', 'last_modified'})
314 314 self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id'])
315 315
316 316 # Modify it
317 317 nbcontent = json.loads(resp.text)['content']
318 318 nb = to_notebook_json(nbcontent)
319 319 ws = new_worksheet()
320 320 nb.worksheets = [ws]
321 321 hcell = new_heading_cell('Created by test')
322 322 ws.cells.append(hcell)
323 323 # Save
324 324 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb}
325 325 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
326 326
327 327 # List checkpoints
328 328 cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json()
329 329 self.assertEqual(cps, [cp1])
330 330
331 331 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
332 332 nb = to_notebook_json(nbcontent)
333 333 self.assertEqual(nb.worksheets[0].cells[0].source, 'Created by test')
334 334
335 335 # Restore cp1
336 336 r = self.nb_api.restore_checkpoint('a.ipynb', 'foo', cp1['id'])
337 337 self.assertEqual(r.status_code, 204)
338 338 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
339 339 nb = to_notebook_json(nbcontent)
340 340 self.assertEqual(nb.worksheets, [])
341 341
342 342 # Delete cp1
343 343 r = self.nb_api.delete_checkpoint('a.ipynb', 'foo', cp1['id'])
344 344 self.assertEqual(r.status_code, 204)
345 345 cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json()
346 346 self.assertEqual(cps, [])
347
General Comments 0
You need to be logged in to leave comments. Login now