##// END OF EJS Templates
Added top-level links.rst file to keep useful links in one place that can be included from other files.
Added top-level links.rst file to keep useful links in one place that can be included from other files.

File last commit:

r2727:106bc2e0
r2729:fe737a98
Show More
messaging.txt
284 lines | 8.2 KiB | text/plain | TextLexer
=====================
Message Specification
=====================
Note: not all of these have yet been fully fleshed out, but the key ones are,
see kernel and frontend files for actual implementation details.
Messages are dicts of dicts with string keys and values that are reasonably
representable in JSON. Our current implementation uses JSON explicitly as its
message format, but this shouldn't be considered a permanent feature. As we've
discovered that JSON has non-trivial performance issues due to excessive
copying, we may in the future move to a pure pickle-based raw message format.
However, it should be possible to easily convert from the raw objects to JSON,
since we may have non-python clients (e.g. a web frontend). As long as it's
easy to make a JSON version of the objects that is a faithful representation of
all the data, we can communicate with such clients.
Python functional API
=====================
As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
should develop, at a few key points, functional forms of all the requests that
take arguments in this manner and automatically construct the necessary dict
for sending.
General Message Format
=====================
General message format::
{
header : { 'msg_id' : 10, # start with 0
'username' : 'name',
'session' : uuid
},
parent_header : dict,
msg_type : 'string_message_type',
content : blackbox_dict , # Must be a dict
}
Request/Reply going from kernel for stdin
=========================================
This is a socket that goes in the opposite direction: from the kernel to a
*single* frontend, and its purpose is to allow ``raw_input`` and similar
operations that read from ``sys.stdin`` on the kernel to be fulfilled by the
client. For now we will keep these messages as simple as possible, since they
basically only mean to convey the ``raw_input(prompt)`` call.
Message type: 'input_request'::
content = { prompt : string }
Message type: 'input_reply'::
content = { value : string }
Side effect: (PUB/SUB)
======================
Message type: 'stream'::
content = {
name : 'stdout',
data : 'blob',
}
When a kernel receives a raw_input call, it should also broadcast it on the pub
socket with the names 'stdin' and 'stdin_reply'. This will allow other clients
to monitor/display kernel interactions and possibly replay them to their user
or otherwise expose them.
Message type: 'pyin'::
content = {
code = 'x=1',
}
Message type: 'pyout'::
content = {
data = 'repr(obj)',
prompt_number = 10
}
Message type: 'pyerr'::
content = {
# Same as the data payload of a code execute_reply, minus the 'status'
# field. See below.
}
When the kernel has an unexpected exception, caught by the last-resort
sys.excepthook, we should broadcast the crash handler's output before exiting.
This will allow clients to notice that a kernel died, inform the user and
propose further actions.
Message type: 'crash'::
content = {
traceback : 'full traceback',
exc_type : 'TypeError',
exc_value : 'msg'
}
Other potential message types, currently unimplemented, listed below as ideas.
Message type: 'file'::
content = {
path : 'cool.jpg',
mimetype : string
data : 'blob'
}
Request/Reply
=============
Execute
-------
The execution request contains a single string, but this may be a multiline
string. The kernel is responsible for splitting this into possibly more than
one block and deciding whether to compile these in 'single' or 'exec' mode.
We're still sorting out this policy. The current inputsplitter is capable of
splitting the input for blocks that can all be run as 'single', but in the long
run it may prove cleaner to only use 'single' mode for truly single-line
inputs, and run all multiline input in 'exec' mode. This would preserve the
natural behavior of single-line inputs while allowing long cells to behave more
likea a script. Some thought is still required here...
Message type: 'execute_request'::
content = {
code : 'a = 10',
}
Reply:
Message type: 'execute_reply'::
content = {
'status' : 'ok' OR 'error' OR 'abort'
# Any additional data depends on status value
}
When status is 'ok', the following extra fields are present::
{
# This has the same structure as the output of a prompt request, but is
# for the client to set up the *next* prompt (with identical limitations
# to a prompt request)
'next_prompt' : {
prompt_string : string
prompt_number : int
}
# The prompt number of the actual execution for this code, which may be
# different from the one used when the code was typed, which was the
# 'next_prompt' field of the *previous* request. They will differ in the
# case where there is more than one client talking simultaneously to a
# kernel, since the numbers can go out of sync. GUI clients can use this
# to correct the previously written number in-place, terminal ones may
# re-print a corrected one if desired.
'prompt_number' : number
# The kernel will often transform the input provided to it. This
# contains the transformed code, which is what was actually executed.
'transformed_code' : new_code
# This 'payload' needs a bit more thinking. The basic idea is that
# certain actions will want to return additional information, such as
# magics producing data output for display by the clients. We may need
# to define a few types of payload, or specify a syntax for the, not sure
# yet... FIXME here.
'payload' : things from page(), for example.
}
When status is 'error', the following extra fields are present::
{
etype : str # Exception type, as a string
evalue : str # Exception value, as a string
# The traceback will contain a list of frames, represented each as a
# string. For now we'll stick to the existing design of ultraTB, which
# controls exception level of detail statefully. But eventually we'll
# want to grow into a model where more information is collected and
# packed into the traceback object, with clients deciding how little or
# how much of it to unpack. But for now, let's start with a simple list
# of strings, since that requires only minimal changes to ultratb as
# written.
traceback : list of strings
}
When status is 'abort', there are for now no additional data fields.
Prompt
------
A simple request for a current prompt string.
Message type: 'prompt_request'::
content = {}
In the reply, the prompt string comes back with the prompt number placeholder
*unevaluated*. The message format is:
Message type: 'prompt_reply'::
content = {
prompt_string : string
prompt_number : int
}
Clients can produce a prompt with ``prompt_string.format(prompt_number)``, but
they should be aware that the actual prompt number for that input could change
later, in the case where multiple clients are interacting with a single
kernel.
Complete
--------
Message type: 'complete_request'::
content = {
text : 'a.f', # complete on this
line : 'print a.f' # full line
}
Message type: 'complete_reply'::
content = {
matches : ['a.foo', 'a.bar']
}
History
-------
For clients to explicitly request history from a kernel
Message type: 'history_request'::
content = {
output : boolean. If true, also return output history in the resulting
dict.
range : optional. A number, a pair of numbers, 'all'
If not given, last 40 are returned.
- number n: return the last n entries.
- pair n1, n2: return entries in the range(n1, n2).
- 'all': return all history
filter : optional, string
If given, treated as a regular expression and only matching entries are
returned. re.search() is used to find matches.
}
Message type: 'history_reply'::
content = {
input : list of pairs (number, input)
output : list of pairs (number, output). Empty if not requested.
}
Control
-------
Message type: 'heartbeat'::
content = {
# XXX - unfinished
}