diff --git a/IPython/frontend/html/notebook/static/js/kernel.js b/IPython/frontend/html/notebook/static/js/kernel.js
index 761402b..e446d5c 100644
--- a/IPython/frontend/html/notebook/static/js/kernel.js
+++ b/IPython/frontend/html/notebook/static/js/kernel.js
@@ -220,19 +220,19 @@ var IPython = (function (IPython) {
// 'set_next_input': set_next_input_callback
// }
//
- // The execute_reply_callback will be passed the content object of the execute_reply
+ // The execute_reply_callback will be passed the content and metadata objects of the execute_reply
// message documented here:
//
// http://ipython.org/ipython-doc/dev/development/messaging.html#execute
//
// The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
- // of the output and the content and header objects of the PUB/SUB channel that contains the
+ // of the output and the content and metadata objects of the PUB/SUB channel that contains the
// output:
//
// http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
//
// The clear_output_callback will be passed a content object that contains
- // stdout, stderr and other fields that are booleans, as well as the header object.
+ // stdout, stderr and other fields that are booleans, as well as the metadata object.
//
// The set_next_input_callback will be passed the text that should become the next
// input cell.
@@ -313,12 +313,13 @@ var IPython = (function (IPython) {
reply = $.parseJSON(e.data);
var header = reply.header;
var content = reply.content;
+ var metadata = reply.metadata;
var msg_type = header.msg_type;
var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
if (callbacks !== undefined) {
var cb = callbacks[msg_type];
if (cb !== undefined) {
- cb(content);
+ cb(content, metadata);
}
};
@@ -347,10 +348,10 @@ var IPython = (function (IPython) {
Kernel.prototype._handle_iopub_reply = function (e) {
- reply = $.parseJSON(e.data);
+ var reply = $.parseJSON(e.data);
var content = reply.content;
var msg_type = reply.header.msg_type;
- var header = reply.header;
+ var metadata = reply.metadata;
var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
if (msg_type !== 'status' && callbacks === undefined) {
// Message not from one of this notebook's cells and there are no
@@ -361,7 +362,7 @@ var IPython = (function (IPython) {
if (output_types.indexOf(msg_type) >= 0) {
var cb = callbacks['output'];
if (cb !== undefined) {
- cb(msg_type, content, header);
+ cb(msg_type, content, metadata);
}
} else if (msg_type === 'status') {
if (content.execution_state === 'busy') {
@@ -375,7 +376,7 @@ var IPython = (function (IPython) {
} else if (msg_type === 'clear_output') {
var cb = callbacks['clear_output'];
if (cb !== undefined) {
- cb(content, header);
+ cb(content, metadata);
}
};
};
diff --git a/IPython/zmq/session.py b/IPython/zmq/session.py
index 18cde61..6c16c63 100644
--- a/IPython/zmq/session.py
+++ b/IPython/zmq/session.py
@@ -292,8 +292,8 @@ class Session(Configurable):
username = Unicode(os.environ.get('USER',u'username'), config=True,
help="""Username for the Session. Default is your system username.""")
- subheader = Dict({}, config=True,
- help="""Subheader dictionary, which serves as the default subheader fields for each message.""")
+ metadata = Dict({}, config=True,
+ help="""Metadata dictionary, which serves as the default top-level metadata dict for each message.""")
# message signature related traits:
@@ -405,7 +405,7 @@ class Session(Configurable):
def msg_header(self, msg_type):
return msg_header(self.msg_id, msg_type, self.username, self.session)
- def msg(self, msg_type, content=None, parent=None, subheader=None, header=None):
+ def msg(self, msg_type, content=None, parent=None, subheader=None, header=None, metadata=None):
"""Return the nested message dict.
This format is different from what is sent over the wire. The
@@ -415,13 +415,17 @@ class Session(Configurable):
msg = {}
header = self.msg_header(msg_type) if header is None else header
msg['header'] = header
+ if subheader is not None:
+ msg['header'].update(subheader)
msg['msg_id'] = header['msg_id']
msg['msg_type'] = header['msg_type']
msg['parent_header'] = {} if parent is None else extract_header(parent)
msg['content'] = {} if content is None else content
- msg['header'].update(self.subheader)
- if subheader is not None:
- msg['header'].update(subheader)
+ metadata_dict = self.metadata.copy()
+ if metadata is not None:
+ metadata_dict.update(metadata)
+ if metadata_dict:
+ msg['metadata'] = metadata_dict
return msg
def sign(self, msg_list):
@@ -496,7 +500,7 @@ class Session(Configurable):
return to_send
def send(self, stream, msg_or_type, content=None, parent=None, ident=None,
- buffers=None, subheader=None, track=False, header=None):
+ buffers=None, subheader=None, track=False, header=None, metadata=None):
"""Build and send a message via stream or socket.
The message format used by this function internally is as follows:
@@ -520,7 +524,7 @@ class Session(Configurable):
content : dict or None
The content of the message (ignored if msg_or_type is a message).
header : dict or None
- The header dict for the message (ignores if msg_to_type is a message).
+ The header dict for the message (ignored if msg_to_type is a message).
parent : Message or dict or None
The parent or parent header describing the parent of this message
(ignored if msg_or_type is a message).
@@ -529,11 +533,14 @@ class Session(Configurable):
subheader : dict or None
Extra header keys for this message's header (ignored if msg_or_type
is a message).
+ metadata : dict or None
+ The metadata describing the message
buffers : list or None
The already-serialized buffers to be appended to the message.
track : bool
Whether to track. Only for use with Sockets, because ZMQStream
objects cannot track messages.
+
Returns
-------
@@ -557,7 +564,7 @@ class Session(Configurable):
msg = msg_or_type
else:
msg = self.msg(msg_or_type, content=content, parent=parent,
- subheader=subheader, header=header)
+ subheader=subheader, header=header, metadata=metadata)
buffers = [] if buffers is None else buffers
to_send = self.serialize(msg, ident)
diff --git a/docs/source/development/messaging.txt b/docs/source/development/messaging.txt
index ab45c64..822ad66 100644
--- a/docs/source/development/messaging.txt
+++ b/docs/source/development/messaging.txt
@@ -81,7 +81,7 @@ representation of all the data, we can communicate with such clients.
General Message Format
======================
-A message is defined by the following three-dictionary structure::
+A message is defined by the following four-dictionary structure::
{
# The message header contains a pair of unique identifiers for the
@@ -105,6 +105,9 @@ A message is defined by the following three-dictionary structure::
# The actual content of the message must be a dict, whose structure
# depends on the message type.
'content' : dict,
+
+ # Any metadata associated with the message; this dictionary is optional.
+ 'metadata' : dict,
}
@@ -127,6 +130,7 @@ messages upon deserialization to the following form for convenience::
'msg_type' : str,
'parent_header' : dict,
'content' : dict,
+ 'metadata' : dict, # optional
}
All messages sent to or received by any IPython process should have this