Show More
@@ -88,7 +88,7 b' from IPython.utils.localinterfaces import localhost' | |||||
88 | from IPython.utils import submodule |
|
88 | from IPython.utils import submodule | |
89 | from IPython.utils.traitlets import ( |
|
89 | from IPython.utils.traitlets import ( | |
90 | Dict, Unicode, Integer, List, Bool, Bytes, |
|
90 | Dict, Unicode, Integer, List, Bool, Bytes, | |
91 | DottedObjectName |
|
91 | DottedObjectName, TraitError, | |
92 | ) |
|
92 | ) | |
93 | from IPython.utils import py3compat |
|
93 | from IPython.utils import py3compat | |
94 | from IPython.utils.path import filefind, get_ipython_dir |
|
94 | from IPython.utils.path import filefind, get_ipython_dir | |
@@ -201,8 +201,11 b' class NotebookWebApplication(web.Application):' | |||||
201 | handlers.extend(load_handlers('services.clusters.handlers')) |
|
201 | handlers.extend(load_handlers('services.clusters.handlers')) | |
202 | handlers.extend(load_handlers('services.sessions.handlers')) |
|
202 | handlers.extend(load_handlers('services.sessions.handlers')) | |
203 | handlers.extend(load_handlers('services.nbconvert.handlers')) |
|
203 | handlers.extend(load_handlers('services.nbconvert.handlers')) | |
204 | handlers.extend([ |
|
204 | # FIXME: /files/ should be handled by the Contents service when it exists | |
205 | (r"/files/(.*)", AuthenticatedFileHandler, {'path' : settings['notebook_manager'].notebook_dir}), |
|
205 | nbm = settings['notebook_manager'] | |
|
206 | if hasattr(nbm, 'notebook_dir'): | |||
|
207 | handlers.extend([ | |||
|
208 | (r"/files/(.*)", AuthenticatedFileHandler, {'path' : nbm.notebook_dir}), | |||
206 | (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}), |
|
209 | (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}), | |
207 | ]) |
|
210 | ]) | |
208 | # prepend base_url onto the patterns that we match |
|
211 | # prepend base_url onto the patterns that we match | |
@@ -278,7 +281,7 b' aliases.update({' | |||||
278 | 'transport': 'KernelManager.transport', |
|
281 | 'transport': 'KernelManager.transport', | |
279 | 'keyfile': 'NotebookApp.keyfile', |
|
282 | 'keyfile': 'NotebookApp.keyfile', | |
280 | 'certfile': 'NotebookApp.certfile', |
|
283 | 'certfile': 'NotebookApp.certfile', | |
281 |
'notebook-dir': 'Notebook |
|
284 | 'notebook-dir': 'NotebookApp.notebook_dir', | |
282 | 'browser': 'NotebookApp.browser', |
|
285 | 'browser': 'NotebookApp.browser', | |
283 | }) |
|
286 | }) | |
284 |
|
287 | |||
@@ -507,6 +510,26 b' class NotebookApp(BaseIPythonApplication):' | |||||
507 | def _info_file_default(self): |
|
510 | def _info_file_default(self): | |
508 | info_file = "nbserver-%s.json"%os.getpid() |
|
511 | info_file = "nbserver-%s.json"%os.getpid() | |
509 | return os.path.join(self.profile_dir.security_dir, info_file) |
|
512 | return os.path.join(self.profile_dir.security_dir, info_file) | |
|
513 | ||||
|
514 | notebook_dir = Unicode(py3compat.getcwd(), config=True, | |||
|
515 | help="The directory to use for notebooks and kernels." | |||
|
516 | ) | |||
|
517 | ||||
|
518 | def _notebook_dir_changed(self, name, old, new): | |||
|
519 | """Do a bit of validation of the notebook dir.""" | |||
|
520 | if not os.path.isabs(new): | |||
|
521 | # If we receive a non-absolute path, make it absolute. | |||
|
522 | self.notebook_dir = os.path.abspath(new) | |||
|
523 | return | |||
|
524 | if os.path.exists(new) and not os.path.isdir(new): | |||
|
525 | raise TraitError("notebook dir %r is not a directory" % new) | |||
|
526 | if not os.path.exists(new): | |||
|
527 | self.log.info("Creating notebook dir %s", new) | |||
|
528 | try: | |||
|
529 | os.mkdir(new) | |||
|
530 | except: | |||
|
531 | raise TraitError("Couldn't create notebook dir %r" % new) | |||
|
532 | ||||
510 |
|
533 | |||
511 | def parse_command_line(self, argv=None): |
|
534 | def parse_command_line(self, argv=None): | |
512 | super(NotebookApp, self).parse_command_line(argv) |
|
535 | super(NotebookApp, self).parse_command_line(argv) | |
@@ -519,7 +542,7 b' class NotebookApp(BaseIPythonApplication):' | |||||
519 | self.log.critical("No such file or directory: %s", f) |
|
542 | self.log.critical("No such file or directory: %s", f) | |
520 | self.exit(1) |
|
543 | self.exit(1) | |
521 | if os.path.isdir(f): |
|
544 | if os.path.isdir(f): | |
522 |
self |
|
545 | self.notebook_dir = f | |
523 | elif os.path.isfile(f): |
|
546 | elif os.path.isfile(f): | |
524 | self.file_to_run = f |
|
547 | self.file_to_run = f | |
525 |
|
548 | |||
@@ -730,7 +753,7 b' class NotebookApp(BaseIPythonApplication):' | |||||
730 | 'port': self.port, |
|
753 | 'port': self.port, | |
731 | 'secure': bool(self.certfile), |
|
754 | 'secure': bool(self.certfile), | |
732 | 'base_url': self.base_url, |
|
755 | 'base_url': self.base_url, | |
733 |
'notebook_dir': os.path.abspath(self. |
|
756 | 'notebook_dir': os.path.abspath(self.notebook_dir), | |
734 | } |
|
757 | } | |
735 |
|
758 | |||
736 | def write_server_info_file(self): |
|
759 | def write_server_info_file(self): |
@@ -16,6 +16,8 b' Authors:' | |||||
16 | # Imports |
|
16 | # Imports | |
17 | #----------------------------------------------------------------------------- |
|
17 | #----------------------------------------------------------------------------- | |
18 |
|
18 | |||
|
19 | import os | |||
|
20 | ||||
19 | from tornado import web |
|
21 | from tornado import web | |
20 |
|
22 | |||
21 | from IPython.kernel.multikernelmanager import MultiKernelManager |
|
23 | from IPython.kernel.multikernelmanager import MultiKernelManager | |
@@ -23,6 +25,9 b' from IPython.utils.traitlets import (' | |||||
23 | Dict, List, Unicode, |
|
25 | Dict, List, Unicode, | |
24 | ) |
|
26 | ) | |
25 |
|
27 | |||
|
28 | from IPython.html.utils import to_os_path | |||
|
29 | from IPython.utils.py3compat import getcwd | |||
|
30 | ||||
26 | #----------------------------------------------------------------------------- |
|
31 | #----------------------------------------------------------------------------- | |
27 | # Classes |
|
32 | # Classes | |
28 | #----------------------------------------------------------------------------- |
|
33 | #----------------------------------------------------------------------------- | |
@@ -35,6 +40,29 b' class MappingKernelManager(MultiKernelManager):' | |||||
35 | return "IPython.kernel.ioloop.IOLoopKernelManager" |
|
40 | return "IPython.kernel.ioloop.IOLoopKernelManager" | |
36 |
|
41 | |||
37 | kernel_argv = List(Unicode) |
|
42 | kernel_argv = List(Unicode) | |
|
43 | ||||
|
44 | root_dir = Unicode(getcwd(), config=True) | |||
|
45 | def _root_dir_default(self): | |||
|
46 | from IPython.html.notebookapp import NotebookApp | |||
|
47 | if NotebookApp.initialized(): | |||
|
48 | try: | |||
|
49 | app = NotebookApp.instance() | |||
|
50 | except Exception: | |||
|
51 | # can raise MultipleInstanceError, ignore | |||
|
52 | pass | |||
|
53 | else: | |||
|
54 | return app.notebook_dir | |||
|
55 | return app.notebook_dir | |||
|
56 | return getcwd() | |||
|
57 | ||||
|
58 | def _root_dir_changed(self, name, old, new): | |||
|
59 | """Do a bit of validation of the root dir.""" | |||
|
60 | if not os.path.isabs(new): | |||
|
61 | # If we receive a non-absolute path, make it absolute. | |||
|
62 | self.root_dir = os.path.abspath(new) | |||
|
63 | return | |||
|
64 | if not os.path.exists(new) or not os.path.isdir(new): | |||
|
65 | raise TraitError("kernel root dir %r is not a directory" % new) | |||
38 |
|
66 | |||
39 | #------------------------------------------------------------------------- |
|
67 | #------------------------------------------------------------------------- | |
40 | # Methods for managing kernels and sessions |
|
68 | # Methods for managing kernels and sessions | |
@@ -44,8 +72,17 b' class MappingKernelManager(MultiKernelManager):' | |||||
44 | """notice that a kernel died""" |
|
72 | """notice that a kernel died""" | |
45 | self.log.warn("Kernel %s died, removing from map.", kernel_id) |
|
73 | self.log.warn("Kernel %s died, removing from map.", kernel_id) | |
46 | self.remove_kernel(kernel_id) |
|
74 | self.remove_kernel(kernel_id) | |
47 |
|
75 | |||
48 | def start_kernel(self, kernel_id=None, **kwargs): |
|
76 | def cwd_for_path(self, path): | |
|
77 | """Turn API path into absolute OS path.""" | |||
|
78 | os_path = to_os_path(path, self.root_dir) | |||
|
79 | # in the case of notebooks and kernels not being on the same filesystem, | |||
|
80 | # walk up to root_dir if the paths don't exist | |||
|
81 | while not os.path.exists(os_path) and os_path != self.root_dir: | |||
|
82 | os_path = os.path.dirname(os_path) | |||
|
83 | return os_path | |||
|
84 | ||||
|
85 | def start_kernel(self, kernel_id=None, path=None, **kwargs): | |||
49 | """Start a kernel for a session an return its kernel_id. |
|
86 | """Start a kernel for a session an return its kernel_id. | |
50 |
|
87 | |||
51 | Parameters |
|
88 | Parameters | |
@@ -54,9 +91,14 b' class MappingKernelManager(MultiKernelManager):' | |||||
54 | The uuid to associate the new kernel with. If this |
|
91 | The uuid to associate the new kernel with. If this | |
55 | is not None, this kernel will be persistent whenever it is |
|
92 | is not None, this kernel will be persistent whenever it is | |
56 | requested. |
|
93 | requested. | |
|
94 | path : API path | |||
|
95 | The API path (unicode, '/' delimited) for the cwd. | |||
|
96 | Will be transformed to an OS path relative to root_dir. | |||
57 | """ |
|
97 | """ | |
58 | if kernel_id is None: |
|
98 | if kernel_id is None: | |
59 | kwargs['extra_arguments'] = self.kernel_argv |
|
99 | kwargs['extra_arguments'] = self.kernel_argv | |
|
100 | if path is not None: | |||
|
101 | kwargs['cwd'] = self.cwd_for_path(path) | |||
60 | kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs) |
|
102 | kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs) | |
61 | self.log.info("Kernel started: %s" % kernel_id) |
|
103 | self.log.info("Kernel started: %s" % kernel_id) | |
62 | self.log.debug("Kernel args: %r" % kwargs) |
|
104 | self.log.debug("Kernel args: %r" % kwargs) |
@@ -18,7 +18,6 b' Authors:' | |||||
18 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
19 |
|
19 | |||
20 | import io |
|
20 | import io | |
21 | import itertools |
|
|||
22 | import os |
|
21 | import os | |
23 | import glob |
|
22 | import glob | |
24 | import shutil |
|
23 | import shutil | |
@@ -28,8 +27,9 b' from tornado import web' | |||||
28 | from .nbmanager import NotebookManager |
|
27 | from .nbmanager import NotebookManager | |
29 | from IPython.nbformat import current |
|
28 | from IPython.nbformat import current | |
30 | from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError |
|
29 | from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError | |
|
30 | from IPython.utils.py3compat import getcwd | |||
31 | from IPython.utils import tz |
|
31 | from IPython.utils import tz | |
32 | from IPython.html.utils import is_hidden |
|
32 | from IPython.html.utils import is_hidden, to_os_path | |
33 |
|
33 | |||
34 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
35 | # Classes |
|
35 | # Classes | |
@@ -46,7 +46,28 b' class FileNotebookManager(NotebookManager):' | |||||
46 | short `--script` flag. |
|
46 | short `--script` flag. | |
47 | """ |
|
47 | """ | |
48 | ) |
|
48 | ) | |
|
49 | notebook_dir = Unicode(getcwd(), config=True) | |||
|
50 | def _notebook_dir_default(self): | |||
|
51 | from IPython.html.notebookapp import NotebookApp | |||
|
52 | if NotebookApp.initialized(): | |||
|
53 | try: | |||
|
54 | app = NotebookApp.instance() | |||
|
55 | except Exception: | |||
|
56 | # can raise MultipleInstanceError, ignore | |||
|
57 | pass | |||
|
58 | else: | |||
|
59 | return app.notebook_dir | |||
|
60 | return getcwd() | |||
49 |
|
61 | |||
|
62 | def _notebook_dir_changed(self, name, old, new): | |||
|
63 | """Do a bit of validation of the notebook dir.""" | |||
|
64 | if not os.path.isabs(new): | |||
|
65 | # If we receive a non-absolute path, make it absolute. | |||
|
66 | self.notebook_dir = os.path.abspath(new) | |||
|
67 | return | |||
|
68 | if not os.path.exists(new) or not os.path.isdir(new): | |||
|
69 | raise TraitError("notebook dir %r is not a directory" % new) | |||
|
70 | ||||
50 | checkpoint_dir = Unicode(config=True, |
|
71 | checkpoint_dir = Unicode(config=True, | |
51 | help="""The location in which to keep notebook checkpoints |
|
72 | help="""The location in which to keep notebook checkpoints | |
52 |
|
73 | |||
@@ -75,9 +96,9 b' class FileNotebookManager(NotebookManager):' | |||||
75 | def get_notebook_names(self, path=''): |
|
96 | def get_notebook_names(self, path=''): | |
76 | """List all notebook names in the notebook dir and path.""" |
|
97 | """List all notebook names in the notebook dir and path.""" | |
77 | path = path.strip('/') |
|
98 | path = path.strip('/') | |
78 | if not os.path.isdir(self.get_os_path(path=path)): |
|
99 | if not os.path.isdir(self._get_os_path(path=path)): | |
79 | raise web.HTTPError(404, 'Directory not found: ' + path) |
|
100 | raise web.HTTPError(404, 'Directory not found: ' + path) | |
80 | names = glob.glob(self.get_os_path('*'+self.filename_ext, path)) |
|
101 | names = glob.glob(self._get_os_path('*'+self.filename_ext, path)) | |
81 | names = [os.path.basename(name) |
|
102 | names = [os.path.basename(name) | |
82 | for name in names] |
|
103 | for name in names] | |
83 | return names |
|
104 | return names | |
@@ -97,7 +118,7 b' class FileNotebookManager(NotebookManager):' | |||||
97 | Whether the path is indeed a directory. |
|
118 | Whether the path is indeed a directory. | |
98 | """ |
|
119 | """ | |
99 | path = path.strip('/') |
|
120 | path = path.strip('/') | |
100 | os_path = self.get_os_path(path=path) |
|
121 | os_path = self._get_os_path(path=path) | |
101 | return os.path.isdir(os_path) |
|
122 | return os.path.isdir(os_path) | |
102 |
|
123 | |||
103 | def is_hidden(self, path): |
|
124 | def is_hidden(self, path): | |
@@ -116,10 +137,10 b' class FileNotebookManager(NotebookManager):' | |||||
116 |
|
137 | |||
117 | """ |
|
138 | """ | |
118 | path = path.strip('/') |
|
139 | path = path.strip('/') | |
119 | os_path = self.get_os_path(path=path) |
|
140 | os_path = self._get_os_path(path=path) | |
120 | return is_hidden(os_path, self.notebook_dir) |
|
141 | return is_hidden(os_path, self.notebook_dir) | |
121 |
|
142 | |||
122 | def get_os_path(self, name=None, path=''): |
|
143 | def _get_os_path(self, name=None, path=''): | |
123 | """Given a notebook name and a URL path, return its file system |
|
144 | """Given a notebook name and a URL path, return its file system | |
124 | path. |
|
145 | path. | |
125 |
|
146 | |||
@@ -138,12 +159,9 b' class FileNotebookManager(NotebookManager):' | |||||
138 | server started), the relative path, and the filename with the |
|
159 | server started), the relative path, and the filename with the | |
139 | current operating system's url. |
|
160 | current operating system's url. | |
140 | """ |
|
161 | """ | |
141 | parts = path.strip('/').split('/') |
|
|||
142 | parts = [p for p in parts if p != ''] # remove duplicate splits |
|
|||
143 | if name is not None: |
|
162 | if name is not None: | |
144 |
pa |
|
163 | path = path + '/' + name | |
145 |
path |
|
164 | return to_os_path(path, self.notebook_dir) | |
146 | return path |
|
|||
147 |
|
165 | |||
148 | def notebook_exists(self, name, path=''): |
|
166 | def notebook_exists(self, name, path=''): | |
149 | """Returns a True if the notebook exists. Else, returns False. |
|
167 | """Returns a True if the notebook exists. Else, returns False. | |
@@ -160,7 +178,7 b' class FileNotebookManager(NotebookManager):' | |||||
160 | bool |
|
178 | bool | |
161 | """ |
|
179 | """ | |
162 | path = path.strip('/') |
|
180 | path = path.strip('/') | |
163 | nbpath = self.get_os_path(name, path=path) |
|
181 | nbpath = self._get_os_path(name, path=path) | |
164 | return os.path.isfile(nbpath) |
|
182 | return os.path.isfile(nbpath) | |
165 |
|
183 | |||
166 | # TODO: Remove this after we create the contents web service and directories are |
|
184 | # TODO: Remove this after we create the contents web service and directories are | |
@@ -168,13 +186,13 b' class FileNotebookManager(NotebookManager):' | |||||
168 | def list_dirs(self, path): |
|
186 | def list_dirs(self, path): | |
169 | """List the directories for a given API style path.""" |
|
187 | """List the directories for a given API style path.""" | |
170 | path = path.strip('/') |
|
188 | path = path.strip('/') | |
171 | os_path = self.get_os_path('', path) |
|
189 | os_path = self._get_os_path('', path) | |
172 | if not os.path.isdir(os_path) or is_hidden(os_path, self.notebook_dir): |
|
190 | if not os.path.isdir(os_path) or is_hidden(os_path, self.notebook_dir): | |
173 | raise web.HTTPError(404, u'directory does not exist: %r' % os_path) |
|
191 | raise web.HTTPError(404, u'directory does not exist: %r' % os_path) | |
174 | dir_names = os.listdir(os_path) |
|
192 | dir_names = os.listdir(os_path) | |
175 | dirs = [] |
|
193 | dirs = [] | |
176 | for name in dir_names: |
|
194 | for name in dir_names: | |
177 | os_path = self.get_os_path(name, path) |
|
195 | os_path = self._get_os_path(name, path) | |
178 | if os.path.isdir(os_path) and not is_hidden(os_path, self.notebook_dir): |
|
196 | if os.path.isdir(os_path) and not is_hidden(os_path, self.notebook_dir): | |
179 | try: |
|
197 | try: | |
180 | model = self.get_dir_model(name, path) |
|
198 | model = self.get_dir_model(name, path) | |
@@ -189,7 +207,7 b' class FileNotebookManager(NotebookManager):' | |||||
189 | def get_dir_model(self, name, path=''): |
|
207 | def get_dir_model(self, name, path=''): | |
190 | """Get the directory model given a directory name and its API style path""" |
|
208 | """Get the directory model given a directory name and its API style path""" | |
191 | path = path.strip('/') |
|
209 | path = path.strip('/') | |
192 | os_path = self.get_os_path(name, path) |
|
210 | os_path = self._get_os_path(name, path) | |
193 | if not os.path.isdir(os_path): |
|
211 | if not os.path.isdir(os_path): | |
194 | raise IOError('directory does not exist: %r' % os_path) |
|
212 | raise IOError('directory does not exist: %r' % os_path) | |
195 | info = os.stat(os_path) |
|
213 | info = os.stat(os_path) | |
@@ -245,7 +263,7 b' class FileNotebookManager(NotebookManager):' | |||||
245 | path = path.strip('/') |
|
263 | path = path.strip('/') | |
246 | if not self.notebook_exists(name=name, path=path): |
|
264 | if not self.notebook_exists(name=name, path=path): | |
247 | raise web.HTTPError(404, u'Notebook does not exist: %s' % name) |
|
265 | raise web.HTTPError(404, u'Notebook does not exist: %s' % name) | |
248 | os_path = self.get_os_path(name, path) |
|
266 | os_path = self._get_os_path(name, path) | |
249 | info = os.stat(os_path) |
|
267 | info = os.stat(os_path) | |
250 | last_modified = tz.utcfromtimestamp(info.st_mtime) |
|
268 | last_modified = tz.utcfromtimestamp(info.st_mtime) | |
251 | created = tz.utcfromtimestamp(info.st_ctime) |
|
269 | created = tz.utcfromtimestamp(info.st_ctime) | |
@@ -284,7 +302,7 b' class FileNotebookManager(NotebookManager):' | |||||
284 | self.rename_notebook(name, path, new_name, new_path) |
|
302 | self.rename_notebook(name, path, new_name, new_path) | |
285 |
|
303 | |||
286 | # Save the notebook file |
|
304 | # Save the notebook file | |
287 | os_path = self.get_os_path(new_name, new_path) |
|
305 | os_path = self._get_os_path(new_name, new_path) | |
288 | nb = current.to_notebook_json(model['content']) |
|
306 | nb = current.to_notebook_json(model['content']) | |
289 |
|
307 | |||
290 | self.check_and_sign(nb, new_path, new_name) |
|
308 | self.check_and_sign(nb, new_path, new_name) | |
@@ -324,7 +342,7 b' class FileNotebookManager(NotebookManager):' | |||||
324 | def delete_notebook(self, name, path=''): |
|
342 | def delete_notebook(self, name, path=''): | |
325 | """Delete notebook by name and path.""" |
|
343 | """Delete notebook by name and path.""" | |
326 | path = path.strip('/') |
|
344 | path = path.strip('/') | |
327 | os_path = self.get_os_path(name, path) |
|
345 | os_path = self._get_os_path(name, path) | |
328 | if not os.path.isfile(os_path): |
|
346 | if not os.path.isfile(os_path): | |
329 | raise web.HTTPError(404, u'Notebook does not exist: %s' % os_path) |
|
347 | raise web.HTTPError(404, u'Notebook does not exist: %s' % os_path) | |
330 |
|
348 | |||
@@ -346,8 +364,8 b' class FileNotebookManager(NotebookManager):' | |||||
346 | if new_name == old_name and new_path == old_path: |
|
364 | if new_name == old_name and new_path == old_path: | |
347 | return |
|
365 | return | |
348 |
|
366 | |||
349 | new_os_path = self.get_os_path(new_name, new_path) |
|
367 | new_os_path = self._get_os_path(new_name, new_path) | |
350 | old_os_path = self.get_os_path(old_name, old_path) |
|
368 | old_os_path = self._get_os_path(old_name, old_path) | |
351 |
|
369 | |||
352 | # Should we proceed with the move? |
|
370 | # Should we proceed with the move? | |
353 | if os.path.isfile(new_os_path): |
|
371 | if os.path.isfile(new_os_path): | |
@@ -409,7 +427,7 b' class FileNotebookManager(NotebookManager):' | |||||
409 | def create_checkpoint(self, name, path=''): |
|
427 | def create_checkpoint(self, name, path=''): | |
410 | """Create a checkpoint from the current state of a notebook""" |
|
428 | """Create a checkpoint from the current state of a notebook""" | |
411 | path = path.strip('/') |
|
429 | path = path.strip('/') | |
412 | nb_path = self.get_os_path(name, path) |
|
430 | nb_path = self._get_os_path(name, path) | |
413 | # only the one checkpoint ID: |
|
431 | # only the one checkpoint ID: | |
414 | checkpoint_id = u"checkpoint" |
|
432 | checkpoint_id = u"checkpoint" | |
415 | cp_path = self.get_checkpoint_path(checkpoint_id, name, path) |
|
433 | cp_path = self.get_checkpoint_path(checkpoint_id, name, path) | |
@@ -439,7 +457,7 b' class FileNotebookManager(NotebookManager):' | |||||
439 | """restore a notebook to a checkpointed state""" |
|
457 | """restore a notebook to a checkpointed state""" | |
440 | path = path.strip('/') |
|
458 | path = path.strip('/') | |
441 | self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id) |
|
459 | self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id) | |
442 | nb_path = self.get_os_path(name, path) |
|
460 | nb_path = self._get_os_path(name, path) | |
443 | cp_path = self.get_checkpoint_path(checkpoint_id, name, path) |
|
461 | cp_path = self.get_checkpoint_path(checkpoint_id, name, path) | |
444 | if not os.path.isfile(cp_path): |
|
462 | if not os.path.isfile(cp_path): | |
445 | self.log.debug("checkpoint file does not exist: %s", cp_path) |
|
463 | self.log.debug("checkpoint file does not exist: %s", cp_path) |
@@ -22,8 +22,7 b' import os' | |||||
22 |
|
22 | |||
23 | from IPython.config.configurable import LoggingConfigurable |
|
23 | from IPython.config.configurable import LoggingConfigurable | |
24 | from IPython.nbformat import current, sign |
|
24 | from IPython.nbformat import current, sign | |
25 |
from IPython.utils import |
|
25 | from IPython.utils.traitlets import Instance, Unicode | |
26 | from IPython.utils.traitlets import Instance, Unicode, TraitError |
|
|||
27 |
|
26 | |||
28 | #----------------------------------------------------------------------------- |
|
27 | #----------------------------------------------------------------------------- | |
29 | # Classes |
|
28 | # Classes | |
@@ -31,16 +30,6 b' from IPython.utils.traitlets import Instance, Unicode, TraitError' | |||||
31 |
|
30 | |||
32 | class NotebookManager(LoggingConfigurable): |
|
31 | class NotebookManager(LoggingConfigurable): | |
33 |
|
32 | |||
34 | # Todo: |
|
|||
35 | # The notebook_dir attribute is used to mean a couple of different things: |
|
|||
36 | # 1. Where the notebooks are stored if FileNotebookManager is used. |
|
|||
37 | # 2. The cwd of the kernel for a project. |
|
|||
38 | # Right now we use this attribute in a number of different places and |
|
|||
39 | # we are going to have to disentangle all of this. |
|
|||
40 | notebook_dir = Unicode(py3compat.getcwd(), config=True, help=""" |
|
|||
41 | The directory to use for notebooks. |
|
|||
42 | """) |
|
|||
43 |
|
||||
44 | filename_ext = Unicode(u'.ipynb') |
|
33 | filename_ext = Unicode(u'.ipynb') | |
45 |
|
34 | |||
46 | notary = Instance(sign.NotebookNotary) |
|
35 | notary = Instance(sign.NotebookNotary) | |
@@ -251,19 +240,4 b' class NotebookManager(LoggingConfigurable):' | |||||
251 | if not trusted: |
|
240 | if not trusted: | |
252 | self.log.warn("Notebook %s/%s is not trusted", path, name) |
|
241 | self.log.warn("Notebook %s/%s is not trusted", path, name) | |
253 | self.notary.mark_cells(nb, trusted) |
|
242 | self.notary.mark_cells(nb, trusted) | |
254 |
|
||||
255 | def _notebook_dir_changed(self, name, old, new): |
|
|||
256 | """Do a bit of validation of the notebook dir.""" |
|
|||
257 | if not os.path.isabs(new): |
|
|||
258 | # If we receive a non-absolute path, make it absolute. |
|
|||
259 | self.notebook_dir = os.path.abspath(new) |
|
|||
260 | return |
|
|||
261 | if os.path.exists(new) and not os.path.isdir(new): |
|
|||
262 | raise TraitError("notebook dir %r is not a directory" % new) |
|
|||
263 | if not os.path.exists(new): |
|
|||
264 | self.log.info("Creating notebook dir %s", new) |
|
|||
265 | try: |
|
|||
266 | os.mkdir(new) |
|
|||
267 | except: |
|
|||
268 | raise TraitError("Couldn't create notebook dir %r" % new) |
|
|||
269 |
|
243 |
@@ -23,12 +23,6 b' class TestFileNotebookManager(TestCase):' | |||||
23 | fm = FileNotebookManager(notebook_dir=td) |
|
23 | fm = FileNotebookManager(notebook_dir=td) | |
24 | self.assertEqual(fm.notebook_dir, td) |
|
24 | self.assertEqual(fm.notebook_dir, td) | |
25 |
|
25 | |||
26 | def test_create_nb_dir(self): |
|
|||
27 | with TemporaryDirectory() as td: |
|
|||
28 | nbdir = os.path.join(td, 'notebooks') |
|
|||
29 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
|||
30 | self.assertEqual(fm.notebook_dir, nbdir) |
|
|||
31 |
|
||||
32 | def test_missing_nb_dir(self): |
|
26 | def test_missing_nb_dir(self): | |
33 | with TemporaryDirectory() as td: |
|
27 | with TemporaryDirectory() as td: | |
34 | nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') |
|
28 | nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') | |
@@ -42,20 +36,20 b' class TestFileNotebookManager(TestCase):' | |||||
42 | # full filesystem path should be returned with correct operating system |
|
36 | # full filesystem path should be returned with correct operating system | |
43 | # separators. |
|
37 | # separators. | |
44 | with TemporaryDirectory() as td: |
|
38 | with TemporaryDirectory() as td: | |
45 |
nbdir = |
|
39 | nbdir = td | |
46 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
40 | fm = FileNotebookManager(notebook_dir=nbdir) | |
47 | path = fm.get_os_path('test.ipynb', '/path/to/notebook/') |
|
41 | path = fm._get_os_path('test.ipynb', '/path/to/notebook/') | |
48 | rel_path_list = '/path/to/notebook/test.ipynb'.split('/') |
|
42 | rel_path_list = '/path/to/notebook/test.ipynb'.split('/') | |
49 | fs_path = os.path.join(fm.notebook_dir, *rel_path_list) |
|
43 | fs_path = os.path.join(fm.notebook_dir, *rel_path_list) | |
50 | self.assertEqual(path, fs_path) |
|
44 | self.assertEqual(path, fs_path) | |
51 |
|
45 | |||
52 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
46 | fm = FileNotebookManager(notebook_dir=nbdir) | |
53 | path = fm.get_os_path('test.ipynb') |
|
47 | path = fm._get_os_path('test.ipynb') | |
54 | fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') |
|
48 | fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') | |
55 | self.assertEqual(path, fs_path) |
|
49 | self.assertEqual(path, fs_path) | |
56 |
|
50 | |||
57 | fm = FileNotebookManager(notebook_dir=nbdir) |
|
51 | fm = FileNotebookManager(notebook_dir=nbdir) | |
58 | path = fm.get_os_path('test.ipynb', '////') |
|
52 | path = fm._get_os_path('test.ipynb', '////') | |
59 | fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') |
|
53 | fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') | |
60 | self.assertEqual(path, fs_path) |
|
54 | self.assertEqual(path, fs_path) | |
61 |
|
55 |
@@ -62,7 +62,7 b' class SessionRootHandler(IPythonHandler):' | |||||
62 | if sm.session_exists(name=name, path=path): |
|
62 | if sm.session_exists(name=name, path=path): | |
63 | model = sm.get_session(name=name, path=path) |
|
63 | model = sm.get_session(name=name, path=path) | |
64 | else: |
|
64 | else: | |
65 |
kernel_id = km.start_kernel( |
|
65 | kernel_id = km.start_kernel(path=path) | |
66 | model = sm.create_session(name=name, path=path, kernel_id=kernel_id) |
|
66 | model = sm.create_session(name=name, path=path, kernel_id=kernel_id) | |
67 | location = url_path_join(self.base_url, 'api', 'sessions', model['id']) |
|
67 | location = url_path_join(self.base_url, 'api', 'sessions', model['id']) | |
68 | self.set_header('Location', url_escape(location)) |
|
68 | self.set_header('Location', url_escape(location)) |
@@ -11,10 +11,16 b'' | |||||
11 | # Imports |
|
11 | # Imports | |
12 | #----------------------------------------------------------------------------- |
|
12 | #----------------------------------------------------------------------------- | |
13 |
|
13 | |||
|
14 | import os | |||
|
15 | from tempfile import NamedTemporaryFile | |||
|
16 | ||||
14 | import nose.tools as nt |
|
17 | import nose.tools as nt | |
15 |
|
18 | |||
|
19 | from IPython.utils.tempdir import TemporaryDirectory | |||
|
20 | from IPython.utils.traitlets import TraitError | |||
16 | import IPython.testing.tools as tt |
|
21 | import IPython.testing.tools as tt | |
17 | from IPython.html import notebookapp |
|
22 | from IPython.html import notebookapp | |
|
23 | NotebookApp = notebookapp.NotebookApp | |||
18 |
|
24 | |||
19 | #----------------------------------------------------------------------------- |
|
25 | #----------------------------------------------------------------------------- | |
20 | # Test functions |
|
26 | # Test functions | |
@@ -25,7 +31,7 b' def test_help_output():' | |||||
25 | tt.help_all_output_test('notebook') |
|
31 | tt.help_all_output_test('notebook') | |
26 |
|
32 | |||
27 | def test_server_info_file(): |
|
33 | def test_server_info_file(): | |
28 |
nbapp = |
|
34 | nbapp = NotebookApp(profile='nbserver_file_test') | |
29 | def get_servers(): |
|
35 | def get_servers(): | |
30 | return list(notebookapp.list_running_servers(profile='nbserver_file_test')) |
|
36 | return list(notebookapp.list_running_servers(profile='nbserver_file_test')) | |
31 | nbapp.initialize(argv=[]) |
|
37 | nbapp.initialize(argv=[]) | |
@@ -38,4 +44,29 b' def test_server_info_file():' | |||||
38 | nt.assert_equal(get_servers(), []) |
|
44 | nt.assert_equal(get_servers(), []) | |
39 |
|
45 | |||
40 | # The ENOENT error should be silenced. |
|
46 | # The ENOENT error should be silenced. | |
41 | nbapp.remove_server_info_file() No newline at end of file |
|
47 | nbapp.remove_server_info_file() | |
|
48 | ||||
|
49 | def test_nb_dir(): | |||
|
50 | with TemporaryDirectory() as td: | |||
|
51 | app = NotebookApp(notebook_dir=td) | |||
|
52 | nt.assert_equal(app.notebook_dir, td) | |||
|
53 | ||||
|
54 | def test_create_nb_dir(): | |||
|
55 | with TemporaryDirectory() as td: | |||
|
56 | nbdir = os.path.join(td, 'notebooks') | |||
|
57 | app = NotebookApp(notebook_dir=nbdir) | |||
|
58 | nt.assert_equal(app.notebook_dir, nbdir) | |||
|
59 | ||||
|
60 | def test_missing_nb_dir(): | |||
|
61 | with TemporaryDirectory() as td: | |||
|
62 | nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') | |||
|
63 | app = NotebookApp() | |||
|
64 | with nt.assert_raises(TraitError): | |||
|
65 | app.notebook_dir = nbdir | |||
|
66 | ||||
|
67 | def test_invalid_nb_dir(): | |||
|
68 | with NamedTemporaryFile() as tf: | |||
|
69 | app = NotebookApp() | |||
|
70 | with nt.assert_raises(TraitError): | |||
|
71 | app.notebook_dir = tf | |||
|
72 |
@@ -81,7 +81,7 b' def url_unescape(path):' | |||||
81 | ]) |
|
81 | ]) | |
82 |
|
82 | |||
83 | def is_hidden(abs_path, abs_root=''): |
|
83 | def is_hidden(abs_path, abs_root=''): | |
84 |
"""Is a file |
|
84 | """Is a file hidden or contained in a hidden directory? | |
85 |
|
85 | |||
86 | This will start with the rightmost path element and work backwards to the |
|
86 | This will start with the rightmost path element and work backwards to the | |
87 | given root to see if a path is hidden or in a hidden directory. Hidden is |
|
87 | given root to see if a path is hidden or in a hidden directory. Hidden is | |
@@ -112,3 +112,14 b" def is_hidden(abs_path, abs_root=''):" | |||||
112 |
|
112 | |||
113 | return False |
|
113 | return False | |
114 |
|
114 | |||
|
115 | def to_os_path(path, root=''): | |||
|
116 | """Convert an API path to a filesystem path | |||
|
117 | ||||
|
118 | If given, root will be prepended to the path. | |||
|
119 | root must be a filesystem path already. | |||
|
120 | """ | |||
|
121 | parts = path.strip('/').split('/') | |||
|
122 | parts = [p for p in parts if p != ''] # remove duplicate splits | |||
|
123 | path = os.path.join(root, *parts) | |||
|
124 | return path | |||
|
125 |
General Comments 0
You need to be logged in to leave comments.
Login now