Implement atomic save

Ping @fperez, this should avoid issues with corrupted/lost notebooks when the disk is full, though I haven't worked out how to test it just yet.

Closes gh-6254
Is there anyway to raise an exception that wraps the current exception?  

Something like this:
```python
except e as Exception:
    # do some stuff
    # ...
    raise IOException('Atomic write failed.', e) # e gets passed into the constructor
                                                 # of the Exception so you can do Exception.parent to 
                                                 # get e..
```

Not sure if that's possible in Python, but it'd be nice...