##// END OF EJS Templates
cleaning up named_notebook_path
Paul Ivanov -
Show More
@@ -1,257 +1,271 b''
1 1 """A base class notebook manager.
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 os
20 20 import uuid
21 21
22 22 from tornado import web
23 23 from urllib import quote, unquote
24 24
25 25 from IPython.config.configurable import LoggingConfigurable
26 26 from IPython.nbformat import current
27 27 from IPython.utils.traitlets import List, Dict, Unicode, TraitError
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes
31 31 #-----------------------------------------------------------------------------
32 32
33 33 class NotebookManager(LoggingConfigurable):
34 34
35 35 # Todo:
36 36 # The notebook_dir attribute is used to mean a couple of different things:
37 37 # 1. Where the notebooks are stored if FileNotebookManager is used.
38 38 # 2. The cwd of the kernel for a project.
39 39 # Right now we use this attribute in a number of different places and
40 40 # we are going to have to disentangle all of this.
41 41 notebook_dir = Unicode(os.getcwdu(), config=True, help="""
42 42 The directory to use for notebooks.
43 43 """)
44 44
45 45 def named_notebook_path(self, notebook_path):
46
46 """Given a notebook_path name, returns a (name, path) tuple, where
47 name is a .ipynb file, and path is the directory for the file.
48
49 Parameters
50 ----------
51 notebook_path : string
52 A path that may be a .ipynb name or a directory
53
54 Returns
55 -------
56 name : string or None
57 the filename of the notebook, or None if not a .ipynb extesnsion
58 path : string or None
59 the path to the directory which contains the notebook
60 """
47 61 names = notebook_path.split('/')
48 62 if len(names) > 1:
49 63 name = names[-1]
50 64 if name.endswith(".ipynb"):
51 65 name = name
52 path = notebook_path[0:-len(name)-1]+'/'
66 path = notebook_path[:-1]+'/'
53 67 else:
54 68 name = None
55 69 path = notebook_path+'/'
56 70 else:
57 71 name = names[0]
58 72 if name.endswith(".ipynb"):
59 73 name = name
60 74 path = None
61 75 else:
62 76 name = None
63 77 path = notebook_path+'/'
64 78 return name, path
65 79
66 80 def url_encode(self, path):
67 81 parts = path.split('/')
68 82 return os.path.join(*[quote(p) for p in parts])
69 83
70 84 def url_decode(self, path):
71 85 parts = path.split('/')
72 86 return os.path.join(*[unquote(p) for p in parts])
73 87
74 88 def _notebook_dir_changed(self, new):
75 89 """do a bit of validation of the notebook dir"""
76 90 if not os.path.isabs(new):
77 91 # If we receive a non-absolute path, make it absolute.
78 92 abs_new = os.path.abspath(new)
79 93 #self.notebook_dir = os.path.dirname(abs_new)
80 94 return
81 95 if os.path.exists(new) and not os.path.isdir(new):
82 96 raise TraitError("notebook dir %r is not a directory" % new)
83 97 if not os.path.exists(new):
84 98 self.log.info("Creating notebook dir %s", new)
85 99 try:
86 100 os.mkdir(new)
87 101 except:
88 102 raise TraitError("Couldn't create notebook dir %r" % new)
89 103
90 104 allowed_formats = List([u'json',u'py'])
91 105
92 106 def add_new_folder(self, path=None):
93 107 new_path = os.path.join(self.notebook_dir, path)
94 108 if not os.path.exists(new_path):
95 109 os.makedirs(new_path)
96 110 else:
97 111 raise web.HTTPError(409, u'Directory already exists or creation permission not allowed.')
98 112
99 113 def load_notebook_names(self, path):
100 114 """Load the notebook names into memory.
101 115
102 116 This should be called once immediately after the notebook manager
103 117 is created to load the existing notebooks into the mapping in
104 118 memory.
105 119 """
106 120 self.list_notebooks(path)
107 121
108 122 def list_notebooks(self):
109 123 """List all notebooks.
110 124
111 125 This returns a list of dicts, each of the form::
112 126
113 127 dict(notebook_id=notebook,name=name)
114 128
115 129 This list of dicts should be sorted by name::
116 130
117 131 data = sorted(data, key=lambda item: item['name'])
118 132 """
119 133 raise NotImplementedError('must be implemented in a subclass')
120 134
121 135
122 136 def notebook_exists(self, notebook_path):
123 137 """Does a notebook exist?"""
124 138
125 139
126 140 def notebook_model(self, notebook_name, notebook_path=None, content=True):
127 141 """ Creates the standard notebook model """
128 142 last_modified, contents = self.read_notebook_object(notebook_name, notebook_path)
129 143 model = {"name": notebook_name,
130 144 "path": notebook_path,
131 145 "last_modified (UTC)": last_modified.ctime()}
132 146 if content == True:
133 147 model['content'] = contents
134 148 return model
135 149
136 150 def get_notebook(self, notebook_name, notebook_path=None, format=u'json'):
137 151 """Get the representation of a notebook in format by notebook_name."""
138 152 format = unicode(format)
139 153 if format not in self.allowed_formats:
140 154 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
141 155 kwargs = {}
142 156 last_mod, nb = self.read_notebook_object(notebook_name, notebook_path)
143 157 if format == 'json':
144 158 # don't split lines for sending over the wire, because it
145 159 # should match the Python in-memory format.
146 160 kwargs['split_lines'] = False
147 161 representation = current.writes(nb, format, **kwargs)
148 162 name = nb.metadata.get('name', 'notebook')
149 163 return last_mod, representation, name
150 164
151 165 def read_notebook_object(self, notebook_name, notebook_path=None):
152 166 """Get the object representation of a notebook by notebook_id."""
153 167 raise NotImplementedError('must be implemented in a subclass')
154 168
155 169 def save_new_notebook(self, data, notebook_path = None, name=None, format=u'json'):
156 170 """Save a new notebook and return its name.
157 171
158 172 If a name is passed in, it overrides any values in the notebook data
159 173 and the value in the data is updated to use that value.
160 174 """
161 175 if format not in self.allowed_formats:
162 176 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
163 177
164 178 try:
165 179 nb = current.reads(data.decode('utf-8'), format)
166 180 except:
167 181 raise web.HTTPError(400, u'Invalid JSON data')
168 182
169 183 if name is None:
170 184 try:
171 185 name = nb.metadata.name
172 186 except AttributeError:
173 187 raise web.HTTPError(400, u'Missing notebook name')
174 188 nb.metadata.name = name
175 189
176 190 notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
177 191 return notebook_name
178 192
179 193 def save_notebook(self, data, notebook_path=None, name=None, new_name=None, format=u'json'):
180 194 """Save an existing notebook by notebook_name."""
181 195 if format not in self.allowed_formats:
182 196 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
183 197
184 198 try:
185 199 nb = current.reads(data.decode('utf-8'), format)
186 200 except:
187 201 raise web.HTTPError(400, u'Invalid JSON data')
188 202
189 203 if name is not None:
190 204 nb.metadata.name = name
191 205 self.write_notebook_object(nb, name, notebook_path, new_name)
192 206
193 207 def write_notebook_object(self, nb, notebook_name=None, notebook_path=None, new_name=None):
194 208 """Write a notebook object and return its notebook_name.
195 209
196 210 If notebook_name is None, this method should create a new notebook_name.
197 211 If notebook_name is not None, this method should check to make sure it
198 212 exists and is valid.
199 213 """
200 214 raise NotImplementedError('must be implemented in a subclass')
201 215
202 216 def delete_notebook(self, notebook_name, notebook_path):
203 217 """Delete notebook by notebook_id."""
204 218 raise NotImplementedError('must be implemented in a subclass')
205 219
206 220 def increment_filename(self, name):
207 221 """Increment a filename to make it unique.
208 222
209 223 This exists for notebook stores that must have unique names. When a notebook
210 224 is created or copied this method constructs a unique filename, typically
211 225 by appending an integer to the name.
212 226 """
213 227 return name
214 228
215 229 def new_notebook(self, notebook_path=None):
216 230 """Create a new notebook and return its notebook_name."""
217 231 name = self.increment_filename('Untitled', notebook_path)
218 232 metadata = current.new_metadata(name=name)
219 233 nb = current.new_notebook(metadata=metadata)
220 234 notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
221 235 return notebook_name
222 236
223 237 def copy_notebook(self, name, path=None):
224 238 """Copy an existing notebook and return its new notebook_name."""
225 239 last_mod, nb = self.read_notebook_object(name, path)
226 240 name = nb.metadata.name + '-Copy'
227 241 name = self.increment_filename(name, path)
228 242 nb.metadata.name = name
229 243 notebook_name = self.write_notebook_object(nb, notebook_path = path)
230 244 return notebook_name
231 245
232 246 # Checkpoint-related
233 247
234 248 def create_checkpoint(self, notebook_name, notebook_path=None):
235 249 """Create a checkpoint of the current state of a notebook
236 250
237 251 Returns a checkpoint_id for the new checkpoint.
238 252 """
239 253 raise NotImplementedError("must be implemented in a subclass")
240 254
241 255 def list_checkpoints(self, notebook_name, notebook_path=None):
242 256 """Return a list of checkpoints for a given notebook"""
243 257 return []
244 258
245 259 def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
246 260 """Restore a notebook from one of its checkpoints"""
247 261 raise NotImplementedError("must be implemented in a subclass")
248 262
249 263 def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
250 264 """delete a checkpoint for a notebook"""
251 265 raise NotImplementedError("must be implemented in a subclass")
252 266
253 267 def log_info(self):
254 268 self.log.info(self.info_string())
255 269
256 270 def info_string(self):
257 271 return "Serving notebooks"
General Comments 0
You need to be logged in to leave comments. Login now