mirror of
https://github.com/jupyter/notebook.git
synced 2024-12-09 03:50:45 +08:00
Merge pull request #1253 from ryanlovett/master
Restore atomic intermediate if notebook is invalid.
This commit is contained in:
commit
6b220c949b
@ -42,6 +42,17 @@ def copy2_safe(src, dst, log=None):
|
|||||||
if log:
|
if log:
|
||||||
log.debug("copystat on %s failed", dst, exc_info=True)
|
log.debug("copystat on %s failed", dst, exc_info=True)
|
||||||
|
|
||||||
|
def path_to_intermediate(path):
|
||||||
|
'''Name of the intermediate file used in atomic writes.
|
||||||
|
|
||||||
|
The .~ prefix will make Dropbox ignore the temporary file.'''
|
||||||
|
dirname, basename = os.path.split(path)
|
||||||
|
return os.path.join(dirname, '.~'+basename)
|
||||||
|
|
||||||
|
def path_to_invalid(path):
|
||||||
|
'''Name of invalid file after a failed atomic write and subsequent read.'''
|
||||||
|
dirname, basename = os.path.split(path)
|
||||||
|
return os.path.join(dirname, basename+'.invalid')
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def atomic_writing(path, text=True, encoding='utf-8', log=None, **kwargs):
|
def atomic_writing(path, text=True, encoding='utf-8', log=None, **kwargs):
|
||||||
@ -73,9 +84,8 @@ def atomic_writing(path, text=True, encoding='utf-8', log=None, **kwargs):
|
|||||||
if os.path.islink(path):
|
if os.path.islink(path):
|
||||||
path = os.path.join(os.path.dirname(path), os.readlink(path))
|
path = os.path.join(os.path.dirname(path), os.readlink(path))
|
||||||
|
|
||||||
dirname, basename = os.path.split(path)
|
tmp_path = path_to_intermediate(path)
|
||||||
# The .~ prefix will make Dropbox ignore the temporary file.
|
|
||||||
tmp_path = os.path.join(dirname, '.~'+basename)
|
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
copy2_safe(path, tmp_path, log=log)
|
copy2_safe(path, tmp_path, log=log)
|
||||||
|
|
||||||
@ -249,11 +259,28 @@ class FileManagerMixin(Configurable):
|
|||||||
try:
|
try:
|
||||||
return nbformat.read(f, as_version=as_version)
|
return nbformat.read(f, as_version=as_version)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
e_orig = e
|
||||||
|
|
||||||
|
# If use_atomic_writing is enabled, we'll guess that it was also
|
||||||
|
# enabled when this notebook was written and look for a valid
|
||||||
|
# atomic intermediate.
|
||||||
|
tmp_path = path_to_intermediate(os_path)
|
||||||
|
|
||||||
|
if not self.use_atomic_writing or not os.path.exists(tmp_path):
|
||||||
raise HTTPError(
|
raise HTTPError(
|
||||||
400,
|
400,
|
||||||
u"Unreadable Notebook: %s %r" % (os_path, e),
|
u"Unreadable Notebook: %s %r" % (os_path, e_orig),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Move the bad file aside, restore the intermediate, and try again.
|
||||||
|
invalid_file = path_to_invalid(os_path)
|
||||||
|
# Rename over existing file doesn't work on Windows
|
||||||
|
if os.name == 'nt' and os.path.exists(invalid_file):
|
||||||
|
os.remove(invalid_file)
|
||||||
|
os.rename(os_path, invalid_file)
|
||||||
|
os.rename(tmp_path, os_path)
|
||||||
|
return self._read_notebook(os_path, as_version)
|
||||||
|
|
||||||
def _save_notebook(self, os_path, nb):
|
def _save_notebook(self, os_path, nb):
|
||||||
"""Save a notebook to an os_path."""
|
"""Save a notebook to an os_path."""
|
||||||
with self.atomic_writing(os_path, encoding='utf-8') as f:
|
with self.atomic_writing(os_path, encoding='utf-8') as f:
|
||||||
|
Loading…
Reference in New Issue
Block a user