Repository: ipython/ipython

Issue:
Error 500 while saving IPython notebook
Hi,

I've created a notebook using IPython master and Python 3.3, then opened and changed it in 'ipython notebook' using IPython master and Python 2.7. Now I can't save changes because IPython raises the following exception:

```
Traceback (most recent call last):
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/tornado/web.py", line 1077, in _execute
    *self.path_args, **self.path_kwargs)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/tornado/web.py", line 1892, in wrapper
    return method(self, *args, **kwargs)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/IPython/frontend/html/notebook/handlers.py", line 672, in put
    nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/IPython/frontend/html/notebook/nbmanager.py", line 168, in save_notebook
    self.write_notebook_object(nb, notebook_id)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/IPython/frontend/html/notebook/filenbmanager.py", line 175, in write_notebook_object
    old_checkpoints = self.list_checkpoints(notebook_id)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/IPython/frontend/html/notebook/filenbmanager.py", line 313, in list_checkpoints
    path = self.get_checkpoint_path(notebook_id, checkpoint_id)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/IPython/frontend/html/notebook/filenbmanager.py", line 277, in get_checkpoint_path
    return self.get_checkpoint_path_by_name(name, checkpoint_id)
  File "/Users/kmike/envs/scraping/lib/python2.7/site-packages/IPython/frontend/html/notebook/filenbmanager.py", line 269, in get_checkpoint_path_by_name
    ext=self.filename_ext,
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-8: ordinal not in range(128)
```



Discussion:
Notebook title has non-ASCII characters. Changing it to be ASCII-only makes save work. 


Resolving patch:
diff --git a/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py b/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py
--- a/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py
+++ b/IPython/frontend/html/notebook/services/notebooks/filenbmanager.py
@@ -21,6 +21,7 @@
 import os
 import glob
 import shutil
+from unicodedata import normalize
 
 from tornado import web
 
@@ -78,7 +79,7 @@ def get_notebook_names(self):
         """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 @@ def read_notebook_object(self, notebook_id):
     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 @@ def increment_filename(self, basename):
     
     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 @@ def create_checkpoint(self, notebook_id):
         """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 @@ def list_checkpoints(self, notebook_id):
         
         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 []


Test patch: