IPEP 13: Updating the Message Spec - ShuaiYAN/ipython GitHub Wiki
Status | Implemented |
Author | Min RK <[email protected]> |
Created | April 6, 2013 |
Updated | April 8, 2014 |
Implementation | PR 3190, 4536 |
See also: IPEP 24: completion and object_info for updates to completion and object_info
We've learned a lot in our first 18 months with the message spec, and we got a remarkable amount right. But we didn't get everything right, and we want to clean some things up prior to 1.0.
A few names need to change, because they are Python-specific, and our message spec is not.
NOTE: Since these names have notebook format implications, they will not be changed until 3.0
-
pyin
will becomeexecute_input
-
pyout
will becomeexecute_result
-
pyerr
will becomeerror
pyout
and display_data
are identical messages, with pyout
having an additional prompt_number
field.
It was proposed to merge these two, but preserving the distinction between result
and side effect
at the message level is desirable.
The display_data
message currently has a metadata
field,
which is not yet used for anything.
We propose some basic usage guidelines for the metadata field,
and some associated changes to the Python API and notebook file format.
In general, there is metadata associated with each output.
This is the metadata
key of the display_data
message.
We propose the convention that sub-dicts of the metadata dict,
under the same mime-type keys as the data
dictionary,
should be interpreted as metadata specific to that particular output.
In general, this will mean that rich frontends will want to implement logic similar to the following to handle metadata of a particular output:
data = message['content']['data']
metadata = message['content']['metadata']
handle_global_metadata(metadata)
for mimetype, mime_data in data.items():
mime_md = metadata.get(mimetype)
if mime_md:
handle_mime_specific_metadata(md)
handle_mime_output(mime_data)
In particular, the javascript handlers for each mimetype in the IPython Notebook now receive both the data and the metadata for each individual mimetype, so
OutputArea.prototype.append_html = function (html, element)
has become
OutputArea.prototype.append_html = function (html, md, element)
Where html
is the content of data['text/html']
,
and md
is the content of metadata['text/html']
.
- The
publish_foo
methods fromcore.displaypub
are to be removed. Thedisplay_foo
methods are our public API, anddisplay_foo(raw=True)
has always been an alias topublish_foo
, so no functionality is lost. - add two kwargs to
display
:- raw:
display({'mime': rawdata}, raw=True)
, so thatdisplay
is consistent withdisplay_foo(raw=True)
- metadata:
display(obj, metadata={'key': value})
, adding a public API to the metadata dict.
- raw:
-
display_foo
methods add an extra, optionalmetadata
key. Ifmetadata
is specified, it will be placed in a sub-dict, keyed by the appropriate mimetype forfoo
. -
display_foo
will no longer result in also displaying the plaintext representation of objects. - type formatters, whether registered via
formatter.for_type
, or_repr_foo_
methods may return either the raw data currently supported, or a tuple of length two, where the first element is the raw data and the second element is a metadata dict. This metadata will be added to the resulting message's metadata under the same mimetype key as the display data itself. - metadata is optional - no metadata key must ever be required to be defined. It should always be allowed that the metadata dict is empty.
The only change to the notebook format is that each output in a cell's outputs
list
now has an extra metadata
key, which has the same keys as the output itself.
Note: The shortname json
, png
transforms made to the output dict
were a bad decision, and will be changed to proper mime-types in the future.
The metadata field is a black-box dict, which you can write to. It is expected that third-party code, such as rich javascript plugins, will want to communicate some extra information via the metadata field. It is appropriate for developers to write their info to the metadata field, but some basic practices should be followed to avoid conflict:
- third-parties should define their own key in the metadata, and only write there.
Actually, that's about it. Just the one guideline to avoid namespace conflicts.
Images currently cannot include shape information, which means that one must use raw HTML to display an image with a specific shape. Using the above proposal, we can now support image sizes in the PNG format by defining the following metadata keys:
{
"image/png" : {
"width": 100,
"height": 200
}
}
The same for image/jpeg
The Image
display class supports these metadata via its width
and height
attributes.
If specified, the display size of the image will be specified.
For example:
def png_with_shape(img, w, h):
md = dict(width=w, height=h)
return png_image_data(img), md
would result in the display_data message:
{
"data" : {
"image/png" : "iVBOR...",
...
},
"metadata" : {
"image/png" : {
"height": 4,
"width": 10
}
...
}
}
This does add a key to the notebook format, and adds an interpretation of a particular subset of the metadata, but these are safely ignored. In this way, notebook frontends that do not support the metadata gracefully degrade, simply losing some layout information, but no data.
Nothing will be done on this issue for this IPEP
Perhaps JSON reprs / js widgets are going to be the only way to do this sort of thing.
Add a password
boolean key to input_request
, to indicate that frontends should not echo input.
Message headers will have the protocol version (5.0 as of IPython 3.0) as a string. If the message header lacks a version key, it should be assumed to be 4.1, the last version before this update.
- version keys in
kernel_info_reply
messages will be strings, notversion_info
-style lists. - A
banner
key is added, for the kernel's contribution to console frontend banners. - instead of
ipython_version
,implementation
andimplementation_version
keys are added.
The IPython kernel's kernel_info_reply in IPython 3.0:
{
"protocol_version" : "5.0.0",
"implementation" : "ipython",
"implementation_version" : "3.0.0",
"language" : "python",
"language_version" : "3.4.0",
"banner" : "Python 3.4.0 (/usr/local/bin/python)\nIPython 3.0.0\n",
}
The mime-type structure used in display_data
is useful in more contexts than just display messages.
It will be adopted in other places where representation information is requested, specifically:
- object_info_reply
- user_expressions
- pager payload
A mime-bundle takes the form:
{
"status" : "ok",
"data" : {
"mime-type" : "mime-type-data",
},
"metadata" : {
"mime-type" : {
"metadata" : "about mime-type",
}
}
}
- if
status == 'ok'
, then 'data' and 'metadata' must be defined. - if
status == 'error'
, then 'ename', 'evalue', and 'etraceback' must be defined, as described in the message spec.
In some contexts, the status = 'error'
case may not make sense,
but status=='ok'
should always be checked before working with data
.
IPEP 24: completion and object_info describes turning object_info_reply with a mime-bundle message.
The `application/json`` key in a mime bundle stores JSON-serialized data, rather than JSON-able data. This results in serializing this data twice, and having to deserialize it twice on the frontend. The JSON key will now be unserialized, rather than the serialized form.
Since variable names are expressions, user_variables will be removed, and only user_expressions remaining.
The user_expressions
and user_variables
keys in an execute_request
currently only support plaintext reprs, and have bad custom representations of errors.
These will be replaced with mime-bundles (like display).
The entire Payload model will be revisited in a separate IPEP.
Currently, display payloads list the full object path of the source, which doesn't make any sense. It should be a simple identifier, so that moving the implementation does not need to be reflected in the frontend.
The pager payload will also become a mime-bundle, instead of having just a 'text' key.
An example execute_reply
:
{
"status" : "ok",
"execution_count" : 4,
"user_expressions" : {
"foo" : {
"status": "ok",
"data" : {
"text/plain" : "5",
},
"metadata" : {},
}
},
"payloads" : [
{
"source" : "pager",
"status" : "ok",
"data" : {
"text/plain" : "pager data...",
},
"metadata" : {},
},
]
}