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