##// END OF EJS Templates
Make hidden directories configurable
Thomas Kluyver -
Show More
@@ -1,479 +1,480 b''
1 1 """A notebook manager that uses the local file system for storage.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 * Zach Sailer
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 import io
21 21 import os
22 22 import glob
23 23 import shutil
24 24
25 25 from tornado import web
26 26
27 27 from .nbmanager import NotebookManager
28 28 from IPython.nbformat import current
29 29 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
30 30 from IPython.utils.py3compat import getcwd
31 31 from IPython.utils import tz
32 32 from IPython.html.utils import is_hidden, to_os_path
33 33
34 34 def sort_key(item):
35 35 """Case-insensitive sorting."""
36 36 return item['name'].lower()
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Classes
40 40 #-----------------------------------------------------------------------------
41 41
42 42 class FileNotebookManager(NotebookManager):
43 43
44 44 save_script = Bool(False, config=True,
45 45 help="""Automatically create a Python script when saving the notebook.
46 46
47 47 For easier use of import, %run and %load across notebooks, a
48 48 <notebook-name>.py script will be created next to any
49 49 <notebook-name>.ipynb on each save. This can also be set with the
50 50 short `--script` flag.
51 51 """
52 52 )
53 53 notebook_dir = Unicode(getcwd(), config=True)
54 54
55 55 def _notebook_dir_changed(self, name, old, new):
56 56 """Do a bit of validation of the notebook dir."""
57 57 if not os.path.isabs(new):
58 58 # If we receive a non-absolute path, make it absolute.
59 59 self.notebook_dir = os.path.abspath(new)
60 60 return
61 61 if not os.path.exists(new) or not os.path.isdir(new):
62 62 raise TraitError("notebook dir %r is not a directory" % new)
63 63
64 64 checkpoint_dir = Unicode(config=True,
65 65 help="""The location in which to keep notebook checkpoints
66 66
67 67 By default, it is notebook-dir/.ipynb_checkpoints
68 68 """
69 69 )
70 70 def _checkpoint_dir_default(self):
71 71 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
72 72
73 73 def _checkpoint_dir_changed(self, name, old, new):
74 74 """do a bit of validation of the checkpoint dir"""
75 75 if not os.path.isabs(new):
76 76 # If we receive a non-absolute path, make it absolute.
77 77 abs_new = os.path.abspath(new)
78 78 self.checkpoint_dir = abs_new
79 79 return
80 80 if os.path.exists(new) and not os.path.isdir(new):
81 81 raise TraitError("checkpoint dir %r is not a directory" % new)
82 82 if not os.path.exists(new):
83 83 self.log.info("Creating checkpoint dir %s", new)
84 84 try:
85 85 os.mkdir(new)
86 86 except:
87 87 raise TraitError("Couldn't create checkpoint dir %r" % new)
88 88
89 89 def get_notebook_names(self, path=''):
90 90 """List all notebook names in the notebook dir and path."""
91 91 path = path.strip('/')
92 92 if not os.path.isdir(self._get_os_path(path=path)):
93 93 raise web.HTTPError(404, 'Directory not found: ' + path)
94 94 names = glob.glob(self._get_os_path('*'+self.filename_ext, path))
95 95 names = [os.path.basename(name)
96 96 for name in names]
97 97 return names
98 98
99 99 def path_exists(self, path):
100 100 """Does the API-style path (directory) actually exist?
101 101
102 102 Parameters
103 103 ----------
104 104 path : string
105 105 The path to check. This is an API path (`/` separated,
106 106 relative to base notebook-dir).
107 107
108 108 Returns
109 109 -------
110 110 exists : bool
111 111 Whether the path is indeed a directory.
112 112 """
113 113 path = path.strip('/')
114 114 os_path = self._get_os_path(path=path)
115 115 return os.path.isdir(os_path)
116 116
117 117 def is_hidden(self, path):
118 118 """Does the API style path correspond to a hidden directory or file?
119 119
120 120 Parameters
121 121 ----------
122 122 path : string
123 123 The path to check. This is an API path (`/` separated,
124 124 relative to base notebook-dir).
125 125
126 126 Returns
127 127 -------
128 128 exists : bool
129 129 Whether the path is hidden.
130 130
131 131 """
132 132 path = path.strip('/')
133 133 os_path = self._get_os_path(path=path)
134 134 return is_hidden(os_path, self.notebook_dir)
135 135
136 136 def _get_os_path(self, name=None, path=''):
137 137 """Given a notebook name and a URL path, return its file system
138 138 path.
139 139
140 140 Parameters
141 141 ----------
142 142 name : string
143 143 The name of a notebook file with the .ipynb extension
144 144 path : string
145 145 The relative URL path (with '/' as separator) to the named
146 146 notebook.
147 147
148 148 Returns
149 149 -------
150 150 path : string
151 151 A file system path that combines notebook_dir (location where
152 152 server started), the relative path, and the filename with the
153 153 current operating system's url.
154 154 """
155 155 if name is not None:
156 156 path = path + '/' + name
157 157 return to_os_path(path, self.notebook_dir)
158 158
159 159 def notebook_exists(self, name, path=''):
160 160 """Returns a True if the notebook exists. Else, returns False.
161 161
162 162 Parameters
163 163 ----------
164 164 name : string
165 165 The name of the notebook you are checking.
166 166 path : string
167 167 The relative path to the notebook (with '/' as separator)
168 168
169 169 Returns
170 170 -------
171 171 bool
172 172 """
173 173 path = path.strip('/')
174 174 nbpath = self._get_os_path(name, path=path)
175 175 return os.path.isfile(nbpath)
176 176
177 177 # TODO: Remove this after we create the contents web service and directories are
178 178 # no longer listed by the notebook web service.
179 179 def list_dirs(self, path):
180 180 """List the directories for a given API style path."""
181 181 path = path.strip('/')
182 182 os_path = self._get_os_path('', path)
183 183 if not os.path.isdir(os_path) or is_hidden(os_path, self.notebook_dir):
184 184 raise web.HTTPError(404, u'directory does not exist: %r' % os_path)
185 185 dir_names = os.listdir(os_path)
186 186 dirs = []
187 187 for name in dir_names:
188 188 os_path = self._get_os_path(name, path)
189 189 if os.path.isdir(os_path) and not is_hidden(os_path, self.notebook_dir)\
190 and not name.startswith('_'):
190 and self.should_list(name):
191 191 try:
192 192 model = self.get_dir_model(name, path)
193 193 except IOError:
194 194 pass
195 195 dirs.append(model)
196 196 dirs = sorted(dirs, key=sort_key)
197 197 return dirs
198 198
199 199 # TODO: Remove this after we create the contents web service and directories are
200 200 # no longer listed by the notebook web service.
201 201 def get_dir_model(self, name, path=''):
202 202 """Get the directory model given a directory name and its API style path"""
203 203 path = path.strip('/')
204 204 os_path = self._get_os_path(name, path)
205 205 if not os.path.isdir(os_path):
206 206 raise IOError('directory does not exist: %r' % os_path)
207 207 info = os.stat(os_path)
208 208 last_modified = tz.utcfromtimestamp(info.st_mtime)
209 209 created = tz.utcfromtimestamp(info.st_ctime)
210 210 # Create the notebook model.
211 211 model ={}
212 212 model['name'] = name
213 213 model['path'] = path
214 214 model['last_modified'] = last_modified
215 215 model['created'] = created
216 216 model['type'] = 'directory'
217 217 return model
218 218
219 219 def list_notebooks(self, path):
220 220 """Returns a list of dictionaries that are the standard model
221 221 for all notebooks in the relative 'path'.
222 222
223 223 Parameters
224 224 ----------
225 225 path : str
226 226 the URL path that describes the relative path for the
227 227 listed notebooks
228 228
229 229 Returns
230 230 -------
231 231 notebooks : list of dicts
232 232 a list of the notebook models without 'content'
233 233 """
234 234 path = path.strip('/')
235 235 notebook_names = self.get_notebook_names(path)
236 notebooks = [self.get_notebook(name, path, content=False) for name in notebook_names]
236 notebooks = [self.get_notebook(name, path, content=False)
237 for name in notebook_names if self.should_list(name)]
237 238 notebooks = sorted(notebooks, key=sort_key)
238 239 return notebooks
239 240
240 241 def get_notebook(self, name, path='', content=True):
241 242 """ Takes a path and name for a notebook and returns its model
242 243
243 244 Parameters
244 245 ----------
245 246 name : str
246 247 the name of the notebook
247 248 path : str
248 249 the URL path that describes the relative path for
249 250 the notebook
250 251
251 252 Returns
252 253 -------
253 254 model : dict
254 255 the notebook model. If contents=True, returns the 'contents'
255 256 dict in the model as well.
256 257 """
257 258 path = path.strip('/')
258 259 if not self.notebook_exists(name=name, path=path):
259 260 raise web.HTTPError(404, u'Notebook does not exist: %s' % name)
260 261 os_path = self._get_os_path(name, path)
261 262 info = os.stat(os_path)
262 263 last_modified = tz.utcfromtimestamp(info.st_mtime)
263 264 created = tz.utcfromtimestamp(info.st_ctime)
264 265 # Create the notebook model.
265 266 model ={}
266 267 model['name'] = name
267 268 model['path'] = path
268 269 model['last_modified'] = last_modified
269 270 model['created'] = created
270 271 model['type'] = 'notebook'
271 272 if content:
272 273 with io.open(os_path, 'r', encoding='utf-8') as f:
273 274 try:
274 275 nb = current.read(f, u'json')
275 276 except Exception as e:
276 277 raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
277 278 self.mark_trusted_cells(nb, path, name)
278 279 model['content'] = nb
279 280 return model
280 281
281 282 def save_notebook(self, model, name='', path=''):
282 283 """Save the notebook model and return the model with no content."""
283 284 path = path.strip('/')
284 285
285 286 if 'content' not in model:
286 287 raise web.HTTPError(400, u'No notebook JSON data provided')
287 288
288 289 # One checkpoint should always exist
289 290 if self.notebook_exists(name, path) and not self.list_checkpoints(name, path):
290 291 self.create_checkpoint(name, path)
291 292
292 293 new_path = model.get('path', path).strip('/')
293 294 new_name = model.get('name', name)
294 295
295 296 if path != new_path or name != new_name:
296 297 self.rename_notebook(name, path, new_name, new_path)
297 298
298 299 # Save the notebook file
299 300 os_path = self._get_os_path(new_name, new_path)
300 301 nb = current.to_notebook_json(model['content'])
301 302
302 303 self.check_and_sign(nb, new_path, new_name)
303 304
304 305 if 'name' in nb['metadata']:
305 306 nb['metadata']['name'] = u''
306 307 try:
307 308 self.log.debug("Autosaving notebook %s", os_path)
308 309 with io.open(os_path, 'w', encoding='utf-8') as f:
309 310 current.write(nb, f, u'json')
310 311 except Exception as e:
311 312 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s %s' % (os_path, e))
312 313
313 314 # Save .py script as well
314 315 if self.save_script:
315 316 py_path = os.path.splitext(os_path)[0] + '.py'
316 317 self.log.debug("Writing script %s", py_path)
317 318 try:
318 319 with io.open(py_path, 'w', encoding='utf-8') as f:
319 320 current.write(nb, f, u'py')
320 321 except Exception as e:
321 322 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s %s' % (py_path, e))
322 323
323 324 model = self.get_notebook(new_name, new_path, content=False)
324 325 return model
325 326
326 327 def update_notebook(self, model, name, path=''):
327 328 """Update the notebook's path and/or name"""
328 329 path = path.strip('/')
329 330 new_name = model.get('name', name)
330 331 new_path = model.get('path', path).strip('/')
331 332 if path != new_path or name != new_name:
332 333 self.rename_notebook(name, path, new_name, new_path)
333 334 model = self.get_notebook(new_name, new_path, content=False)
334 335 return model
335 336
336 337 def delete_notebook(self, name, path=''):
337 338 """Delete notebook by name and path."""
338 339 path = path.strip('/')
339 340 os_path = self._get_os_path(name, path)
340 341 if not os.path.isfile(os_path):
341 342 raise web.HTTPError(404, u'Notebook does not exist: %s' % os_path)
342 343
343 344 # clear checkpoints
344 345 for checkpoint in self.list_checkpoints(name, path):
345 346 checkpoint_id = checkpoint['id']
346 347 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
347 348 if os.path.isfile(cp_path):
348 349 self.log.debug("Unlinking checkpoint %s", cp_path)
349 350 os.unlink(cp_path)
350 351
351 352 self.log.debug("Unlinking notebook %s", os_path)
352 353 os.unlink(os_path)
353 354
354 355 def rename_notebook(self, old_name, old_path, new_name, new_path):
355 356 """Rename a notebook."""
356 357 old_path = old_path.strip('/')
357 358 new_path = new_path.strip('/')
358 359 if new_name == old_name and new_path == old_path:
359 360 return
360 361
361 362 new_os_path = self._get_os_path(new_name, new_path)
362 363 old_os_path = self._get_os_path(old_name, old_path)
363 364
364 365 # Should we proceed with the move?
365 366 if os.path.isfile(new_os_path):
366 367 raise web.HTTPError(409, u'Notebook with name already exists: %s' % new_os_path)
367 368 if self.save_script:
368 369 old_py_path = os.path.splitext(old_os_path)[0] + '.py'
369 370 new_py_path = os.path.splitext(new_os_path)[0] + '.py'
370 371 if os.path.isfile(new_py_path):
371 372 raise web.HTTPError(409, u'Python script with name already exists: %s' % new_py_path)
372 373
373 374 # Move the notebook file
374 375 try:
375 376 os.rename(old_os_path, new_os_path)
376 377 except Exception as e:
377 378 raise web.HTTPError(500, u'Unknown error renaming notebook: %s %s' % (old_os_path, e))
378 379
379 380 # Move the checkpoints
380 381 old_checkpoints = self.list_checkpoints(old_name, old_path)
381 382 for cp in old_checkpoints:
382 383 checkpoint_id = cp['id']
383 384 old_cp_path = self.get_checkpoint_path(checkpoint_id, old_name, old_path)
384 385 new_cp_path = self.get_checkpoint_path(checkpoint_id, new_name, new_path)
385 386 if os.path.isfile(old_cp_path):
386 387 self.log.debug("Renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
387 388 os.rename(old_cp_path, new_cp_path)
388 389
389 390 # Move the .py script
390 391 if self.save_script:
391 392 os.rename(old_py_path, new_py_path)
392 393
393 394 # Checkpoint-related utilities
394 395
395 396 def get_checkpoint_path(self, checkpoint_id, name, path=''):
396 397 """find the path to a checkpoint"""
397 398 path = path.strip('/')
398 399 basename, _ = os.path.splitext(name)
399 400 filename = u"{name}-{checkpoint_id}{ext}".format(
400 401 name=basename,
401 402 checkpoint_id=checkpoint_id,
402 403 ext=self.filename_ext,
403 404 )
404 405 cp_path = os.path.join(path, self.checkpoint_dir, filename)
405 406 return cp_path
406 407
407 408 def get_checkpoint_model(self, checkpoint_id, name, path=''):
408 409 """construct the info dict for a given checkpoint"""
409 410 path = path.strip('/')
410 411 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
411 412 stats = os.stat(cp_path)
412 413 last_modified = tz.utcfromtimestamp(stats.st_mtime)
413 414 info = dict(
414 415 id = checkpoint_id,
415 416 last_modified = last_modified,
416 417 )
417 418 return info
418 419
419 420 # public checkpoint API
420 421
421 422 def create_checkpoint(self, name, path=''):
422 423 """Create a checkpoint from the current state of a notebook"""
423 424 path = path.strip('/')
424 425 nb_path = self._get_os_path(name, path)
425 426 # only the one checkpoint ID:
426 427 checkpoint_id = u"checkpoint"
427 428 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
428 429 self.log.debug("creating checkpoint for notebook %s", name)
429 430 if not os.path.exists(self.checkpoint_dir):
430 431 os.mkdir(self.checkpoint_dir)
431 432 shutil.copy2(nb_path, cp_path)
432 433
433 434 # return the checkpoint info
434 435 return self.get_checkpoint_model(checkpoint_id, name, path)
435 436
436 437 def list_checkpoints(self, name, path=''):
437 438 """list the checkpoints for a given notebook
438 439
439 440 This notebook manager currently only supports one checkpoint per notebook.
440 441 """
441 442 path = path.strip('/')
442 443 checkpoint_id = "checkpoint"
443 444 path = self.get_checkpoint_path(checkpoint_id, name, path)
444 445 if not os.path.exists(path):
445 446 return []
446 447 else:
447 448 return [self.get_checkpoint_model(checkpoint_id, name, path)]
448 449
449 450
450 451 def restore_checkpoint(self, checkpoint_id, name, path=''):
451 452 """restore a notebook to a checkpointed state"""
452 453 path = path.strip('/')
453 454 self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id)
454 455 nb_path = self._get_os_path(name, path)
455 456 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
456 457 if not os.path.isfile(cp_path):
457 458 self.log.debug("checkpoint file does not exist: %s", cp_path)
458 459 raise web.HTTPError(404,
459 460 u'Notebook checkpoint does not exist: %s-%s' % (name, checkpoint_id)
460 461 )
461 462 # ensure notebook is readable (never restore from an unreadable notebook)
462 463 with io.open(cp_path, 'r', encoding='utf-8') as f:
463 464 nb = current.read(f, u'json')
464 465 shutil.copy2(cp_path, nb_path)
465 466 self.log.debug("copying %s -> %s", cp_path, nb_path)
466 467
467 468 def delete_checkpoint(self, checkpoint_id, name, path=''):
468 469 """delete a notebook's checkpoint"""
469 470 path = path.strip('/')
470 471 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
471 472 if not os.path.isfile(cp_path):
472 473 raise web.HTTPError(404,
473 474 u'Notebook checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id)
474 475 )
475 476 self.log.debug("unlinking %s", cp_path)
476 477 os.unlink(cp_path)
477 478
478 479 def info_string(self):
479 480 return "Serving notebooks from local directory: %s" % self.notebook_dir
@@ -1,243 +1,251 b''
1 1 """A base class notebook manager.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 * Zach Sailer
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2011 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 from fnmatch import fnmatch
20 21 import itertools
21 22 import os
22 23
23 24 from IPython.config.configurable import LoggingConfigurable
24 25 from IPython.nbformat import current, sign
25 from IPython.utils.traitlets import Instance, Unicode
26 from IPython.utils.traitlets import Instance, Unicode, List
26 27
27 28 #-----------------------------------------------------------------------------
28 29 # Classes
29 30 #-----------------------------------------------------------------------------
30 31
31 32 class NotebookManager(LoggingConfigurable):
32 33
33 34 filename_ext = Unicode(u'.ipynb')
34 35
35 36 notary = Instance(sign.NotebookNotary)
36 37 def _notary_default(self):
37 38 return sign.NotebookNotary(parent=self)
38 39
40 hide_globs = List(Unicode, [u'__pycache__'], config=True, help="""
41 Glob patterns to hide in file and directory listings.
42 """)
43
39 44 # NotebookManager API part 1: methods that must be
40 45 # implemented in subclasses.
41 46
42 47 def path_exists(self, path):
43 48 """Does the API-style path (directory) actually exist?
44 49
45 50 Override this method in subclasses.
46 51
47 52 Parameters
48 53 ----------
49 54 path : string
50 55 The
51 56
52 57 Returns
53 58 -------
54 59 exists : bool
55 60 Whether the path does indeed exist.
56 61 """
57 62 raise NotImplementedError
58 63
59 64 def is_hidden(self, path):
60 65 """Does the API style path correspond to a hidden directory or file?
61 66
62 67 Parameters
63 68 ----------
64 69 path : string
65 70 The path to check. This is an API path (`/` separated,
66 71 relative to base notebook-dir).
67 72
68 73 Returns
69 74 -------
70 75 exists : bool
71 76 Whether the path is hidden.
72 77
73 78 """
74 79 raise NotImplementedError
75 80
76 81 def notebook_exists(self, name, path=''):
77 82 """Returns a True if the notebook exists. Else, returns False.
78 83
79 84 Parameters
80 85 ----------
81 86 name : string
82 87 The name of the notebook you are checking.
83 88 path : string
84 89 The relative path to the notebook (with '/' as separator)
85 90
86 91 Returns
87 92 -------
88 93 bool
89 94 """
90 95 raise NotImplementedError('must be implemented in a subclass')
91 96
92 97 # TODO: Remove this after we create the contents web service and directories are
93 98 # no longer listed by the notebook web service.
94 99 def list_dirs(self, path):
95 100 """List the directory models for a given API style path."""
96 101 raise NotImplementedError('must be implemented in a subclass')
97 102
98 103 # TODO: Remove this after we create the contents web service and directories are
99 104 # no longer listed by the notebook web service.
100 105 def get_dir_model(self, name, path=''):
101 106 """Get the directory model given a directory name and its API style path.
102 107
103 108 The keys in the model should be:
104 109 * name
105 110 * path
106 111 * last_modified
107 112 * created
108 113 * type='directory'
109 114 """
110 115 raise NotImplementedError('must be implemented in a subclass')
111 116
112 117 def list_notebooks(self, path=''):
113 118 """Return a list of notebook dicts without content.
114 119
115 120 This returns a list of dicts, each of the form::
116 121
117 122 dict(notebook_id=notebook,name=name)
118 123
119 124 This list of dicts should be sorted by name::
120 125
121 126 data = sorted(data, key=lambda item: item['name'])
122 127 """
123 128 raise NotImplementedError('must be implemented in a subclass')
124 129
125 130 def get_notebook(self, name, path='', content=True):
126 131 """Get the notebook model with or without content."""
127 132 raise NotImplementedError('must be implemented in a subclass')
128 133
129 134 def save_notebook(self, model, name, path=''):
130 135 """Save the notebook and return the model with no content."""
131 136 raise NotImplementedError('must be implemented in a subclass')
132 137
133 138 def update_notebook(self, model, name, path=''):
134 139 """Update the notebook and return the model with no content."""
135 140 raise NotImplementedError('must be implemented in a subclass')
136 141
137 142 def delete_notebook(self, name, path=''):
138 143 """Delete notebook by name and path."""
139 144 raise NotImplementedError('must be implemented in a subclass')
140 145
141 146 def create_checkpoint(self, name, path=''):
142 147 """Create a checkpoint of the current state of a notebook
143 148
144 149 Returns a checkpoint_id for the new checkpoint.
145 150 """
146 151 raise NotImplementedError("must be implemented in a subclass")
147 152
148 153 def list_checkpoints(self, name, path=''):
149 154 """Return a list of checkpoints for a given notebook"""
150 155 return []
151 156
152 157 def restore_checkpoint(self, checkpoint_id, name, path=''):
153 158 """Restore a notebook from one of its checkpoints"""
154 159 raise NotImplementedError("must be implemented in a subclass")
155 160
156 161 def delete_checkpoint(self, checkpoint_id, name, path=''):
157 162 """delete a checkpoint for a notebook"""
158 163 raise NotImplementedError("must be implemented in a subclass")
159 164
160 165 def info_string(self):
161 166 return "Serving notebooks"
162 167
163 168 # NotebookManager API part 2: methods that have useable default
164 169 # implementations, but can be overridden in subclasses.
165 170
166 171 def increment_filename(self, basename, path=''):
167 172 """Increment a notebook filename without the .ipynb to make it unique.
168 173
169 174 Parameters
170 175 ----------
171 176 basename : unicode
172 177 The name of a notebook without the ``.ipynb`` file extension.
173 178 path : unicode
174 179 The URL path of the notebooks directory
175 180
176 181 Returns
177 182 -------
178 183 name : unicode
179 184 A notebook name (with the .ipynb extension) that starts
180 185 with basename and does not refer to any existing notebook.
181 186 """
182 187 path = path.strip('/')
183 188 for i in itertools.count():
184 189 name = u'{basename}{i}{ext}'.format(basename=basename, i=i,
185 190 ext=self.filename_ext)
186 191 if not self.notebook_exists(name, path):
187 192 break
188 193 return name
189 194
190 195 def create_notebook(self, model=None, path=''):
191 196 """Create a new notebook and return its model with no content."""
192 197 path = path.strip('/')
193 198 if model is None:
194 199 model = {}
195 200 if 'content' not in model:
196 201 metadata = current.new_metadata(name=u'')
197 202 model['content'] = current.new_notebook(metadata=metadata)
198 203 if 'name' not in model:
199 204 model['name'] = self.increment_filename('Untitled', path)
200 205
201 206 model['path'] = path
202 207 model = self.save_notebook(model, model['name'], model['path'])
203 208 return model
204 209
205 210 def copy_notebook(self, from_name, to_name=None, path=''):
206 211 """Copy an existing notebook and return its new model.
207 212
208 213 If to_name not specified, increment `from_name-Copy#.ipynb`.
209 214 """
210 215 path = path.strip('/')
211 216 model = self.get_notebook(from_name, path)
212 217 if not to_name:
213 218 base = os.path.splitext(from_name)[0] + '-Copy'
214 219 to_name = self.increment_filename(base, path)
215 220 model['name'] = to_name
216 221 model = self.save_notebook(model, to_name, path)
217 222 return model
218 223
219 224 def log_info(self):
220 225 self.log.info(self.info_string())
221 226
222 227 # NotebookManager methods provided for use in subclasses.
223 228
224 229 def check_and_sign(self, nb, path, name):
225 230 """Check for trusted cells, and sign the notebook.
226 231
227 232 Called as a part of saving notebooks.
228 233 """
229 234 if self.notary.check_cells(nb):
230 235 self.notary.sign(nb)
231 236 else:
232 237 self.log.warn("Saving untrusted notebook %s/%s", path, name)
233 238
234 239 def mark_trusted_cells(self, nb, path, name):
235 240 """Mark cells as trusted if the notebook signature matches.
236 241
237 242 Called as a part of loading notebooks.
238 243 """
239 244 trusted = self.notary.check_signature(nb)
240 245 if not trusted:
241 246 self.log.warn("Notebook %s/%s is not trusted", path, name)
242 247 self.notary.mark_cells(nb, trusted)
243 248
249 def should_list(self, name):
250 """Should this file/directory name be displayed in a listing?"""
251 return not any(fnmatch(name, glob) for glob in self.hide_globs)
General Comments 0
You need to be logged in to leave comments. Login now