IPEP 27: Contents Service - ShuaiYAN/ipython GitHub Wiki
Status | Implemented |
Author | Min RK <[email protected]> |
Created | May 30, 2014 |
Updated | Dec 9, 2014 |
Implementation | PR #5938 |
Moving the generic file-handling code (save, load, list, delete) out of the notebook API, and into a generic contents API. The only notebook-specific actions are trust and signing.
API:
- Create new file
- Upload a file
- Copy a file
- List contents
- Get an existing file
- Rename a file
- Save file
- Delete file
- List checkpoints
- Create a checkpoint
- Restore from a checkpoint
- Delete a checkpoint
This webservice handles operations on files - plain files, directories, and notebooks.
A basic contents model:
{
"name": "My Notebook.ipynb",
"path": "foo/bar/My Notebook.ipynb",
"type": "notebook",
"writable": "true",
"created": "2013-10-01T14:22:36.123456+00:00",
"last_modified": "2013-10-02T11:29:27.616675+00:00",
"mimetype": null,
"content": null,
"format": null,
}
All contents models have basic name, path, type, writable, created, and last_modified keys defined.
-
path
field contains the full file path. It will not start with/
, and it will be/
-delimited. -
name
is always equivalent to the last part of the path field. -
writable
indicates whether the requester has permission to edit the file they have requested. -
type
will have one of three values:"directory"
"file"
"notebook"
If the model does not contain content
, the content
, format
, and mimetype
keys will be null
.
mimetype
is used to specify the mime-type of file
contents, so it is only non-null when content
is present and type
is file
.
If the model includes the file content (e.g. requesting a file with GET),
then content
will be defined, and its format described in format
.
There are a few basic cases:
-
type="directory"
-
content
will be a list containing a model for each item in the directory format="json"
-
-
type="file"
, file is UTF-8 text-
content
will be the text of the file as a string format="text"
-
mimetype="mime/type"
will be the mime-type of the file (text/plain if unknown)
-
-
type="file"
, file is binary-
content
will be the base64-encoded content of the file as a string format="base64"
-
mimetype="mime/type"
will be the mime-type of the file (application/octet-stream if unknown)
-
-
type="notebook"
-
content
will be the JSON structure of the file (dict) format="json"
-
Client requests will never specify a whole model. They will only specify those values that should indicate a change.
The timestamps in the file model are read-only. A request to the server to update or upload a file model should never contain the timestamp keys (they will be ignored).
Since path
is specified in the URL, it is generally omitted from requests.
One exception being renaming an existing file,
in which case the destination name and path are given in the model,
and the current name and path are in the URL.
There are a few ways to create file: creating empty untitled files, uploading, and copying existing files. In each case, POST requests are always made to a directory URL, in which case the server picks the new file's name. If you want to upload a file to a specific URL, make a PUT request to the full path URL.
Creates a new file. The name will be the first unused name of the form "Untitled#.ipynb"
, with the first available integer.
POST /api/contents/[:path]
Creating new untitled files via POST accepts one or two optional arguments:
- ext
- the file extension (ignored if `type` is `'notebook'`)
- type
- the model type, one of 'file', 'notebook', or 'directory'
If both of these are unspecified, an empty file with no extension will be created.
status: 201 Created
Location: /api/contents/foo/bar/Untitled0.ipynb
{
"name": "Untitled0.ipynb",
"path": "foo/bar/Untitled0.ipynb",
"type": "notebook",
"created": "2013-09-01T09:15:14.12345+00:00",
"last_modified": "2013-10-02T11:29:27.616675+00:00"
}
Uploads a new file to path
, or creates an empty directory at path
.
PUT /api/contents/[:path]
- type
- One of file, notebook, or directory
- format
- One of json (notebook), text, or base64 (file). Ignored if type is 'directory'.
- content
- The actual file content (json for notebook, text or b64 for file, ignored for directory)
Example:
{
"path": "foo/bar/MyNotebook.ipynb",
"name": "MyNotebook.ipynb",
"type": "notebook",
"format": "json",
"content": {
"metadata":{},
"nbformat": 4,
"nbformat_minor": 0,
"cells": []
}
}
status: 201 Created
Location: /api/contents/foo/bar/MyNotebook.ipynb
{
"name": "MyNotebook.ipynb",
"path": "foo/bar/MyNotebook.ipynb",
"type": "notebook",
"created": "2013-09-01T09:15:14.12345+00:00",
"last_modified": "2013-09-01T09:15:14.12345+00:00"
}
Create a new file from a copy of an existing file.
The new file will have a name of the form {copy_from}-Copy#.{ext}
,
with the first available integer.
POST /api/contents/[:path]
- copy_from
- The file name to copy from
Example:
POST /api/contents/foo/bar/
{
"copy_from" : "/baz/bat/MyNotebook.ipynb"
}
status: 201 Created
Location: /api/contents/foo/bar/MyNotebook-Copy0.ipynb
{
"name": "MyNotebook-Copy0.ipynb",
"path": "foo/bar/MyNotebook-Copy0.ipynb",
"type": "notebook",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-09-01T09:15:14.12345+00:00"
}
GET on a directory returns the directory model.
The content
of a directory model is a list of models (without content)
of the directory's contents.
GET /api/contents/[:path]
status: 200 OK
{
"name": "bar",
"path": "foo/bar",
"type": "directory",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-10-02T11:29:27.616675+00:00",
"format": "json",
"content": [
{
"name": "notebook1.ipynb",
"path": "foo/bar/notebook1.ipynb",
"type": "notebook",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-10-02T11:29:27.616675+00:00"
},
{
"name": "file.txt",
"path": "foo/bar/file.txt",
"type": "file",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-10-02T11:29:27.616675+00:00"
},
{
"name": "subdir",
"path": "foo/bar/subdir",
"type": "directory",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-10-02T11:29:27.616675+00:00"
},
]
}
Returns the contents model for a given file path, including the full document content.
GET /api/contents/[:path]
status: 200 OK
{
"name": "notebook1.ipynb",
"path": "foo/bar/notebook1.ipynb",
"type": "notebook",
"format": "json",
"writable": true,
"last_modified": "2013-10-02T11:29:27.616675+00:00",
"created": "2013-10-01T12:21:20.123456+00:00",
"content":{
"metadata": {},
"nbformat": 4,
"nbformat_minor": 0,
"cells": []
}
}
A client can optionally specify a type
and/or format
argument via URL parameter.
When given, the Contents service shall return a model in the requested type and/or format.
If the request cannot be satisfied, e.g. type=text
is requested, but the file is binary,
then the request shall fail with 400
and have a JSON response containing a 'reason' field,
with the value 'bad format' or 'bad type', depending on what was requested. A client can also optionally specify a content
argument via URL parameter. If content=1
is requested (the default), the full contents of the document are returned. If content=0
is requested, the content
field is empty.
GET /path/to/file.bin?format=text
status: 400 Bad Request
{
"error": "File is not text",
"reason": "bad format"
}
Make a PATCH request with a new path
in the body to move a file to a new location.
This request moves the file and returns the updated model without content.
PATCH /api/contents/[:path]
- path
- The new path (e.g. `foo/bar/notebook.ipynb`)
status: 200 OK
Location: /api/contents/foo/bar/notebook1.ipynb
{
"name": "notebook1.ipynb",
"path": "foo/bar/notebook1.ipynb",
"type": "notebook",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-10-04T14:32:19.123456+00:00"
}
Update an existing file in-place. This is how standard saves are performed.
Returns the updated model without content.
PUT /api/contents/[:path]
- name
- The new filename (`my notebook.ipynb`), if changed
- path
- The new path for the file, if changed
- type
- One of 'notebook', 'file', or 'directory'
- format
- One of 'json', 'text', or 'base64'
- content
- The actual body of the document (excluding 'type=directory')
Example:
PUT /api/contents/foo/bar/notebook1.ipynb
{
"type": "notebook",
"format": "json",
"content": {
"metadata":{},
"nbformat": 4,
"nbformat_minor": 0,
"cells": []
}
}
status: 200 OK
{
"name": "notebook1.ipynb",
"path": "foo/bar/notebook1.ipynb",
"type": "notebook",
"created": "2013-10-01T12:21:20.123456+00:00",
"last_modified": "2013-10-04T14:32:19.123456+00:00"
}
Deletes a file from the specified location.
DELETE /api/contents/[:path]
status: 204 No Content
Will raise 400 if you attempt to delete a non-empty directory.
List checkpoints for a given file. There will typically be zero or one results.
GET /api/contents/[:path]/[:name.ipynb]/checkpoints
status: 200 OK
[
{
"id" : "checkpoint-id",
"last_modified" : "2013-10-18T23:54:40+00:00"
}
]
Create a new checkpoint with the current state of a file. With the default FileContentsManager, only one checkpoint is supported, so creating new checkpoints clobbers existing ones.
POST /api/contents/[:path]/[:name.ipynb]/checkpoints
status: 201 Created
Location: /api/contents/foo/bar/Untitled0.ipynb/checkpoints/[checkpoint_id]
{
"id" : "checkpoint-id",
"last_modified" : "2013-10-18T23:54:40+00:00"
}
Restore a file to a particular checkpointed state.
POST /api/contents/[:path]/[:name.ipynb]/checkpoints/[checkpoint_id]
status: 204 No Content
Delete a checkpoint.
DELETE /api/contents/[:path]/[:name.ipynb]/checkpoints/[checkpoint_id]
status: 204 No Content
This section describes just the changes from the implementation in IPEP 16 / IPython 2.0.
The client-visible changes to the REST API
- replace
/api/notebooks/
with/api/contents
- add
type
key to content model (one ofdirectory
,file
,notebook
) - GET on a directory gives a listing of its contents
- add
format
key to content model. one of:- json (notebooks)
- text (utf-8 files)
- base64 (binary files)
- only copy with POST, never PUT
- only create empty files with POST, never PUT
- only upload with PUT, never POST
-
path
key in model is now the full path.name
is redundant, but kept for convenience.
The changes in the Python API, relevant to authors of alternative ContentsManagers (formerly NotebookManagers).
- ...services.notebooks.manager.NotebookManager moved to ...services.contents.manager.ContentsManager
- FileNotebookManager.notebook_dir is now FileContentsManager.root_dir
- Most
foo_notebook
methods renamed to justfoo
, except:get_model
file_exists
-
trust_notebook
left alone
-
increment_filename
takes and returns a full filename (with ext), not just the base (no ext) - separate
path
,name
arguments are replaced with singlepath
argument containing full path