##// END OF EJS Templates
Download '.py' fixed, deleted debugging output
Zachary Sailer -
Show More
@@ -1,98 +1,94 b''
1 1 """Tornado handlers for the live notebook view.
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 from tornado import web
21 21 HTTPError = web.HTTPError
22 22
23 23 from ..base.handlers import IPythonHandler
24 24 from ..utils import url_path_join
25 from urllib import quote
26 25
27 26 #-----------------------------------------------------------------------------
28 27 # Handlers
29 28 #-----------------------------------------------------------------------------
30 29
31 30
32 31 class NewPathHandler(IPythonHandler):
33 32
34 33 @web.authenticated
35 34 def get(self, notebook_path):
36 35 notebook_name = self.notebook_manager.new_notebook(notebook_path)
37 36 self.redirect(url_path_join(self.base_project_url,"notebooks", notebook_path, notebook_name))
38 37
39 38
40 39 class NewHandler(IPythonHandler):
41 40
42 41 @web.authenticated
43 42 def get(self):
44 43 notebook_name = self.notebook_manager.new_notebook()
45 44 self.redirect(url_path_join(self.base_project_url, "notebooks", notebook_name))
46 45
47 46
48 47 class NamedNotebookHandler(IPythonHandler):
49 48
50 49 @web.authenticated
51 50 def get(self, notebook_path):
52 51 nbm = self.notebook_manager
53 52 name, path = nbm.named_notebook_path(notebook_path)
54 if name != None:
55 name = quote(name)
56 self.log.info(name)
57 53 if path == None:
58 54 project = self.project + '/' + name
59 55 else:
60 56 project = self.project + '/' + path +'/'+ name
61 57 #if not nbm.notebook_exists(notebook_path):
62 58 # raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_path)
63 59 self.write(self.render_template('notebook.html',
64 60 project=project,
65 61 notebook_path=path,
66 62 notebook_name=name,
67 63 kill_kernel=False,
68 64 mathjax_url=self.mathjax_url,
69 65 )
70 66 )
71 67
72 68
73 69 class NotebookCopyHandler(IPythonHandler):
74 70
75 71 @web.authenticated
76 72 def get(self, notebook_path=None):
77 73 nbm = self.notebook_manager
78 74 name, path = nbm.named_notebook_path(notebook_path)
79 75 notebook_name = self.notebook_manager.copy_notebook(name, path)
80 76 if path==None:
81 77 self.redirect(url_path_join(self.base_project_url, "notebooks", notebook_name))
82 78 else:
83 79 self.redirect(url_path_join(self.base_project_url, "notebooks", path, notebook_name))
84 80
85 81
86 82 #-----------------------------------------------------------------------------
87 83 # URL to handler mappings
88 84 #-----------------------------------------------------------------------------
89 85
90 86
91 87 _notebook_path_regex = r"(?P<notebook_path>.+)"
92 88
93 89 default_handlers = [
94 90 (r"/notebooks/%s/new" % _notebook_path_regex, NewPathHandler),
95 91 (r"/notebooks/new", NewHandler),
96 92 (r"/notebooks/%s/copy" % _notebook_path_regex, NotebookCopyHandler),
97 93 (r"/notebooks/%s" % _notebook_path_regex, NamedNotebookHandler)
98 94 ]
@@ -1,199 +1,199 b''
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) 2008-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 from tornado import web
20 20 import ast
21 21
22 22 from zmq.utils import jsonapi
23 23
24 24 from IPython.utils.jsonutil import date_default
25 25
26 26 from ...base.handlers import IPythonHandler
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Notebook web service handlers
30 30 #-----------------------------------------------------------------------------
31 31
32 32
33 33 class NotebookRootHandler(IPythonHandler):
34 34
35 35 @web.authenticated
36 36 def get(self):
37 37 nbm = self.notebook_manager
38 38 km = self.kernel_manager
39 39 notebooks = nbm.list_notebooks("")
40 40 self.finish(jsonapi.dumps(notebooks))
41 41
42 42 @web.authenticated
43 43 def post(self):
44 44 nbm = self.notebook_manager
45 45 notebook_name = nbm.new_notebook()
46 46 model = nbm.notebook_model(notebook_name)
47 47 self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, notebook_name))
48 48 self.finish(jsonapi.dumps(model))
49 49
50 50
51 51 class NotebookRootRedirect(IPythonHandler):
52 52
53 53 @authenticate_unless_readonly
54 54 def get(self):
55 55 self.redirect("/api/notebooks")
56 56
57 57
58 58 class NotebookHandler(IPythonHandler):
59 59
60 60 SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'DELETE')
61 61
62 62 @web.authenticated
63 63 def get(self, notebook_path):
64 64 nbm = self.notebook_manager
65 65 name, path = nbm.named_notebook_path(notebook_path)
66 66
67 67 if name == None:
68 68 notebooks = nbm.list_notebooks(path)
69 69 self.finish(jsonapi.dumps(notebooks))
70 70 else:
71 71 format = self.get_argument('format', default='json')
72 72 model = nbm.notebook_model(name,path)
73 data, name = nbm.get_notebook(model, format)
73 last_mod, representation, name = nbm.get_notebook(name, path, format)
74 74
75 75 if format == u'json':
76 76 self.set_header('Content-Type', 'application/json')
77 77 self.set_header('Content-Disposition','attachment; filename="%s.ipynb"' % name)
78 78 elif format == u'py':
79 79 self.set_header('Content-Type', 'application/x-python')
80 80 self.set_header('Content-Disposition','attachment; filename="%s.py"' % name)
81 81 #self.set_header('Last-Modified', last_mod)
82 82 self.finish(jsonapi.dumps(model))
83 83
84 84 @web.authenticated
85 85 def patch(self, notebook_path):
86 86 nbm = self.notebook_manager
87 87 notebook_name, notebook_path = nbm.named_notebook_path(notebook_path)
88 88 data = jsonapi.loads(self.request.body)
89 89 model = nbm.change_notebook(data, notebook_name, notebook_path)
90 90 self.finish(jsonapi.dumps(model))
91 91
92 92 @web.authenticated
93 93 def put(self, notebook_path):
94 94 nbm = self.notebook_manager
95 95 notebook_name, notebook_path = nbm.named_notebook_path(notebook_path)
96 96 if notebook_name == None:
97 97 body = self.request.body.strip()
98 98 format = self.get_argument('format', default='json')
99 99 name = self.get_argument('name', default=None)
100 100 if body:
101 101 notebook_name = nbm.save_new_notebook(body, notebook_path=notebook_path, name=name, format=format)
102 102 else:
103 103 notebook_name = nbm.new_notebook(notebook_path=notebook_path)
104 104 if notebook_path==None:
105 105 self.set_header('Location', nbm.notebook_dir + '/'+ notebook_name)
106 106 else:
107 107 self.set_header('Location', nbm.notebook_dir + '/'+ notebook_path + '/' + notebook_name)
108 108 model = nbm.notebook_model(notebook_name, notebook_path)
109 109 self.finish(jsonapi.dumps(model))
110 110 else:
111 111 format = self.get_argument('format', default='json')
112 112 name = self.get_argument('name', default=None)
113 113 nbm.save_notebook(self.request.body, notebook_path=notebook_path, name=name, format=format)
114 114 model = nbm.notebook_model(notebook_name, notebook_path)
115 115 self.set_status(204)
116 116 self.finish(jsonapi.dumps(model))
117 117
118 118 @web.authenticated
119 119 def delete(self, notebook_path):
120 120 nbm = self.notebook_manager
121 121 name, path = nbm.named_notebook_path(notebook_path)
122 122 nbm.delete_notebook(name, path)
123 123 self.set_status(204)
124 124 self.finish()
125 125
126 126
127 127 class NotebookCheckpointsHandler(IPythonHandler):
128 128
129 129 SUPPORTED_METHODS = ('GET', 'POST')
130 130
131 131 @web.authenticated
132 132 def get(self, notebook_path):
133 133 """get lists checkpoints for a notebook"""
134 134 nbm = self.notebook_manager
135 135 name, path = nbm.named_notebook_path(notebook_path)
136 136 checkpoints = nbm.list_checkpoints(name, path)
137 137 data = jsonapi.dumps(checkpoints, default=date_default)
138 138 self.finish(data)
139 139
140 140 @web.authenticated
141 141 def post(self, notebook_path):
142 142 """post creates a new checkpoint"""
143 143 nbm = self.notebook_manager
144 144 name, path = nbm.named_notebook_path(notebook_path)
145 145 checkpoint = nbm.create_checkpoint(name, path)
146 146 data = jsonapi.dumps(checkpoint, default=date_default)
147 147 if path == None:
148 148 self.set_header('Location', '{0}notebooks/{1}/checkpoints/{2}'.format(
149 149 self.base_project_url, name, checkpoint['checkpoint_id']
150 150 ))
151 151 else:
152 152 self.set_header('Location', '{0}notebooks/{1}/{2}/checkpoints/{3}'.format(
153 153 self.base_project_url, path, name, checkpoint['checkpoint_id']
154 154 ))
155 155 self.finish(data)
156 156
157 157
158 158 class ModifyNotebookCheckpointsHandler(IPythonHandler):
159 159
160 160 SUPPORTED_METHODS = ('POST', 'DELETE')
161 161
162 162 @web.authenticated
163 163 def post(self, notebook_path, checkpoint_id):
164 164 """post restores a notebook from a checkpoint"""
165 165 nbm = self.notebook_manager
166 166 name, path = nbm.named_notebook_path(notebook_path)
167 167 nbm.restore_checkpoint(name, checkpoint_id, path)
168 168 self.set_status(204)
169 169 self.finish()
170 170
171 171 @web.authenticated
172 172 def delete(self, notebook_path, checkpoint_id):
173 173 """delete clears a checkpoint for a given notebook"""
174 174 nbm = self.notebook_manager
175 175 name, path = nbm.named_notebook_path(notebook_path)
176 176 nbm.delete_checkpoint(name, checkpoint_id, path)
177 177 self.set_status(204)
178 178 self.finish()
179 179
180 180 #-----------------------------------------------------------------------------
181 181 # URL to handler mappings
182 182 #-----------------------------------------------------------------------------
183 183
184 184
185 185 _notebook_path_regex = r"(?P<notebook_path>.+)"
186 186 _checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
187 187
188 188 default_handlers = [
189 189 (r"api/notebooks/%s/checkpoints" % _notebook_path_regex, NotebookCheckpointsHandler),
190 190 (r"api/notebooks/%s/checkpoints/%s" % (_notebook_path_regex, _checkpoint_id_regex),
191 191 ModifyNotebookCheckpointsHandler),
192 192 (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler),
193 193 (r"api/notebooks/", NotebookRootRedirect),
194 194 (r"api/notebooks", NotebookRootHandler),
195 195 ]
196 196
197 197
198 198
199 199
@@ -1,240 +1,241 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
24 24 from IPython.config.configurable import LoggingConfigurable
25 25 from IPython.nbformat import current
26 26 from IPython.utils.traitlets import List, Dict, Unicode, TraitError
27 27
28 28 #-----------------------------------------------------------------------------
29 29 # Classes
30 30 #-----------------------------------------------------------------------------
31 31
32 32 class NotebookManager(LoggingConfigurable):
33 33
34 34 # Todo:
35 35 # The notebook_dir attribute is used to mean a couple of different things:
36 36 # 1. Where the notebooks are stored if FileNotebookManager is used.
37 37 # 2. The cwd of the kernel for a project.
38 38 # Right now we use this attribute in a number of different places and
39 39 # we are going to have to disentangle all of this.
40 40 notebook_dir = Unicode(os.getcwdu(), config=True, help="""
41 41 The directory to use for notebooks.
42 42 """)
43 43
44 44 def named_notebook_path(self, notebook_path):
45 45
46 46 l = len(notebook_path)
47 47 names = notebook_path.split('/')
48 48 if len(names) > 1:
49 49 name = names[len(names)-1]
50 50 if name[(len(name)-6):(len(name))] == ".ipynb":
51 51 name = name
52 52 path = notebook_path[0:l-len(name)-1]+'/'
53 53 else:
54 54 name = None
55 55 path = notebook_path+'/'
56 56 else:
57 57 name = names[0]
58 58 if name[(len(name)-6):(len(name))] == ".ipynb":
59 59 name = name
60 60 path = None
61 61 else:
62 62 name = None
63 63 path = notebook_path+'/'
64 64 return name, path
65 65
66 66 def _notebook_dir_changed(self, new):
67 67 """do a bit of validation of the notebook dir"""
68 68 if not os.path.isabs(new):
69 69 # If we receive a non-absolute path, make it absolute.
70 70 abs_new = os.path.abspath(new)
71 71 #self.notebook_dir = os.path.dirname(abs_new)
72 72 return
73 73 if os.path.exists(new) and not os.path.isdir(new):
74 74 raise TraitError("notebook dir %r is not a directory" % new)
75 75 if not os.path.exists(new):
76 76 self.log.info("Creating notebook dir %s", new)
77 77 try:
78 78 os.mkdir(new)
79 79 except:
80 80 raise TraitError("Couldn't create notebook dir %r" % new)
81 81
82 82 allowed_formats = List([u'json',u'py'])
83 83
84 84
85 85 def load_notebook_names(self, path):
86 86 """Load the notebook names into memory.
87 87
88 88 This should be called once immediately after the notebook manager
89 89 is created to load the existing notebooks into the mapping in
90 90 memory.
91 91 """
92 92 self.list_notebooks(path)
93 93
94 94 def list_notebooks(self):
95 95 """List all notebooks.
96 96
97 97 This returns a list of dicts, each of the form::
98 98
99 99 dict(notebook_id=notebook,name=name)
100 100
101 101 This list of dicts should be sorted by name::
102 102
103 103 data = sorted(data, key=lambda item: item['name'])
104 104 """
105 105 raise NotImplementedError('must be implemented in a subclass')
106 106
107 107
108 108 def notebook_exists(self, notebook_name):
109 109 """Does a notebook exist?"""
110 110 return notebook_name in self.mapping
111 111
112 112 def notebook_model(self, notebook_name, notebook_path=None):
113 113 """ Creates the standard notebook model """
114 114 last_modified, content = self.read_notebook_object(notebook_name, notebook_path)
115 115 model = {"notebook_name": notebook_name,
116 116 "notebook_path": notebook_path,
117 117 "content": content}
118 118 return model
119 119
120 def get_notebook(self, body, format=u'json'):
120 def get_notebook(self, notebook_name, notebook_path=None, format=u'json'):
121 121 """Get the representation of a notebook in format by notebook_name."""
122 122 format = unicode(format)
123 123 if format not in self.allowed_formats:
124 124 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
125 125 kwargs = {}
126 last_mod, nb = self.read_notebook_object(notebook_name, notebook_path)
126 127 if format == 'json':
127 128 # don't split lines for sending over the wire, because it
128 129 # should match the Python in-memory format.
129 130 kwargs['split_lines'] = False
130 representation = current.writes(body, format, **kwargs)
131 name = body['content']['metadata']['name']
132 return representation, name
131 representation = current.writes(nb, format, **kwargs)
132 name = nb.metadata.get('name', 'notebook')
133 return last_mod, representation, name
133 134
134 135 def read_notebook_object(self, notebook_name, notebook_path):
135 136 """Get the object representation of a notebook by notebook_id."""
136 137 raise NotImplementedError('must be implemented in a subclass')
137 138
138 139 def save_new_notebook(self, data, notebook_path = None, name=None, format=u'json'):
139 140 """Save a new notebook and return its notebook_id.
140 141
141 142 If a name is passed in, it overrides any values in the notebook data
142 143 and the value in the data is updated to use that value.
143 144 """
144 145 if format not in self.allowed_formats:
145 146 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
146 147
147 148 try:
148 149 nb = current.reads(data.decode('utf-8'), format)
149 150 except:
150 151 raise web.HTTPError(400, u'Invalid JSON data')
151 152
152 153 if name is None:
153 154 try:
154 155 name = nb.metadata.name
155 156 except AttributeError:
156 157 raise web.HTTPError(400, u'Missing notebook name')
157 158 nb.metadata.name = name
158 159
159 160 notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
160 161 return notebook_name
161 162
162 163 def save_notebook(self, data, notebook_path=None, name=None, new_name=None, format=u'json'):
163 164 """Save an existing notebook by notebook_name."""
164 165 if format not in self.allowed_formats:
165 166 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
166 167
167 168 try:
168 169 nb = current.reads(data.decode('utf-8'), format)
169 170 except:
170 171 raise web.HTTPError(400, u'Invalid JSON data')
171 172
172 173 if name is not None:
173 174 nb.metadata.name = name
174 175 self.write_notebook_object(nb, name, notebook_path, new_name)
175 176
176 177 def write_notebook_object(self, nb, notebook_name=None, notebook_path=None, new_name=None):
177 178 """Write a notebook object and return its notebook_name.
178 179
179 180 If notebook_name is None, this method should create a new notebook_name.
180 181 If notebook_name is not None, this method should check to make sure it
181 182 exists and is valid.
182 183 """
183 184 raise NotImplementedError('must be implemented in a subclass')
184 185
185 186 def delete_notebook(self, notebook_name, notebook_path):
186 187 """Delete notebook by notebook_id."""
187 188 raise NotImplementedError('must be implemented in a subclass')
188 189
189 190 def increment_filename(self, name):
190 191 """Increment a filename to make it unique.
191 192
192 193 This exists for notebook stores that must have unique names. When a notebook
193 194 is created or copied this method constructs a unique filename, typically
194 195 by appending an integer to the name.
195 196 """
196 197 return name
197 198
198 199 def new_notebook(self, notebook_path=None):
199 200 """Create a new notebook and return its notebook_id."""
200 201 name = self.increment_filename('Untitled', notebook_path)
201 202 metadata = current.new_metadata(name=name)
202 203 nb = current.new_notebook(metadata=metadata)
203 204 notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
204 205 return notebook_name
205 206
206 207 def copy_notebook(self, name, path):
207 208 """Copy an existing notebook and return its notebook_id."""
208 209 last_mod, nb = self.read_notebook_object(name, path)
209 210 name = nb.metadata.name + '-Copy'
210 211 name = self.increment_filename(name, path)
211 212 nb.metadata.name = name
212 213 notebook_name = self.write_notebook_object(nb, notebook_path = path)
213 214 return notebook_name
214 215
215 216 # Checkpoint-related
216 217
217 218 def create_checkpoint(self, notebook_name, notebook_path=None):
218 219 """Create a checkpoint of the current state of a notebook
219 220
220 221 Returns a checkpoint_id for the new checkpoint.
221 222 """
222 223 raise NotImplementedError("must be implemented in a subclass")
223 224
224 225 def list_checkpoints(self, notebook_name, notebook_path=None):
225 226 """Return a list of checkpoints for a given notebook"""
226 227 return []
227 228
228 229 def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
229 230 """Restore a notebook from one of its checkpoints"""
230 231 raise NotImplementedError("must be implemented in a subclass")
231 232
232 233 def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
233 234 """delete a checkpoint for a notebook"""
234 235 raise NotImplementedError("must be implemented in a subclass")
235 236
236 237 def log_info(self):
237 238 self.log.info(self.info_string())
238 239
239 240 def info_string(self):
240 241 return "Serving notebooks"
General Comments 0
You need to be logged in to leave comments. Login now