Show More
@@ -265,6 +265,11 b" flags['no-mathjax']=(" | |||||
265 | """ |
|
265 | """ | |
266 | ) |
|
266 | ) | |
267 |
|
267 | |||
|
268 | # Add notebook manager flags | |||
|
269 | flags.update(boolean_flag('script', 'FileContentsManager.save_script', | |||
|
270 | 'DEPRECATED, IGNORED', | |||
|
271 | 'DEPRECATED, IGNORED')) | |||
|
272 | ||||
268 | aliases = dict(base_aliases) |
|
273 | aliases = dict(base_aliases) | |
269 |
|
274 | |||
270 | aliases.update({ |
|
275 | aliases.update({ |
@@ -17,20 +17,27 b' from IPython.utils.path import ensure_dir_exists' | |||||
17 | from IPython.utils.traitlets import Unicode, Bool, TraitError |
|
17 | from IPython.utils.traitlets import Unicode, Bool, TraitError | |
18 | from IPython.utils.py3compat import getcwd |
|
18 | from IPython.utils.py3compat import getcwd | |
19 | from IPython.utils import tz |
|
19 | from IPython.utils import tz | |
20 | from IPython.html.utils import is_hidden, to_os_path |
|
20 | from IPython.html.utils import is_hidden, to_os_path, url_path_join | |
21 |
|
21 | |||
22 |
|
22 | |||
23 | class FileContentsManager(ContentsManager): |
|
23 | class FileContentsManager(ContentsManager): | |
24 |
|
24 | |||
25 | root_dir = Unicode(getcwd(), config=True) |
|
25 | root_dir = Unicode(getcwd(), config=True) | |
26 |
|
26 | |||
|
27 | save_script = Bool(False, config=True, help='DEPRECATED, IGNORED') | |||
|
28 | def _save_script_changed(self): | |||
|
29 | self.log.warn(""" | |||
|
30 | Automatically saving notebooks as scripts has been removed. | |||
|
31 | Use `ipython nbconvert --to python [notebook]` instead. | |||
|
32 | """) | |||
|
33 | ||||
27 | def _root_dir_changed(self, name, old, new): |
|
34 | def _root_dir_changed(self, name, old, new): | |
28 | """Do a bit of validation of the root_dir.""" |
|
35 | """Do a bit of validation of the root_dir.""" | |
29 | if not os.path.isabs(new): |
|
36 | if not os.path.isabs(new): | |
30 | # If we receive a non-absolute path, make it absolute. |
|
37 | # If we receive a non-absolute path, make it absolute. | |
31 | self.root_dir = os.path.abspath(new) |
|
38 | self.root_dir = os.path.abspath(new) | |
32 | return |
|
39 | return | |
33 |
if |
|
40 | if not os.path.isdir(new): | |
34 | raise TraitError("%r is not a directory" % new) |
|
41 | raise TraitError("%r is not a directory" % new) | |
35 |
|
42 | |||
36 | checkpoint_dir = Unicode('.ipynb_checkpoints', config=True, |
|
43 | checkpoint_dir = Unicode('.ipynb_checkpoints', config=True, | |
@@ -70,7 +77,7 b' class FileContentsManager(ContentsManager):' | |||||
70 | API path to be evaluated relative to root_dir. |
|
77 | API path to be evaluated relative to root_dir. | |
71 | """ |
|
78 | """ | |
72 | if name is not None: |
|
79 | if name is not None: | |
73 |
path = path |
|
80 | path = url_path_join(path, name) | |
74 | return to_os_path(path, self.root_dir) |
|
81 | return to_os_path(path, self.root_dir) | |
75 |
|
82 | |||
76 | def path_exists(self, path): |
|
83 | def path_exists(self, path): | |
@@ -177,11 +184,15 b' class FileContentsManager(ContentsManager):' | |||||
177 | """ |
|
184 | """ | |
178 | os_path = self._get_os_path(name, path) |
|
185 | os_path = self._get_os_path(name, path) | |
179 |
|
186 | |||
|
187 | four_o_four = u'directory does not exist: %r' % os_path | |||
|
188 | ||||
180 | if not os.path.isdir(os_path): |
|
189 | if not os.path.isdir(os_path): | |
181 |
raise web.HTTPError(404, |
|
190 | raise web.HTTPError(404, four_o_four) | |
182 | elif is_hidden(os_path, self.root_dir): |
|
191 | elif is_hidden(os_path, self.root_dir): | |
183 |
self.log.info("Refusing to serve hidden directory, via 404 Error" |
|
192 | self.log.info("Refusing to serve hidden directory %r, via 404 Error", | |
184 | raise web.HTTPError(404, u'directory does not exist: %r' % os_path) |
|
193 | os_path | |
|
194 | ) | |||
|
195 | raise web.HTTPError(404, four_o_four) | |||
185 |
|
196 | |||
186 | if name is None: |
|
197 | if name is None: | |
187 | if '/' in path: |
|
198 | if '/' in path: | |
@@ -212,14 +223,13 b' class FileContentsManager(ContentsManager):' | |||||
212 | model['type'] = 'file' |
|
223 | model['type'] = 'file' | |
213 | if content: |
|
224 | if content: | |
214 | os_path = self._get_os_path(name, path) |
|
225 | os_path = self._get_os_path(name, path) | |
|
226 | with io.open(os_path, 'rb') as f: | |||
|
227 | bcontent = f.read() | |||
215 | try: |
|
228 | try: | |
216 | with io.open(os_path, 'r', encoding='utf-8') as f: |
|
229 | model['content'] = bcontent.decode('utf8') | |
217 | model['content'] = f.read() |
|
|||
218 | except UnicodeError as e: |
|
230 | except UnicodeError as e: | |
219 | with io.open(os_path, 'rb') as f: |
|
231 | model['content'] = base64.encodestring(bcontent).decode('ascii') | |
220 | bcontent = f.read() |
|
232 | model['format'] = 'base64' | |
221 | model['content'] = base64.encodestring(bcontent).decode('ascii') |
|
|||
222 | model['format'] = 'base64' |
|
|||
223 | else: |
|
233 | else: | |
224 | model['format'] = 'text' |
|
234 | model['format'] = 'text' | |
225 | return model |
|
235 | return model | |
@@ -307,10 +317,14 b' class FileContentsManager(ContentsManager):' | |||||
307 |
|
317 | |||
308 | def _save_directory(self, os_path, model, name='', path=''): |
|
318 | def _save_directory(self, os_path, model, name='', path=''): | |
309 | """create a directory""" |
|
319 | """create a directory""" | |
|
320 | if is_hidden(os_path, self.root_dir): | |||
|
321 | raise web.HTTPError(400, u'Cannot create hidden directory %r' % os_path) | |||
310 | if not os.path.exists(os_path): |
|
322 | if not os.path.exists(os_path): | |
311 | os.mkdir(os_path) |
|
323 | os.mkdir(os_path) | |
312 | elif not os.path.isdir(os_path): |
|
324 | elif not os.path.isdir(os_path): | |
313 | raise web.HTTPError(400, u'Not a directory: %s' % (os_path)) |
|
325 | raise web.HTTPError(400, u'Not a directory: %s' % (os_path)) | |
|
326 | else: | |||
|
327 | self.log.debug("Directory %r already exists", os_path) | |||
314 |
|
328 | |||
315 | def save(self, model, name='', path=''): |
|
329 | def save(self, model, name='', path=''): | |
316 | """Save the file model and return the model with no content.""" |
|
330 | """Save the file model and return the model with no content.""" | |
@@ -372,7 +386,7 b' class FileContentsManager(ContentsManager):' | |||||
372 | if os.path.isdir(os_path): |
|
386 | if os.path.isdir(os_path): | |
373 | listing = os.listdir(os_path) |
|
387 | listing = os.listdir(os_path) | |
374 | # don't delete non-empty directories (checkpoints dir doesn't count) |
|
388 | # don't delete non-empty directories (checkpoints dir doesn't count) | |
375 |
if listing and listing != [ |
|
389 | if listing and listing != [self.checkpoint_dir]: | |
376 | raise web.HTTPError(400, u'Directory %s not empty' % os_path) |
|
390 | raise web.HTTPError(400, u'Directory %s not empty' % os_path) | |
377 | elif not os.path.isfile(os_path): |
|
391 | elif not os.path.isfile(os_path): | |
378 | raise web.HTTPError(404, u'File does not exist: %s' % os_path) |
|
392 | raise web.HTTPError(404, u'File does not exist: %s' % os_path) |
@@ -303,6 +303,10 b' class APITest(NotebookTestBase):' | |||||
303 | resp = self.api.mkdir(u'New ∂ir', path=u'å b') |
|
303 | resp = self.api.mkdir(u'New ∂ir', path=u'å b') | |
304 | self._check_created(resp, u'New ∂ir', u'å b', type='directory') |
|
304 | self._check_created(resp, u'New ∂ir', u'å b', type='directory') | |
305 |
|
305 | |||
|
306 | def test_mkdir_hidden_400(self): | |||
|
307 | with assert_http_error(400): | |||
|
308 | resp = self.api.mkdir(u'.hidden', path=u'å b') | |||
|
309 | ||||
306 | def test_upload_txt(self): |
|
310 | def test_upload_txt(self): | |
307 | body = u'ünicode téxt' |
|
311 | body = u'ünicode téxt' | |
308 | model = { |
|
312 | model = { |
@@ -382,7 +382,20 b' define([' | |||||
382 | if (file_ext === '.ipynb') { |
|
382 | if (file_ext === '.ipynb') { | |
383 | model.type = 'notebook'; |
|
383 | model.type = 'notebook'; | |
384 | model.format = 'json'; |
|
384 | model.format = 'json'; | |
385 | model.content = JSON.parse(filedata); |
|
385 | try { | |
|
386 | model.content = JSON.parse(filedata); | |||
|
387 | } catch (e) { | |||
|
388 | dialog.modal({ | |||
|
389 | title : 'Cannot upload invalid Notebook', | |||
|
390 | body : "The error was: " + e, | |||
|
391 | buttons : {'OK' : { | |||
|
392 | 'class' : 'btn-primary', | |||
|
393 | click: function () { | |||
|
394 | item.remove(); | |||
|
395 | } | |||
|
396 | }} | |||
|
397 | }); | |||
|
398 | } | |||
386 | content_type = 'application/json'; |
|
399 | content_type = 'application/json'; | |
387 | } else { |
|
400 | } else { | |
388 | model.type = 'file'; |
|
401 | model.type = 'file'; |
@@ -113,6 +113,9 b" def is_hidden(abs_path, abs_root=''):" | |||||
113 | # check UF_HIDDEN on any location up to root |
|
113 | # check UF_HIDDEN on any location up to root | |
114 | path = abs_path |
|
114 | path = abs_path | |
115 | while path and path.startswith(abs_root) and path != abs_root: |
|
115 | while path and path.startswith(abs_root) and path != abs_root: | |
|
116 | if not os.path.exists(path): | |||
|
117 | path = os.path.dirname(path) | |||
|
118 | continue | |||
116 | try: |
|
119 | try: | |
117 | # may fail on Windows junctions |
|
120 | # may fail on Windows junctions | |
118 | st = os.stat(path) |
|
121 | st = os.stat(path) |
@@ -3,4 +3,4 b'' | |||||
3 | which supports all kinds of files. |
|
3 | which supports all kinds of files. | |
4 | - The Dashboard now lists all files, not just notebooks and directories. |
|
4 | - The Dashboard now lists all files, not just notebooks and directories. | |
5 | - The ``--script`` hook for saving notebooks to Python scripts is removed, |
|
5 | - The ``--script`` hook for saving notebooks to Python scripts is removed, | |
6 |
use |
|
6 | use :samp:`ipython nbconvert --to python {notebook}` instead. |
General Comments 0
You need to be logged in to leave comments.
Login now