From 718bf61bd31c9ac08c03bc1613794c2442ac80a3 Mon Sep 17 00:00:00 2001 From: MinRK Date: Tue, 28 May 2013 11:47:05 -0700 Subject: [PATCH] normalize unicode notebook filenames used in comparison check for notebook name change. Unless the filenames are normalized, unchanged names may result in false positives for a name change (e.g. OS X uses NFD on the filesystem, so u'\xfc' roundtripped to the filesystem will be u'u\u0308'), which can result in the first save of a notebook after open performing the following actions: 1. save the recently opened notebook 2. `old_name != new_name`, so name change detected 3. delete old_name (which is actually new_name), which ultimately deletes the just-saved notebook In master, this has a symptom of the first checkpoint failing because the first save actually deleted the file, and you can't checkpoint a notebook that doesn't exist. closes #3360 --- .../html/notebook/services/notebooks/filenbmanager.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py b/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py index b430db657..16e8ce3ca 100644 --- a/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py +++ b/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py @@ -21,6 +21,7 @@ import io import os import glob import shutil +from unicodedata import normalize from tornado import web @@ -78,7 +79,7 @@ class FileNotebookManager(NotebookManager): """List all notebook names in the notebook dir.""" names = glob.glob(os.path.join(self.notebook_dir, '*' + self.filename_ext)) - names = [os.path.splitext(os.path.basename(name))[0] + names = [normalize('NFC', os.path.splitext(os.path.basename(name))[0]) for name in names] return names @@ -161,7 +162,7 @@ class FileNotebookManager(NotebookManager): def write_notebook_object(self, nb, notebook_id=None): """Save an existing notebook object by notebook_id.""" try: - new_name = nb.metadata.name + new_name = normalize('NFC', nb.metadata.name) except AttributeError: raise web.HTTPError(400, u'Missing notebook name') @@ -263,7 +264,7 @@ class FileNotebookManager(NotebookManager): def get_checkpoint_path_by_name(self, name, checkpoint_id): """Return a full path to a notebook checkpoint, given its name and checkpoint id.""" - filename = "{name}-{checkpoint_id}{ext}".format( + filename = u"{name}-{checkpoint_id}{ext}".format( name=name, checkpoint_id=checkpoint_id, ext=self.filename_ext, @@ -294,7 +295,7 @@ class FileNotebookManager(NotebookManager): """Create a checkpoint from the current state of a notebook""" nb_path = self.get_path(notebook_id) # only the one checkpoint ID: - checkpoint_id = "checkpoint" + checkpoint_id = u"checkpoint" cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id) self.log.debug("creating checkpoint for notebook %s", notebook_id) if not os.path.exists(self.checkpoint_dir): @@ -309,7 +310,7 @@ class FileNotebookManager(NotebookManager): This notebook manager currently only supports one checkpoint per notebook. """ - checkpoint_id = "checkpoint" + checkpoint_id = u"checkpoint" path = self.get_checkpoint_path(notebook_id, checkpoint_id) if not os.path.exists(path): return []