Repository: ipython/ipython

Issue:
better message for unsupported nbformat
Currently, when opening a notebook that is newer than the current format (just change the nbformat to 4 in an existing notebook to test), the user gets the following message in the notebook UI:

> An error occurred while loading this notebook. Most likely this notebook is in a newer format than is supported by this version of IPython. This version can load notebook formats v3 or earlier.

And this message is logged in the nbserver:

> WARNING:root:500 GET /notebooks/8291848e-ba50-407d-88a3-5480e60d700e?_=1334382692585 (127.0.0.1): Unreadable JSON notebook.

The message for an unsupported nb version should be clear and concise, and distinguishable from any _other_ arbitrary failure to load, e.g. from corrupted JSON.  In particular, it should report the actual nbformat version of the notebook that is failing to open, so users can more easily find what version of IPython they need.



Discussion:
Recently with @stefanv we were talking about this, and wondering if it wouldn't be a good idea to move to x.y notebook format identifiers, with the idea that we'd only increment x when the client simply couldn't read the new format _at all_, while y would be incremented for changes that might result in loss of formatting or simplified structure from earlier clients, but that could still be read.  

For example, the introduction of new cell types could be considered a `y+=1`-type increment, as an x.y client could read a x.{y+1} notebook and simply leave all cells of unknown type marked as raw (with a suitable warning).  That would in many cases be an acceptable solution for a user who only wants to be able to get to the code.  Thoughts?

How about something like:

> Error: This notebook format (v4.0) is newer than the installed version of IPython (v3.0).

and

> Warning: This notebook format (v3.1) is newer than the installed version of IPython (v3.0), and some formatting may be lost.

Could we also maybe alert the user that is trying to import (via drag'n'drop ) a too recent/too old notebook into the dashboard. ?

The current error message is much, much better, so I'm tempted to close this blocker issue.  What we have left are two questions:
- improvements and refinements to the format with sub-versions as indicated above by me.
- better reporting on the dashboard as indicated by @Carreau.

Both of these are valid, but not blockers.  Should we close this puppy and just open those two as separate issues?

The message is better, but I would still not consider it good enough to close.  Right now, if notebook load fails _for any reason_, we tell users it's probably just too new.  This is the same message you get for corrupt or otherwise genuinely unreadable files.

I think the current state is tolerable enough to demote from blocker to high, but not to close.  This issue should not be closed until a totally unparsable notebook and a notebook with an unhandled version number get different error messages.

Sounds good, demoting to high.

Should we retarget this for 0.14?  Doing final triage, and we need to be pretty realistic at this point on what's honestly doable...

yes, I think that's fine.

OK, done.


Resolving patch:
diff --git a/IPython/html/services/notebooks/filenbmanager.py b/IPython/html/services/notebooks/filenbmanager.py
--- a/IPython/html/services/notebooks/filenbmanager.py
+++ b/IPython/html/services/notebooks/filenbmanager.py
@@ -146,8 +146,9 @@ def read_notebook_object_from_path(self, path):
             try:
                 # v1 and v2 and json in the .ipynb files.
                 nb = current.reads(s, u'json')
-            except Exception as e:
-                raise web.HTTPError(500, u'Unreadable JSON notebook: %s' % e)
+            except ValueError as e:
+                msg = u"Unreadable Notebook: %s" % e
+                raise web.HTTPError(400, msg, reason=msg)
         return last_modified, nb
     
     def read_notebook_object(self, notebook_id):
diff --git a/IPython/nbformat/current.py b/IPython/nbformat/current.py
--- a/IPython/nbformat/current.py
+++ b/IPython/nbformat/current.py
@@ -40,14 +40,19 @@
 current_nbformat_minor = nbformat_minor
 
 
+class NBFormatError(ValueError):
+    pass
 
-class NBFormatError(Exception):
+class NotJSONError(ValueError):
     pass
 
 
 def parse_json(s, **kwargs):
     """Parse a string into a (nbformat, dict) tuple."""
-    d = json.loads(s, **kwargs)
+    try:
+        d = json.loads(s, **kwargs)
+    except ValueError:
+        raise NotJSONError("Notebook does not appear to be JSON: %r" % s[:16])
     nbf = d.get('nbformat', 1)
     nbm = d.get('nbformat_minor', 0)
     return nbf, nbm, d
@@ -82,7 +87,7 @@ def reads_json(s, **kwargs):
         nb = v3.to_notebook_json(d, **kwargs)
         nb = v3.convert_to_this_nbformat(nb, orig_version=3, orig_minor=minor)
     else:
-        raise NBFormatError('Unsupported JSON nbformat version: %i' % nbf)
+        raise NBFormatError('Unsupported JSON nbformat version %s (supported version: %i)' % (nbf, 3))
     return nb
 
 


Test patch: