##// END OF EJS Templates
Refactoring notebook managers and adding Azure backed storage....
Brian Granger -
Show More
@@ -0,0 +1,140 b''
1 """A notebook manager that uses Azure blob storage.
2
3 Authors:
4
5 * Brian Granger
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2012 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import datetime
20
21 import azure
22 from azure.storage import BlobService
23
24 from tornado import web
25
26 from .basenbmanager import BaseNotebookManager
27 from IPython.nbformat import current
28 from IPython.utils.traitlets import Unicode, Instance
29
30
31 #-----------------------------------------------------------------------------
32 # Classes
33 #-----------------------------------------------------------------------------
34
35 class AzureNotebookManager(BaseNotebookManager):
36
37 account_name = Unicode('', config=True, help='Azure storage account name.')
38 account_key = Unicode('', config=True, help='Azure storage account key.')
39 container = Unicode('', config=True, help='Container name for notebooks.')
40
41 blob_service_host_base = Unicode('.blob.core.windows.net', config=True,
42 help='The basename for the blob service URL. If running on the preview site this '
43 'will be .blob.core.azure-preview.com.')
44 def _blob_service_host_base_changed(self, new):
45 self._update_service_host_base(new)
46
47 blob_service = Instance('azure.storage.BlobService')
48 def _blob_service_default(self):
49 return BlobService(account_name=self.account_name, account_key=self.account_key)
50
51 def __init__(self, **kwargs):
52 super(BaseNotebookManager,self).__init__(**kwargs)
53 self._update_service_host_base(self.blob_service_host_base)
54 self._create_container()
55
56 def _update_service_host_base(self, shb):
57 azure.BLOB_SERVICE_HOST_BASE = shb
58
59 def _create_container(self):
60 self.blob_service.create_container(self.container)
61
62 def load_notebook_names(self):
63 """On startup load the notebook ids and names from Azure.
64
65 The blob names are the notebook ids and the notebook names are stored
66 as blob metadata.
67 """
68 self.mapping = {}
69 blobs = self.blob_service.list_blobs(self.container)
70 ids = [blob.name for blob in blobs]
71
72 for id in ids:
73 md = self.blob_service.get_blob_metadata(self.container, id)
74 name = md['x-ms-meta-nbname']
75 self.mapping[id] = name
76
77 def list_notebooks(self):
78 """List all notebooks in the container.
79
80 This version uses `self.mapping` as the authoritative notebook list.
81 """
82 data = [dict(notebook_id=id,name=name) for id, name in self.mapping.items()]
83 data = sorted(data, key=lambda item: item['name'])
84 return data
85
86 def read_notebook_object(self, notebook_id):
87 """Get the object representation of a notebook by notebook_id."""
88 if not self.notebook_exists(notebook_id):
89 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
90 try:
91 s = self.blob_service.get_blob(self.container, notebook_id)
92 except:
93 raise web.HTTPError(500, u'Notebook cannot be read.')
94 try:
95 # v1 and v2 and json in the .ipynb files.
96 nb = current.reads(s, u'json')
97 except:
98 raise web.HTTPError(500, u'Unreadable JSON notebook.')
99 # Todo: The last modified should actually be saved in the notebook document.
100 # We are just using the current datetime until that is implemented.
101 last_modified = datetime.datetime.utcnow()
102 return last_modified, nb
103
104 def write_notebook_object(self, nb, notebook_id=None):
105 """Save an existing notebook object by notebook_id."""
106 try:
107 new_name = nb.metadata.name
108 except AttributeError:
109 raise web.HTTPError(400, u'Missing notebook name')
110
111 if notebook_id is None:
112 notebook_id = self.new_notebook_id(new_name)
113
114 if notebook_id not in self.mapping:
115 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
116
117 try:
118 data = current.writes(nb, u'json')
119 except Exception as e:
120 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
121
122 metadata = {'nbname': new_name}
123 try:
124 self.blob_service.put_blob(self.container, notebook_id, data, 'BlockBlob', x_ms_meta_name_values=metadata)
125 except Exception as e:
126 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
127
128 self.mapping[notebook_id] = new_name
129 return notebook_id
130
131 def delete_notebook(self, notebook_id):
132 """Delete notebook by notebook_id."""
133 if not self.notebook_exists(notebook_id):
134 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
135 try:
136 self.blob_service.delete_blob(self.container, notebook_id)
137 except Exception as e:
138 raise web.HTTPError(400, u'Unexpected error while deleting notebook: %s' % e)
139 else:
140 self.delete_notebook_id(notebook_id)
@@ -0,0 +1,181 b''
1 """A base class notebook manager.
2
3 Authors:
4
5 * Brian Granger
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 import uuid
20
21 from tornado import web
22
23 from IPython.config.configurable import LoggingConfigurable
24 from IPython.nbformat import current
25 from IPython.utils.traitlets import List, Dict
26
27 #-----------------------------------------------------------------------------
28 # Classes
29 #-----------------------------------------------------------------------------
30
31 class BaseNotebookManager(LoggingConfigurable):
32
33 allowed_formats = List([u'json',u'py'])
34
35 # Map notebook_ids to notebook names
36 mapping = Dict()
37
38 def load_notebook_names(self):
39 """Load the notebook names into memory.
40
41 This should be called once immediately after the notebook manager
42 is created to load the existing notebooks into the mapping in
43 memory.
44 """
45 self.list_notebooks()
46
47 def list_notebooks(self):
48 """List all notebooks.
49
50 This returns a list of dicts, each of the form::
51
52 dict(notebook_id=notebook,name=name)
53
54 This list of dicts should be sorted by name::
55
56 data = sorted(data, key=lambda item: item['name'])
57 """
58 raise NotImplementedError('must be implemented in a subclass')
59
60
61 def new_notebook_id(self, name):
62 """Generate a new notebook_id for a name and store its mapping."""
63 # TODO: the following will give stable urls for notebooks, but unless
64 # the notebooks are immediately redirected to their new urls when their
65 # filemname changes, nasty inconsistencies result. So for now it's
66 # disabled and instead we use a random uuid4() call. But we leave the
67 # logic here so that we can later reactivate it, whhen the necessary
68 # url redirection code is written.
69 #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
70 # 'file://'+self.get_path_by_name(name).encode('utf-8')))
71
72 notebook_id = unicode(uuid.uuid4())
73 self.mapping[notebook_id] = name
74 return notebook_id
75
76 def delete_notebook_id(self, notebook_id):
77 """Delete a notebook's id in the mapping.
78
79 This doesn't delete the actual notebook, only its entry in the mapping.
80 """
81 del self.mapping[notebook_id]
82
83 def notebook_exists(self, notebook_id):
84 """Does a notebook exist?"""
85 return notebook_id in self.mapping
86
87 def get_notebook(self, notebook_id, format=u'json'):
88 """Get the representation of a notebook in format by notebook_id."""
89 format = unicode(format)
90 if format not in self.allowed_formats:
91 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
92 last_modified, nb = self.read_notebook_object(notebook_id)
93 kwargs = {}
94 if format == 'json':
95 # don't split lines for sending over the wire, because it
96 # should match the Python in-memory format.
97 kwargs['split_lines'] = False
98 data = current.writes(nb, format, **kwargs)
99 name = nb.get('name','notebook')
100 return last_modified, name, data
101
102 def read_notebook_object(self, notebook_id):
103 """Get the object representation of a notebook by notebook_id."""
104 raise NotImplementedError('must be implemented in a subclass')
105
106 def save_new_notebook(self, data, name=None, format=u'json'):
107 """Save a new notebook and return its notebook_id.
108
109 If a name is passed in, it overrides any values in the notebook data
110 and the value in the data is updated to use that value.
111 """
112 if format not in self.allowed_formats:
113 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
114
115 try:
116 nb = current.reads(data.decode('utf-8'), format)
117 except:
118 raise web.HTTPError(400, u'Invalid JSON data')
119
120 if name is None:
121 try:
122 name = nb.metadata.name
123 except AttributeError:
124 raise web.HTTPError(400, u'Missing notebook name')
125 nb.metadata.name = name
126
127 notebook_id = self.write_notebook_object(nb)
128 return notebook_id
129
130 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
131 """Save an existing notebook by notebook_id."""
132 if format not in self.allowed_formats:
133 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
134
135 try:
136 nb = current.reads(data.decode('utf-8'), format)
137 except:
138 raise web.HTTPError(400, u'Invalid JSON data')
139
140 if name is not None:
141 nb.metadata.name = name
142 self.write_notebook_object(nb, notebook_id)
143
144 def write_notebook_object(self, nb, notebook_id=None):
145 """Write a notebook object and return its notebook_id.
146
147 If notebook_id is None, this method should create a new notebook_id.
148 If notebook_id is not None, this method should check to make sure it
149 exists and is valid.
150 """
151 raise NotImplementedError('must be implemented in a subclass')
152
153 def delete_notebook(self, notebook_id):
154 """Delete notebook by notebook_id."""
155 raise NotImplementedError('must be implemented in a subclass')
156
157 def increment_filename(self, name):
158 """Increment a filename to make it unique.
159
160 This exists for notebook stores that must have unique names. When a notebook
161 is created or copied this method constructs a unique filename, typically
162 by appending an integer to the name.
163 """
164 return name
165
166 def new_notebook(self):
167 """Create a new notebook and return its notebook_id."""
168 name = self.increment_filename('Untitled')
169 metadata = current.new_metadata(name=name)
170 nb = current.new_notebook(metadata=metadata)
171 notebook_id = self.write_notebook_object(nb)
172 return notebook_id
173
174 def copy_notebook(self, notebook_id):
175 """Copy an existing notebook and return its notebook_id."""
176 last_mod, nb = self.read_notebook_object(notebook_id)
177 name = nb.metadata.name + '-Copy'
178 name = self.increment_filename(name)
179 nb.metadata.name = name
180 notebook_id = self.write_notebook_object(nb)
181 return notebook_id
@@ -6,7 +6,7 b' Authors:'
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-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.
@@ -19,20 +19,19 b' Authors:'
19 import datetime
19 import datetime
20 import io
20 import io
21 import os
21 import os
22 import uuid
23 import glob
22 import glob
24
23
25 from tornado import web
24 from tornado import web
26
25
27 from IPython.config.configurable import LoggingConfigurable
26 from .basenbmanager import BaseNotebookManager
28 from IPython.nbformat import current
27 from IPython.nbformat import current
29 from IPython.utils.traitlets import Unicode, List, Dict, Bool, TraitError
28 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
30
29
31 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
32 # Classes
31 # Classes
33 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
34
33
35 class NotebookManager(LoggingConfigurable):
34 class FileNotebookManager(BaseNotebookManager):
36
35
37 notebook_dir = Unicode(os.getcwdu(), config=True, help="""
36 notebook_dir = Unicode(os.getcwdu(), config=True, help="""
38 The directory to use for notebooks.
37 The directory to use for notebooks.
@@ -59,24 +58,21 b' class NotebookManager(LoggingConfigurable):'
59 )
58 )
60
59
61 filename_ext = Unicode(u'.ipynb')
60 filename_ext = Unicode(u'.ipynb')
62 allowed_formats = List([u'json',u'py'])
63
61
64 # Map notebook_ids to notebook names
65 mapping = Dict()
66 # Map notebook names to notebook_ids
62 # Map notebook names to notebook_ids
67 rev_mapping = Dict()
63 rev_mapping = Dict()
68
64
69 def list_notebooks(self):
65 def get_notebook_names(self):
70 """List all notebooks in the notebook dir.
66 """List all notebook names in the notebook dir."""
71
72 This returns a list of dicts of the form::
73
74 dict(notebook_id=notebook,name=name)
75 """
76 names = glob.glob(os.path.join(self.notebook_dir,
67 names = glob.glob(os.path.join(self.notebook_dir,
77 '*' + self.filename_ext))
68 '*' + self.filename_ext))
78 names = [os.path.splitext(os.path.basename(name))[0]
69 names = [os.path.splitext(os.path.basename(name))[0]
79 for name in names]
70 for name in names]
71 return names
72
73 def list_notebooks(self):
74 """List all notebooks in the notebook dir."""
75 names = self.get_notebook_names()
80
76
81 data = []
77 data = []
82 for name in names:
78 for name in names:
@@ -90,30 +86,20 b' class NotebookManager(LoggingConfigurable):'
90
86
91 def new_notebook_id(self, name):
87 def new_notebook_id(self, name):
92 """Generate a new notebook_id for a name and store its mappings."""
88 """Generate a new notebook_id for a name and store its mappings."""
93 # TODO: the following will give stable urls for notebooks, but unless
89 notebook_id = super(BaseNotebookManager, self).new_notebook_id(name)
94 # the notebooks are immediately redirected to their new urls when their
95 # filemname changes, nasty inconsistencies result. So for now it's
96 # disabled and instead we use a random uuid4() call. But we leave the
97 # logic here so that we can later reactivate it, whhen the necessary
98 # url redirection code is written.
99 #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
100 # 'file://'+self.get_path_by_name(name).encode('utf-8')))
101
102 notebook_id = unicode(uuid.uuid4())
103
104 self.mapping[notebook_id] = name
105 self.rev_mapping[name] = notebook_id
90 self.rev_mapping[name] = notebook_id
106 return notebook_id
91 return notebook_id
107
92
108 def delete_notebook_id(self, notebook_id):
93 def delete_notebook_id(self, notebook_id):
109 """Delete a notebook's id only. This doesn't delete the actual notebook."""
94 """Delete a notebook's id in the mapping."""
95 super(BaseNotebookManager, self).delete_notebook_id(notebook_id)
110 name = self.mapping[notebook_id]
96 name = self.mapping[notebook_id]
111 del self.mapping[notebook_id]
112 del self.rev_mapping[name]
97 del self.rev_mapping[name]
113
98
114 def notebook_exists(self, notebook_id):
99 def notebook_exists(self, notebook_id):
115 """Does a notebook exist?"""
100 """Does a notebook exist?"""
116 if notebook_id not in self.mapping:
101 exists = super(BaseNotebookManager, self).notebook_exists(notebook_id)
102 if not exists:
117 return False
103 return False
118 path = self.get_path_by_name(self.mapping[notebook_id])
104 path = self.get_path_by_name(self.mapping[notebook_id])
119 return os.path.isfile(path)
105 return os.path.isfile(path)
@@ -132,22 +118,7 b' class NotebookManager(LoggingConfigurable):'
132 path = os.path.join(self.notebook_dir, filename)
118 path = os.path.join(self.notebook_dir, filename)
133 return path
119 return path
134
120
135 def get_notebook(self, notebook_id, format=u'json'):
121 def read_notebook_object(self, notebook_id):
136 """Get the representation of a notebook in format by notebook_id."""
137 format = unicode(format)
138 if format not in self.allowed_formats:
139 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
140 last_modified, nb = self.get_notebook_object(notebook_id)
141 kwargs = {}
142 if format == 'json':
143 # don't split lines for sending over the wire, because it
144 # should match the Python in-memory format.
145 kwargs['split_lines'] = False
146 data = current.writes(nb, format, **kwargs)
147 name = nb.get('name','notebook')
148 return last_modified, name, data
149
150 def get_notebook_object(self, notebook_id):
151 """Get the NotebookNode representation of a notebook by notebook_id."""
122 """Get the NotebookNode representation of a notebook by notebook_id."""
152 path = self.find_path(notebook_id)
123 path = self.find_path(notebook_id)
153 if not os.path.isfile(path):
124 if not os.path.isfile(path):
@@ -165,60 +136,27 b' class NotebookManager(LoggingConfigurable):'
165 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
136 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
166 return last_modified, nb
137 return last_modified, nb
167
138
168 def save_new_notebook(self, data, name=None, format=u'json'):
139 def write_notebook_object(self, nb, notebook_id=None):
169 """Save a new notebook and return its notebook_id.
140 """Save an existing notebook object by notebook_id."""
170
171 If a name is passed in, it overrides any values in the notebook data
172 and the value in the data is updated to use that value.
173 """
174 if format not in self.allowed_formats:
175 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
176
177 try:
178 nb = current.reads(data.decode('utf-8'), format)
179 except:
180 raise web.HTTPError(400, u'Invalid JSON data')
181
182 if name is None:
183 try:
184 name = nb.metadata.name
185 except AttributeError:
186 raise web.HTTPError(400, u'Missing notebook name')
187 nb.metadata.name = name
188
189 notebook_id = self.new_notebook_id(name)
190 self.save_notebook_object(notebook_id, nb)
191 return notebook_id
192
193 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
194 """Save an existing notebook by notebook_id."""
195 if format not in self.allowed_formats:
196 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
197
198 try:
141 try:
199 nb = current.reads(data.decode('utf-8'), format)
142 new_name = nb.metadata.name
200 except:
143 except AttributeError:
201 raise web.HTTPError(400, u'Invalid JSON data')
144 raise web.HTTPError(400, u'Missing notebook name')
202
145
203 if name is not None:
146 if notebook_id is None:
204 nb.metadata.name = name
147 notebook_id = self.new_notebook_id(new_name)
205 self.save_notebook_object(notebook_id, nb)
206
148
207 def save_notebook_object(self, notebook_id, nb):
208 """Save an existing notebook object by notebook_id."""
209 if notebook_id not in self.mapping:
149 if notebook_id not in self.mapping:
210 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
150 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
151
211 old_name = self.mapping[notebook_id]
152 old_name = self.mapping[notebook_id]
212 try:
213 new_name = nb.metadata.name
214 except AttributeError:
215 raise web.HTTPError(400, u'Missing notebook name')
216 path = self.get_path_by_name(new_name)
153 path = self.get_path_by_name(new_name)
217 try:
154 try:
218 with open(path,'w') as f:
155 with open(path,'w') as f:
219 current.write(nb, f, u'json')
156 current.write(nb, f, u'json')
220 except Exception as e:
157 except Exception as e:
221 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
158 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
159
222 # save .py script as well
160 # save .py script as well
223 if self.save_script:
161 if self.save_script:
224 pypath = os.path.splitext(path)[0] + '.py'
162 pypath = os.path.splitext(path)[0] + '.py'
@@ -228,6 +166,7 b' class NotebookManager(LoggingConfigurable):'
228 except Exception as e:
166 except Exception as e:
229 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
167 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
230
168
169 # remove old files if the name changed
231 if old_name != new_name:
170 if old_name != new_name:
232 old_path = self.get_path_by_name(old_name)
171 old_path = self.get_path_by_name(old_name)
233 if os.path.isfile(old_path):
172 if os.path.isfile(old_path):
@@ -239,6 +178,8 b' class NotebookManager(LoggingConfigurable):'
239 self.mapping[notebook_id] = new_name
178 self.mapping[notebook_id] = new_name
240 self.rev_mapping[new_name] = notebook_id
179 self.rev_mapping[new_name] = notebook_id
241 del self.rev_mapping[old_name]
180 del self.rev_mapping[old_name]
181
182 return notebook_id
242
183
243 def delete_notebook(self, notebook_id):
184 def delete_notebook(self, notebook_id):
244 """Delete notebook by notebook_id."""
185 """Delete notebook by notebook_id."""
@@ -263,24 +204,4 b' class NotebookManager(LoggingConfigurable):'
263 break
204 break
264 else:
205 else:
265 i = i+1
206 i = i+1
266 return path, name
207 return name
267
268 def new_notebook(self):
269 """Create a new notebook and return its notebook_id."""
270 path, name = self.increment_filename('Untitled')
271 notebook_id = self.new_notebook_id(name)
272 metadata = current.new_metadata(name=name)
273 nb = current.new_notebook(metadata=metadata)
274 with open(path,'w') as f:
275 current.write(nb, f, u'json')
276 return notebook_id
277
278 def copy_notebook(self, notebook_id):
279 """Copy an existing notebook and return its notebook_id."""
280 last_mod, nb = self.get_notebook_object(notebook_id)
281 name = nb.metadata.name + '-Copy'
282 path, name = self.increment_filename(name)
283 nb.metadata.name = name
284 notebook_id = self.new_notebook_id(name)
285 self.save_notebook_object(notebook_id, nb)
286 return notebook_id
@@ -66,7 +66,11 b' from IPython.zmq.ipkernel import ('
66 aliases as ipkernel_aliases,
66 aliases as ipkernel_aliases,
67 IPKernelApp
67 IPKernelApp
68 )
68 )
69 from IPython.utils.traitlets import Dict, Unicode, Integer, List, Enum, Bool
69 from IPython.utils.importstring import import_item
70 from IPython.utils.traitlets import (
71 Dict, Unicode, Integer, List, Enum, Bool,
72 DottedObjectName
73 )
70 from IPython.utils import py3compat
74 from IPython.utils import py3compat
71 from IPython.utils.path import filefind
75 from IPython.utils.path import filefind
72
76
@@ -404,6 +408,10 b' class NotebookApp(BaseIPythonApplication):'
404 else:
408 else:
405 self.log.info("Using MathJax: %s", new)
409 self.log.info("Using MathJax: %s", new)
406
410
411 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.notebookmanager.NotebookManager',
412 config=True,
413 help='The notebook manager class to use.')
414
407 def parse_command_line(self, argv=None):
415 def parse_command_line(self, argv=None):
408 super(NotebookApp, self).parse_command_line(argv)
416 super(NotebookApp, self).parse_command_line(argv)
409 if argv is None:
417 if argv is None:
@@ -430,9 +438,10 b' class NotebookApp(BaseIPythonApplication):'
430 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
438 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
431 connection_dir = self.profile_dir.security_dir,
439 connection_dir = self.profile_dir.security_dir,
432 )
440 )
433 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
441 kls = import_item(self.notebook_manager_class)
442 self.notebook_manager = kls(config=self.config, log=self.log)
434 self.log.info("Serving notebooks from %s", self.notebook_manager.notebook_dir)
443 self.log.info("Serving notebooks from %s", self.notebook_manager.notebook_dir)
435 self.notebook_manager.list_notebooks()
444 self.notebook_manager.load_notebook_names()
436 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
445 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
437 self.cluster_manager.update_profiles()
446 self.cluster_manager.update_profiles()
438
447
@@ -7,28 +7,28 b' from tempfile import NamedTemporaryFile'
7 from IPython.utils.tempdir import TemporaryDirectory
7 from IPython.utils.tempdir import TemporaryDirectory
8 from IPython.utils.traitlets import TraitError
8 from IPython.utils.traitlets import TraitError
9
9
10 from IPython.frontend.html.notebook.notebookmanager import NotebookManager
10 from IPython.frontend.html.notebook.filenbmanager import FileNotebookManager
11
11
12 class TestNotebookManager(TestCase):
12 class TestNotebookManager(TestCase):
13
13
14 def test_nb_dir(self):
14 def test_nb_dir(self):
15 with TemporaryDirectory() as td:
15 with TemporaryDirectory() as td:
16 km = NotebookManager(notebook_dir=td)
16 km = FileNotebookManager(notebook_dir=td)
17 self.assertEqual(km.notebook_dir, td)
17 self.assertEquals(km.notebook_dir, td)
18
18
19 def test_create_nb_dir(self):
19 def test_create_nb_dir(self):
20 with TemporaryDirectory() as td:
20 with TemporaryDirectory() as td:
21 nbdir = os.path.join(td, 'notebooks')
21 nbdir = os.path.join(td, 'notebooks')
22 km = NotebookManager(notebook_dir=nbdir)
22 km = FileNotebookManager(notebook_dir=nbdir)
23 self.assertEqual(km.notebook_dir, nbdir)
23 self.assertEquals(km.notebook_dir, nbdir)
24
24
25 def test_missing_nb_dir(self):
25 def test_missing_nb_dir(self):
26 with TemporaryDirectory() as td:
26 with TemporaryDirectory() as td:
27 nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
27 nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
28 self.assertRaises(TraitError, NotebookManager, notebook_dir=nbdir)
28 self.assertRaises(TraitError, FileNotebookManager, notebook_dir=nbdir)
29
29
30 def test_invalid_nb_dir(self):
30 def test_invalid_nb_dir(self):
31 with NamedTemporaryFile() as tf:
31 with NamedTemporaryFile() as tf:
32 self.assertRaises(TraitError, NotebookManager, notebook_dir=tf.name)
32 self.assertRaises(TraitError, FileNotebookManager, notebook_dir=tf.name)
33
33
34
34
General Comments 0
You need to be logged in to leave comments. Login now