##// END OF EJS Templates
Merge pull request #3186 from maximsch2/fix_etype_ename...
Min RK -
r10271:9456b7ca merge
parent child Browse files
Show More
@@ -1,209 +1,209 b''
1 1 """The basic dict based notebook format.
2 2
3 3 The Python representation of a notebook is a nested structure of
4 4 dictionary subclasses that support attribute access
5 5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
6 6 helpers to build the structs in the right form.
7 7
8 8 Authors:
9 9
10 10 * Brian Granger
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Copyright (C) 2008-2011 The IPython Development Team
15 15 #
16 16 # Distributed under the terms of the BSD License. The full license is in
17 17 # the file COPYING, distributed as part of this software.
18 18 #-----------------------------------------------------------------------------
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Imports
22 22 #-----------------------------------------------------------------------------
23 23
24 24 import pprint
25 25 import uuid
26 26
27 27 from IPython.utils.ipstruct import Struct
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Code
31 31 #-----------------------------------------------------------------------------
32 32
33 33 # Change this when incrementing the nbformat version
34 34 nbformat = 3
35 35 nbformat_minor = 0
36 36
37 37 class NotebookNode(Struct):
38 38 pass
39 39
40 40
41 41 def from_dict(d):
42 42 if isinstance(d, dict):
43 43 newd = NotebookNode()
44 44 for k,v in d.items():
45 45 newd[k] = from_dict(v)
46 46 return newd
47 47 elif isinstance(d, (tuple, list)):
48 48 return [from_dict(i) for i in d]
49 49 else:
50 50 return d
51 51
52 52
53 53 def new_output(output_type=None, output_text=None, output_png=None,
54 54 output_html=None, output_svg=None, output_latex=None, output_json=None,
55 55 output_javascript=None, output_jpeg=None, prompt_number=None,
56 etype=None, evalue=None, traceback=None, stream=None):
56 ename=None, evalue=None, traceback=None, stream=None):
57 57 """Create a new code cell with input and output"""
58 58 output = NotebookNode()
59 59 if output_type is not None:
60 60 output.output_type = unicode(output_type)
61 61
62 62 if output_type != 'pyerr':
63 63 if output_text is not None:
64 64 output.text = unicode(output_text)
65 65 if output_png is not None:
66 66 output.png = bytes(output_png)
67 67 if output_jpeg is not None:
68 68 output.jpeg = bytes(output_jpeg)
69 69 if output_html is not None:
70 70 output.html = unicode(output_html)
71 71 if output_svg is not None:
72 72 output.svg = unicode(output_svg)
73 73 if output_latex is not None:
74 74 output.latex = unicode(output_latex)
75 75 if output_json is not None:
76 76 output.json = unicode(output_json)
77 77 if output_javascript is not None:
78 78 output.javascript = unicode(output_javascript)
79 79
80 80 if output_type == u'pyout':
81 81 if prompt_number is not None:
82 82 output.prompt_number = int(prompt_number)
83 83
84 84 if output_type == u'pyerr':
85 if etype is not None:
86 output.etype = unicode(etype)
85 if ename is not None:
86 output.ename = unicode(ename)
87 87 if evalue is not None:
88 88 output.evalue = unicode(evalue)
89 89 if traceback is not None:
90 90 output.traceback = [unicode(frame) for frame in list(traceback)]
91 91
92 92 if output_type == u'stream':
93 93 output.stream = 'stdout' if stream is None else unicode(stream)
94 94
95 95 return output
96 96
97 97
98 98 def new_code_cell(input=None, prompt_number=None, outputs=None,
99 99 language=u'python', collapsed=False, metadata=None):
100 100 """Create a new code cell with input and output"""
101 101 cell = NotebookNode()
102 102 cell.cell_type = u'code'
103 103 if language is not None:
104 104 cell.language = unicode(language)
105 105 if input is not None:
106 106 cell.input = unicode(input)
107 107 if prompt_number is not None:
108 108 cell.prompt_number = int(prompt_number)
109 109 if outputs is None:
110 110 cell.outputs = []
111 111 else:
112 112 cell.outputs = outputs
113 113 if collapsed is not None:
114 114 cell.collapsed = bool(collapsed)
115 115 cell.metadata = NotebookNode(metadata or {})
116 116
117 117 return cell
118 118
119 119 def new_text_cell(cell_type, source=None, rendered=None, metadata=None):
120 120 """Create a new text cell."""
121 121 cell = NotebookNode()
122 122 # VERSIONHACK: plaintext -> raw
123 123 # handle never-released plaintext name for raw cells
124 124 if cell_type == 'plaintext':
125 125 cell_type = 'raw'
126 126 if source is not None:
127 127 cell.source = unicode(source)
128 128 if rendered is not None:
129 129 cell.rendered = unicode(rendered)
130 130 cell.metadata = NotebookNode(metadata or {})
131 131 cell.cell_type = cell_type
132 132 return cell
133 133
134 134
135 135 def new_heading_cell(source=None, rendered=None, level=1, metadata=None):
136 136 """Create a new section cell with a given integer level."""
137 137 cell = NotebookNode()
138 138 cell.cell_type = u'heading'
139 139 if source is not None:
140 140 cell.source = unicode(source)
141 141 if rendered is not None:
142 142 cell.rendered = unicode(rendered)
143 143 cell.level = int(level)
144 144 cell.metadata = NotebookNode(metadata or {})
145 145 return cell
146 146
147 147
148 148 def new_worksheet(name=None, cells=None, metadata=None):
149 149 """Create a worksheet by name with with a list of cells."""
150 150 ws = NotebookNode()
151 151 if name is not None:
152 152 ws.name = unicode(name)
153 153 if cells is None:
154 154 ws.cells = []
155 155 else:
156 156 ws.cells = list(cells)
157 157 ws.metadata = NotebookNode(metadata or {})
158 158 return ws
159 159
160 160
161 161 def new_notebook(name=None, metadata=None, worksheets=None):
162 162 """Create a notebook by name, id and a list of worksheets."""
163 163 nb = NotebookNode()
164 164 nb.nbformat = nbformat
165 165 nb.nbformat_minor = nbformat_minor
166 166 if worksheets is None:
167 167 nb.worksheets = []
168 168 else:
169 169 nb.worksheets = list(worksheets)
170 170 if metadata is None:
171 171 nb.metadata = new_metadata()
172 172 else:
173 173 nb.metadata = NotebookNode(metadata)
174 174 if name is not None:
175 175 nb.metadata.name = unicode(name)
176 176 return nb
177 177
178 178
179 179 def new_metadata(name=None, authors=None, license=None, created=None,
180 180 modified=None, gistid=None):
181 181 """Create a new metadata node."""
182 182 metadata = NotebookNode()
183 183 if name is not None:
184 184 metadata.name = unicode(name)
185 185 if authors is not None:
186 186 metadata.authors = list(authors)
187 187 if created is not None:
188 188 metadata.created = unicode(created)
189 189 if modified is not None:
190 190 metadata.modified = unicode(modified)
191 191 if license is not None:
192 192 metadata.license = unicode(license)
193 193 if gistid is not None:
194 194 metadata.gistid = unicode(gistid)
195 195 return metadata
196 196
197 197 def new_author(name=None, email=None, affiliation=None, url=None):
198 198 """Create a new author."""
199 199 author = NotebookNode()
200 200 if name is not None:
201 201 author.name = unicode(name)
202 202 if email is not None:
203 203 author.email = unicode(email)
204 204 if affiliation is not None:
205 205 author.affiliation = unicode(affiliation)
206 206 if url is not None:
207 207 author.url = unicode(url)
208 208 return author
209 209
@@ -1,154 +1,154 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 import os
4 4 from base64 import encodestring
5 5
6 6 from ..nbbase import (
7 7 NotebookNode,
8 8 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output,
9 9 new_metadata, new_author, new_heading_cell, nbformat, nbformat_minor
10 10 )
11 11
12 12 # some random base64-encoded *bytes*
13 13 png = encodestring(os.urandom(5))
14 14 jpeg = encodestring(os.urandom(6))
15 15
16 16 ws = new_worksheet(name='worksheet1')
17 17
18 18 ws.cells.append(new_text_cell(
19 19 u'html',
20 20 source='Some NumPy Examples',
21 21 rendered='Some NumPy Examples'
22 22 ))
23 23
24 24
25 25 ws.cells.append(new_code_cell(
26 26 input='import numpy',
27 27 prompt_number=1,
28 28 collapsed=False
29 29 ))
30 30
31 31 ws.cells.append(new_text_cell(
32 32 u'markdown',
33 33 source='A random array',
34 34 rendered='A random array'
35 35 ))
36 36
37 37 ws.cells.append(new_text_cell(
38 38 u'raw',
39 39 source='A random array',
40 40 ))
41 41
42 42 ws.cells.append(new_heading_cell(
43 43 u'My Heading',
44 44 level=2
45 45 ))
46 46
47 47 ws.cells.append(new_code_cell(
48 48 input='a = numpy.random.rand(100)',
49 49 prompt_number=2,
50 50 collapsed=True
51 51 ))
52 52 ws.cells.append(new_code_cell(
53 53 input='a = 10\nb = 5\n',
54 54 prompt_number=3,
55 55 ))
56 56 ws.cells.append(new_code_cell(
57 57 input='a = 10\nb = 5',
58 58 prompt_number=4,
59 59 ))
60 60
61 61 ws.cells.append(new_code_cell(
62 62 input=u'print "ünîcødé"',
63 63 prompt_number=3,
64 64 collapsed=False,
65 65 outputs=[new_output(
66 66 output_type=u'pyout',
67 67 output_text=u'<array a>',
68 68 output_html=u'The HTML rep',
69 69 output_latex=u'$a$',
70 70 output_png=png,
71 71 output_jpeg=jpeg,
72 72 output_svg=u'<svg>',
73 73 output_json=u'json data',
74 74 output_javascript=u'var i=0;',
75 75 prompt_number=3
76 76 ),new_output(
77 77 output_type=u'display_data',
78 78 output_text=u'<array a>',
79 79 output_html=u'The HTML rep',
80 80 output_latex=u'$a$',
81 81 output_png=png,
82 82 output_jpeg=jpeg,
83 83 output_svg=u'<svg>',
84 84 output_json=u'json data',
85 85 output_javascript=u'var i=0;'
86 86 ),new_output(
87 87 output_type=u'pyerr',
88 etype=u'NameError',
88 ename=u'NameError',
89 89 evalue=u'NameError was here',
90 90 traceback=[u'frame 0', u'frame 1', u'frame 2']
91 91 ),new_output(
92 92 output_type=u'stream',
93 93 output_text='foo\rbar\r\n'
94 94 ),new_output(
95 95 output_type=u'stream',
96 96 stream='stderr',
97 97 output_text='\rfoo\rbar\n'
98 98 )]
99 99 ))
100 100
101 101 authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com',
102 102 affiliation=u'Fox',url=u'http://www.fox.com')]
103 103 md = new_metadata(name=u'My Notebook',license=u'BSD',created=u'8601_goes_here',
104 104 modified=u'8601_goes_here',gistid=u'21341231',authors=authors)
105 105
106 106 nb0 = new_notebook(
107 107 worksheets=[ws, new_worksheet(name='worksheet2')],
108 108 metadata=md
109 109 )
110 110
111 111 nb0_py = u"""# -*- coding: utf-8 -*-
112 112 # <nbformat>%i.%i</nbformat>
113 113
114 114 # <htmlcell>
115 115
116 116 # Some NumPy Examples
117 117
118 118 # <codecell>
119 119
120 120 import numpy
121 121
122 122 # <markdowncell>
123 123
124 124 # A random array
125 125
126 126 # <rawcell>
127 127
128 128 # A random array
129 129
130 130 # <headingcell level=2>
131 131
132 132 # My Heading
133 133
134 134 # <codecell>
135 135
136 136 a = numpy.random.rand(100)
137 137
138 138 # <codecell>
139 139
140 140 a = 10
141 141 b = 5
142 142
143 143 # <codecell>
144 144
145 145 a = 10
146 146 b = 5
147 147
148 148 # <codecell>
149 149
150 150 print "ünîcødé"
151 151
152 152 """ % (nbformat, nbformat_minor)
153 153
154 154
@@ -1,143 +1,143 b''
1 1 from unittest import TestCase
2 2
3 3 from ..nbbase import (
4 4 NotebookNode,
5 5 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output,
6 6 new_author, new_metadata, new_heading_cell, nbformat
7 7 )
8 8
9 9 class TestCell(TestCase):
10 10
11 11 def test_empty_code_cell(self):
12 12 cc = new_code_cell()
13 13 self.assertEqual(cc.cell_type,u'code')
14 14 self.assertEqual(u'input' not in cc, True)
15 15 self.assertEqual(u'prompt_number' not in cc, True)
16 16 self.assertEqual(cc.outputs, [])
17 17 self.assertEqual(cc.collapsed, False)
18 18
19 19 def test_code_cell(self):
20 20 cc = new_code_cell(input='a=10', prompt_number=0, collapsed=True)
21 21 cc.outputs = [new_output(output_type=u'pyout',
22 22 output_svg=u'foo',output_text=u'10',prompt_number=0)]
23 23 self.assertEqual(cc.input, u'a=10')
24 24 self.assertEqual(cc.prompt_number, 0)
25 25 self.assertEqual(cc.language, u'python')
26 26 self.assertEqual(cc.outputs[0].svg, u'foo')
27 27 self.assertEqual(cc.outputs[0].text, u'10')
28 28 self.assertEqual(cc.outputs[0].prompt_number, 0)
29 29 self.assertEqual(cc.collapsed, True)
30 30
31 31 def test_pyerr(self):
32 o = new_output(output_type=u'pyerr', etype=u'NameError',
32 o = new_output(output_type=u'pyerr', ename=u'NameError',
33 33 evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2']
34 34 )
35 35 self.assertEqual(o.output_type, u'pyerr')
36 self.assertEqual(o.etype, u'NameError')
36 self.assertEqual(o.ename, u'NameError')
37 37 self.assertEqual(o.evalue, u'Name not found')
38 38 self.assertEqual(o.traceback, [u'frame 0', u'frame 1', u'frame 2'])
39 39
40 40 def test_empty_html_cell(self):
41 41 tc = new_text_cell(u'html')
42 42 self.assertEqual(tc.cell_type, u'html')
43 43 self.assertEqual(u'source' not in tc, True)
44 44 self.assertEqual(u'rendered' not in tc, True)
45 45
46 46 def test_html_cell(self):
47 47 tc = new_text_cell(u'html', 'hi', 'hi')
48 48 self.assertEqual(tc.source, u'hi')
49 49 self.assertEqual(tc.rendered, u'hi')
50 50
51 51 def test_empty_markdown_cell(self):
52 52 tc = new_text_cell(u'markdown')
53 53 self.assertEqual(tc.cell_type, u'markdown')
54 54 self.assertEqual(u'source' not in tc, True)
55 55 self.assertEqual(u'rendered' not in tc, True)
56 56
57 57 def test_markdown_cell(self):
58 58 tc = new_text_cell(u'markdown', 'hi', 'hi')
59 59 self.assertEqual(tc.source, u'hi')
60 60 self.assertEqual(tc.rendered, u'hi')
61 61
62 62 def test_empty_raw_cell(self):
63 63 tc = new_text_cell(u'raw')
64 64 self.assertEqual(tc.cell_type, u'raw')
65 65 self.assertEqual(u'source' not in tc, True)
66 66 self.assertEqual(u'rendered' not in tc, True)
67 67
68 68 def test_raw_cell(self):
69 69 tc = new_text_cell(u'raw', 'hi', 'hi')
70 70 self.assertEqual(tc.source, u'hi')
71 71 self.assertEqual(tc.rendered, u'hi')
72 72
73 73 def test_empty_heading_cell(self):
74 74 tc = new_heading_cell()
75 75 self.assertEqual(tc.cell_type, u'heading')
76 76 self.assertEqual(u'source' not in tc, True)
77 77 self.assertEqual(u'rendered' not in tc, True)
78 78
79 79 def test_heading_cell(self):
80 80 tc = new_heading_cell(u'hi', u'hi', level=2)
81 81 self.assertEqual(tc.source, u'hi')
82 82 self.assertEqual(tc.rendered, u'hi')
83 83 self.assertEqual(tc.level, 2)
84 84
85 85
86 86 class TestWorksheet(TestCase):
87 87
88 88 def test_empty_worksheet(self):
89 89 ws = new_worksheet()
90 90 self.assertEqual(ws.cells,[])
91 91 self.assertEqual(u'name' not in ws, True)
92 92
93 93 def test_worksheet(self):
94 94 cells = [new_code_cell(), new_text_cell(u'html')]
95 95 ws = new_worksheet(cells=cells,name=u'foo')
96 96 self.assertEqual(ws.cells,cells)
97 97 self.assertEqual(ws.name,u'foo')
98 98
99 99 class TestNotebook(TestCase):
100 100
101 101 def test_empty_notebook(self):
102 102 nb = new_notebook()
103 103 self.assertEqual(nb.worksheets, [])
104 104 self.assertEqual(nb.metadata, NotebookNode())
105 105 self.assertEqual(nb.nbformat,nbformat)
106 106
107 107 def test_notebook(self):
108 108 worksheets = [new_worksheet(),new_worksheet()]
109 109 metadata = new_metadata(name=u'foo')
110 110 nb = new_notebook(metadata=metadata,worksheets=worksheets)
111 111 self.assertEqual(nb.metadata.name,u'foo')
112 112 self.assertEqual(nb.worksheets,worksheets)
113 113 self.assertEqual(nb.nbformat,nbformat)
114 114
115 115 def test_notebook_name(self):
116 116 worksheets = [new_worksheet(),new_worksheet()]
117 117 nb = new_notebook(name='foo',worksheets=worksheets)
118 118 self.assertEqual(nb.metadata.name,u'foo')
119 119 self.assertEqual(nb.worksheets,worksheets)
120 120 self.assertEqual(nb.nbformat,nbformat)
121 121
122 122 class TestMetadata(TestCase):
123 123
124 124 def test_empty_metadata(self):
125 125 md = new_metadata()
126 126 self.assertEqual(u'name' not in md, True)
127 127 self.assertEqual(u'authors' not in md, True)
128 128 self.assertEqual(u'license' not in md, True)
129 129 self.assertEqual(u'saved' not in md, True)
130 130 self.assertEqual(u'modified' not in md, True)
131 131 self.assertEqual(u'gistid' not in md, True)
132 132
133 133 def test_metadata(self):
134 134 authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com')]
135 135 md = new_metadata(name=u'foo',license=u'BSD',created=u'today',
136 136 modified=u'now',gistid=u'21341231',authors=authors)
137 137 self.assertEqual(md.name, u'foo')
138 138 self.assertEqual(md.license, u'BSD')
139 139 self.assertEqual(md.created, u'today')
140 140 self.assertEqual(md.modified, u'now')
141 141 self.assertEqual(md.gistid, u'21341231')
142 142 self.assertEqual(md.authors, authors)
143 143
@@ -1,1050 +1,1050 b''
1 1 .. _messaging:
2 2
3 3 ======================
4 4 Messaging in IPython
5 5 ======================
6 6
7 7
8 8 Introduction
9 9 ============
10 10
11 11 This document explains the basic communications design and messaging
12 12 specification for how the various IPython objects interact over a network
13 13 transport. The current implementation uses the ZeroMQ_ library for messaging
14 14 within and between hosts.
15 15
16 16 .. Note::
17 17
18 18 This document should be considered the authoritative description of the
19 19 IPython messaging protocol, and all developers are strongly encouraged to
20 20 keep it updated as the implementation evolves, so that we have a single
21 21 common reference for all protocol details.
22 22
23 23 The basic design is explained in the following diagram:
24 24
25 25 .. image:: figs/frontend-kernel.png
26 26 :width: 450px
27 27 :alt: IPython kernel/frontend messaging architecture.
28 28 :align: center
29 29 :target: ../_images/frontend-kernel.png
30 30
31 31 A single kernel can be simultaneously connected to one or more frontends. The
32 32 kernel has three sockets that serve the following functions:
33 33
34 34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
35 35 the kernel to request input from the active frontend when :func:`raw_input` is called.
36 36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
37 37 for the kernel while this communication is happening (illustrated in the
38 38 figure by the black outline around the central keyboard). In practice,
39 39 frontends may display such kernel requests using a special input widget or
40 40 otherwise indicating that the user is to type input for the kernel instead
41 41 of normal commands in the frontend.
42 42
43 43 2. Shell: this single ROUTER socket allows multiple incoming connections from
44 44 frontends, and this is the socket where requests for code execution, object
45 45 information, prompts, etc. are made to the kernel by any frontend. The
46 46 communication on this socket is a sequence of request/reply actions from
47 47 each frontend and the kernel.
48 48
49 49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
50 50 side effects (stdout, stderr, etc.) as well as the requests coming from any
51 51 client over the shell socket and its own requests on the stdin socket. There
52 52 are a number of actions in Python which generate side effects: :func:`print`
53 53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
54 54 a multi-client scenario, we want all frontends to be able to know what each
55 55 other has sent to the kernel (this can be useful in collaborative scenarios,
56 56 for example). This socket allows both side effects and the information
57 57 about communications taking place with one client over the shell channel
58 58 to be made available to all clients in a uniform manner.
59 59
60 60 All messages are tagged with enough information (details below) for clients
61 61 to know which messages come from their own interaction with the kernel and
62 62 which ones are from other clients, so they can display each type
63 63 appropriately.
64 64
65 65 The actual format of the messages allowed on each of these channels is
66 66 specified below. Messages are dicts of dicts with string keys and values that
67 67 are reasonably representable in JSON. Our current implementation uses JSON
68 68 explicitly as its message format, but this shouldn't be considered a permanent
69 69 feature. As we've discovered that JSON has non-trivial performance issues due
70 70 to excessive copying, we may in the future move to a pure pickle-based raw
71 71 message format. However, it should be possible to easily convert from the raw
72 72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
73 73 As long as it's easy to make a JSON version of the objects that is a faithful
74 74 representation of all the data, we can communicate with such clients.
75 75
76 76 .. Note::
77 77
78 78 Not all of these have yet been fully fleshed out, but the key ones are, see
79 79 kernel and frontend files for actual implementation details.
80 80
81 81 General Message Format
82 82 ======================
83 83
84 84 A message is defined by the following four-dictionary structure::
85 85
86 86 {
87 87 # The message header contains a pair of unique identifiers for the
88 88 # originating session and the actual message id, in addition to the
89 89 # username for the process that generated the message. This is useful in
90 90 # collaborative settings where multiple users may be interacting with the
91 91 # same kernel simultaneously, so that frontends can label the various
92 92 # messages in a meaningful way.
93 93 'header' : {
94 94 'msg_id' : uuid,
95 95 'username' : str,
96 96 'session' : uuid
97 97 # All recognized message type strings are listed below.
98 98 'msg_type' : str,
99 99 },
100 100
101 101 # In a chain of messages, the header from the parent is copied so that
102 102 # clients can track where messages come from.
103 103 'parent_header' : dict,
104 104
105 105 # The actual content of the message must be a dict, whose structure
106 106 # depends on the message type.
107 107 'content' : dict,
108 108
109 109 # Any metadata associated with the message.
110 110 'metadata' : dict,
111 111 }
112 112
113 113
114 114 Python functional API
115 115 =====================
116 116
117 117 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
118 118 should develop, at a few key points, functional forms of all the requests that
119 119 take arguments in this manner and automatically construct the necessary dict
120 120 for sending.
121 121
122 122 In addition, the Python implementation of the message specification extends
123 123 messages upon deserialization to the following form for convenience::
124 124
125 125 {
126 126 'header' : dict,
127 127 # The msg's unique identifier and type are always stored in the header,
128 128 # but the Python implementation copies them to the top level.
129 129 'msg_id' : uuid,
130 130 'msg_type' : str,
131 131 'parent_header' : dict,
132 132 'content' : dict,
133 133 'metadata' : dict,
134 134 }
135 135
136 136 All messages sent to or received by any IPython process should have this
137 137 extended structure.
138 138
139 139
140 140 Messages on the shell ROUTER/DEALER sockets
141 141 ===========================================
142 142
143 143 .. _execute:
144 144
145 145 Execute
146 146 -------
147 147
148 148 This message type is used by frontends to ask the kernel to execute code on
149 149 behalf of the user, in a namespace reserved to the user's variables (and thus
150 150 separate from the kernel's own internal code and variables).
151 151
152 152 Message type: ``execute_request``::
153 153
154 154 content = {
155 155 # Source code to be executed by the kernel, one or more lines.
156 156 'code' : str,
157 157
158 158 # A boolean flag which, if True, signals the kernel to execute
159 159 # this code as quietly as possible. This means that the kernel
160 160 # will compile the code with 'exec' instead of 'single' (so
161 161 # sys.displayhook will not fire), forces store_history to be False,
162 162 # and will *not*:
163 163 # - broadcast exceptions on the PUB socket
164 164 # - do any logging
165 165 #
166 166 # The default is False.
167 167 'silent' : bool,
168 168
169 169 # A boolean flag which, if True, signals the kernel to populate history
170 170 # The default is True if silent is False. If silent is True, store_history
171 171 # is forced to be False.
172 172 'store_history' : bool,
173 173
174 174 # A list of variable names from the user's namespace to be retrieved. What
175 175 # returns is a JSON string of the variable's repr(), not a python object.
176 176 'user_variables' : list,
177 177
178 178 # Similarly, a dict mapping names to expressions to be evaluated in the
179 179 # user's dict.
180 180 'user_expressions' : dict,
181 181
182 182 # Some frontends (e.g. the Notebook) do not support stdin requests. If
183 183 # raw_input is called from code executed from such a frontend, a
184 184 # StdinNotImplementedError will be raised.
185 185 'allow_stdin' : True,
186 186
187 187 }
188 188
189 189 The ``code`` field contains a single string (possibly multiline). The kernel
190 190 is responsible for splitting this into one or more independent execution blocks
191 191 and deciding whether to compile these in 'single' or 'exec' mode (see below for
192 192 detailed execution semantics).
193 193
194 194 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
195 195 the notion of a prompt string that allowed arbitrary code to be evaluated, and
196 196 this was put to good use by many in creating prompts that displayed system
197 197 status, path information, and even more esoteric uses like remote instrument
198 198 status aqcuired over the network. But now that IPython has a clean separation
199 199 between the kernel and the clients, the kernel has no prompt knowledge; prompts
200 200 are a frontend-side feature, and it should be even possible for different
201 201 frontends to display different prompts while interacting with the same kernel.
202 202
203 203 The kernel now provides the ability to retrieve data from the user's namespace
204 204 after the execution of the main ``code``, thanks to two fields in the
205 205 ``execute_request`` message:
206 206
207 207 - ``user_variables``: If only variables from the user's namespace are needed, a
208 208 list of variable names can be passed and a dict with these names as keys and
209 209 their :func:`repr()` as values will be returned.
210 210
211 211 - ``user_expressions``: For more complex expressions that require function
212 212 evaluations, a dict can be provided with string keys and arbitrary python
213 213 expressions as values. The return message will contain also a dict with the
214 214 same keys and the :func:`repr()` of the evaluated expressions as value.
215 215
216 216 With this information, frontends can display any status information they wish
217 217 in the form that best suits each frontend (a status line, a popup, inline for a
218 218 terminal, etc).
219 219
220 220 .. Note::
221 221
222 222 In order to obtain the current execution counter for the purposes of
223 223 displaying input prompts, frontends simply make an execution request with an
224 224 empty code string and ``silent=True``.
225 225
226 226 Execution semantics
227 227 ~~~~~~~~~~~~~~~~~~~
228 228
229 229 When the silent flag is false, the execution of use code consists of the
230 230 following phases (in silent mode, only the ``code`` field is executed):
231 231
232 232 1. Run the ``pre_runcode_hook``.
233 233
234 234 2. Execute the ``code`` field, see below for details.
235 235
236 236 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
237 237 computed. This ensures that any error in the latter don't harm the main
238 238 code execution.
239 239
240 240 4. Call any method registered with :meth:`register_post_execute`.
241 241
242 242 .. warning::
243 243
244 244 The API for running code before/after the main code block is likely to
245 245 change soon. Both the ``pre_runcode_hook`` and the
246 246 :meth:`register_post_execute` are susceptible to modification, as we find a
247 247 consistent model for both.
248 248
249 249 To understand how the ``code`` field is executed, one must know that Python
250 250 code can be compiled in one of three modes (controlled by the ``mode`` argument
251 251 to the :func:`compile` builtin):
252 252
253 253 *single*
254 254 Valid for a single interactive statement (though the source can contain
255 255 multiple lines, such as a for loop). When compiled in this mode, the
256 256 generated bytecode contains special instructions that trigger the calling of
257 257 :func:`sys.displayhook` for any expression in the block that returns a value.
258 258 This means that a single statement can actually produce multiple calls to
259 259 :func:`sys.displayhook`, if for example it contains a loop where each
260 260 iteration computes an unassigned expression would generate 10 calls::
261 261
262 262 for i in range(10):
263 263 i**2
264 264
265 265 *exec*
266 266 An arbitrary amount of source code, this is how modules are compiled.
267 267 :func:`sys.displayhook` is *never* implicitly called.
268 268
269 269 *eval*
270 270 A single expression that returns a value. :func:`sys.displayhook` is *never*
271 271 implicitly called.
272 272
273 273
274 274 The ``code`` field is split into individual blocks each of which is valid for
275 275 execution in 'single' mode, and then:
276 276
277 277 - If there is only a single block: it is executed in 'single' mode.
278 278
279 279 - If there is more than one block:
280 280
281 281 * if the last one is a single line long, run all but the last in 'exec' mode
282 282 and the very last one in 'single' mode. This makes it easy to type simple
283 283 expressions at the end to see computed values.
284 284
285 285 * if the last one is no more than two lines long, run all but the last in
286 286 'exec' mode and the very last one in 'single' mode. This makes it easy to
287 287 type simple expressions at the end to see computed values. - otherwise
288 288 (last one is also multiline), run all in 'exec' mode
289 289
290 290 * otherwise (last one is also multiline), run all in 'exec' mode as a single
291 291 unit.
292 292
293 293 Any error in retrieving the ``user_variables`` or evaluating the
294 294 ``user_expressions`` will result in a simple error message in the return fields
295 295 of the form::
296 296
297 297 [ERROR] ExceptionType: Exception message
298 298
299 299 The user can simply send the same variable name or expression for evaluation to
300 300 see a regular traceback.
301 301
302 302 Errors in any registered post_execute functions are also reported similarly,
303 303 and the failing function is removed from the post_execution set so that it does
304 304 not continue triggering failures.
305 305
306 306 Upon completion of the execution request, the kernel *always* sends a reply,
307 307 with a status code indicating what happened and additional data depending on
308 308 the outcome. See :ref:`below <execution_results>` for the possible return
309 309 codes and associated data.
310 310
311 311
312 312 Execution counter (old prompt number)
313 313 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
314 314
315 315 The kernel has a single, monotonically increasing counter of all execution
316 316 requests that are made with ``store_history=True``. This counter is used to populate
317 317 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
318 318 display it in some form to the user, which will typically (but not necessarily)
319 319 be done in the prompts. The value of this counter will be returned as the
320 320 ``execution_count`` field of all ``execute_reply`` messages.
321 321
322 322 .. _execution_results:
323 323
324 324 Execution results
325 325 ~~~~~~~~~~~~~~~~~
326 326
327 327 Message type: ``execute_reply``::
328 328
329 329 content = {
330 330 # One of: 'ok' OR 'error' OR 'abort'
331 331 'status' : str,
332 332
333 333 # The global kernel counter that increases by one with each request that
334 334 # stores history. This will typically be used by clients to display
335 335 # prompt numbers to the user. If the request did not store history, this will
336 336 # be the current value of the counter in the kernel.
337 337 'execution_count' : int,
338 338 }
339 339
340 340 When status is 'ok', the following extra fields are present::
341 341
342 342 {
343 343 # 'payload' will be a list of payload dicts.
344 344 # Each execution payload is a dict with string keys that may have been
345 345 # produced by the code being executed. It is retrieved by the kernel at
346 346 # the end of the execution and sent back to the front end, which can take
347 347 # action on it as needed. See main text for further details.
348 348 'payload' : list(dict),
349 349
350 350 # Results for the user_variables and user_expressions.
351 351 'user_variables' : dict,
352 352 'user_expressions' : dict,
353 353 }
354 354
355 355 .. admonition:: Execution payloads
356 356
357 357 The notion of an 'execution payload' is different from a return value of a
358 358 given set of code, which normally is just displayed on the pyout stream
359 359 through the PUB socket. The idea of a payload is to allow special types of
360 360 code, typically magics, to populate a data container in the IPython kernel
361 361 that will be shipped back to the caller via this channel. The kernel
362 362 has an API for this in the PayloadManager::
363 363
364 364 ip.payload_manager.write_payload(payload_dict)
365 365
366 366 which appends a dictionary to the list of payloads.
367 367
368 368
369 369 When status is 'error', the following extra fields are present::
370 370
371 371 {
372 372 'ename' : str, # Exception name, as a string
373 373 'evalue' : str, # Exception value, as a string
374 374
375 375 # The traceback will contain a list of frames, represented each as a
376 376 # string. For now we'll stick to the existing design of ultraTB, which
377 377 # controls exception level of detail statefully. But eventually we'll
378 378 # want to grow into a model where more information is collected and
379 379 # packed into the traceback object, with clients deciding how little or
380 380 # how much of it to unpack. But for now, let's start with a simple list
381 381 # of strings, since that requires only minimal changes to ultratb as
382 382 # written.
383 383 'traceback' : list,
384 384 }
385 385
386 386
387 387 When status is 'abort', there are for now no additional data fields. This
388 388 happens when the kernel was interrupted by a signal.
389 389
390 390 Kernel attribute access
391 391 -----------------------
392 392
393 393 .. warning::
394 394
395 395 This part of the messaging spec is not actually implemented in the kernel
396 396 yet.
397 397
398 398 While this protocol does not specify full RPC access to arbitrary methods of
399 399 the kernel object, the kernel does allow read (and in some cases write) access
400 400 to certain attributes.
401 401
402 402 The policy for which attributes can be read is: any attribute of the kernel, or
403 403 its sub-objects, that belongs to a :class:`Configurable` object and has been
404 404 declared at the class-level with Traits validation, is in principle accessible
405 405 as long as its name does not begin with a leading underscore. The attribute
406 406 itself will have metadata indicating whether it allows remote read and/or write
407 407 access. The message spec follows for attribute read and write requests.
408 408
409 409 Message type: ``getattr_request``::
410 410
411 411 content = {
412 412 # The (possibly dotted) name of the attribute
413 413 'name' : str,
414 414 }
415 415
416 416 When a ``getattr_request`` fails, there are two possible error types:
417 417
418 418 - AttributeError: this type of error was raised when trying to access the
419 419 given name by the kernel itself. This means that the attribute likely
420 420 doesn't exist.
421 421
422 422 - AccessError: the attribute exists but its value is not readable remotely.
423 423
424 424
425 425 Message type: ``getattr_reply``::
426 426
427 427 content = {
428 428 # One of ['ok', 'AttributeError', 'AccessError'].
429 429 'status' : str,
430 430 # If status is 'ok', a JSON object.
431 431 'value' : object,
432 432 }
433 433
434 434 Message type: ``setattr_request``::
435 435
436 436 content = {
437 437 # The (possibly dotted) name of the attribute
438 438 'name' : str,
439 439
440 440 # A JSON-encoded object, that will be validated by the Traits
441 441 # information in the kernel
442 442 'value' : object,
443 443 }
444 444
445 445 When a ``setattr_request`` fails, there are also two possible error types with
446 446 similar meanings as those of the ``getattr_request`` case, but for writing.
447 447
448 448 Message type: ``setattr_reply``::
449 449
450 450 content = {
451 451 # One of ['ok', 'AttributeError', 'AccessError'].
452 452 'status' : str,
453 453 }
454 454
455 455
456 456
457 457 Object information
458 458 ------------------
459 459
460 460 One of IPython's most used capabilities is the introspection of Python objects
461 461 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
462 462 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
463 463 enough that it warrants an explicit message type, especially because frontends
464 464 may want to get object information in response to user keystrokes (like Tab or
465 465 F1) besides from the user explicitly typing code like ``x??``.
466 466
467 467 Message type: ``object_info_request``::
468 468
469 469 content = {
470 470 # The (possibly dotted) name of the object to be searched in all
471 471 # relevant namespaces
472 472 'name' : str,
473 473
474 474 # The level of detail desired. The default (0) is equivalent to typing
475 475 # 'x?' at the prompt, 1 is equivalent to 'x??'.
476 476 'detail_level' : int,
477 477 }
478 478
479 479 The returned information will be a dictionary with keys very similar to the
480 480 field names that IPython prints at the terminal.
481 481
482 482 Message type: ``object_info_reply``::
483 483
484 484 content = {
485 485 # The name the object was requested under
486 486 'name' : str,
487 487
488 488 # Boolean flag indicating whether the named object was found or not. If
489 489 # it's false, all other fields will be empty.
490 490 'found' : bool,
491 491
492 492 # Flags for magics and system aliases
493 493 'ismagic' : bool,
494 494 'isalias' : bool,
495 495
496 496 # The name of the namespace where the object was found ('builtin',
497 497 # 'magics', 'alias', 'interactive', etc.)
498 498 'namespace' : str,
499 499
500 500 # The type name will be type.__name__ for normal Python objects, but it
501 501 # can also be a string like 'Magic function' or 'System alias'
502 502 'type_name' : str,
503 503
504 504 # The string form of the object, possibly truncated for length if
505 505 # detail_level is 0
506 506 'string_form' : str,
507 507
508 508 # For objects with a __class__ attribute this will be set
509 509 'base_class' : str,
510 510
511 511 # For objects with a __len__ attribute this will be set
512 512 'length' : int,
513 513
514 514 # If the object is a function, class or method whose file we can find,
515 515 # we give its full path
516 516 'file' : str,
517 517
518 518 # For pure Python callable objects, we can reconstruct the object
519 519 # definition line which provides its call signature. For convenience this
520 520 # is returned as a single 'definition' field, but below the raw parts that
521 521 # compose it are also returned as the argspec field.
522 522 'definition' : str,
523 523
524 524 # The individual parts that together form the definition string. Clients
525 525 # with rich display capabilities may use this to provide a richer and more
526 526 # precise representation of the definition line (e.g. by highlighting
527 527 # arguments based on the user's cursor position). For non-callable
528 528 # objects, this field is empty.
529 529 'argspec' : { # The names of all the arguments
530 530 args : list,
531 531 # The name of the varargs (*args), if any
532 532 varargs : str,
533 533 # The name of the varkw (**kw), if any
534 534 varkw : str,
535 535 # The values (as strings) of all default arguments. Note
536 536 # that these must be matched *in reverse* with the 'args'
537 537 # list above, since the first positional args have no default
538 538 # value at all.
539 539 defaults : list,
540 540 },
541 541
542 542 # For instances, provide the constructor signature (the definition of
543 543 # the __init__ method):
544 544 'init_definition' : str,
545 545
546 546 # Docstrings: for any object (function, method, module, package) with a
547 547 # docstring, we show it. But in addition, we may provide additional
548 548 # docstrings. For example, for instances we will show the constructor
549 549 # and class docstrings as well, if available.
550 550 'docstring' : str,
551 551
552 552 # For instances, provide the constructor and class docstrings
553 553 'init_docstring' : str,
554 554 'class_docstring' : str,
555 555
556 556 # If it's a callable object whose call method has a separate docstring and
557 557 # definition line:
558 558 'call_def' : str,
559 559 'call_docstring' : str,
560 560
561 561 # If detail_level was 1, we also try to find the source code that
562 562 # defines the object, if possible. The string 'None' will indicate
563 563 # that no source was found.
564 564 'source' : str,
565 565 }
566 566
567 567
568 568 Complete
569 569 --------
570 570
571 571 Message type: ``complete_request``::
572 572
573 573 content = {
574 574 # The text to be completed, such as 'a.is'
575 575 'text' : str,
576 576
577 577 # The full line, such as 'print a.is'. This allows completers to
578 578 # make decisions that may require information about more than just the
579 579 # current word.
580 580 'line' : str,
581 581
582 582 # The entire block of text where the line is. This may be useful in the
583 583 # case of multiline completions where more context may be needed. Note: if
584 584 # in practice this field proves unnecessary, remove it to lighten the
585 585 # messages.
586 586
587 587 'block' : str,
588 588
589 589 # The position of the cursor where the user hit 'TAB' on the line.
590 590 'cursor_pos' : int,
591 591 }
592 592
593 593 Message type: ``complete_reply``::
594 594
595 595 content = {
596 596 # The list of all matches to the completion request, such as
597 597 # ['a.isalnum', 'a.isalpha'] for the above example.
598 598 'matches' : list
599 599 }
600 600
601 601
602 602 History
603 603 -------
604 604
605 605 For clients to explicitly request history from a kernel. The kernel has all
606 606 the actual execution history stored in a single location, so clients can
607 607 request it from the kernel when needed.
608 608
609 609 Message type: ``history_request``::
610 610
611 611 content = {
612 612
613 613 # If True, also return output history in the resulting dict.
614 614 'output' : bool,
615 615
616 616 # If True, return the raw input history, else the transformed input.
617 617 'raw' : bool,
618 618
619 619 # So far, this can be 'range', 'tail' or 'search'.
620 620 'hist_access_type' : str,
621 621
622 622 # If hist_access_type is 'range', get a range of input cells. session can
623 623 # be a positive session number, or a negative number to count back from
624 624 # the current session.
625 625 'session' : int,
626 626 # start and stop are line numbers within that session.
627 627 'start' : int,
628 628 'stop' : int,
629 629
630 630 # If hist_access_type is 'tail' or 'search', get the last n cells.
631 631 'n' : int,
632 632
633 633 # If hist_access_type is 'search', get cells matching the specified glob
634 634 # pattern (with * and ? as wildcards).
635 635 'pattern' : str,
636 636
637 637 # If hist_access_type is 'search' and unique is true, do not
638 638 # include duplicated history. Default is false.
639 639 'unique' : bool,
640 640
641 641 }
642 642
643 643 .. versionadded:: 4.0
644 644 The key ``unique`` for ``history_request``.
645 645
646 646 Message type: ``history_reply``::
647 647
648 648 content = {
649 649 # A list of 3 tuples, either:
650 650 # (session, line_number, input) or
651 651 # (session, line_number, (input, output)),
652 652 # depending on whether output was False or True, respectively.
653 653 'history' : list,
654 654 }
655 655
656 656
657 657 Connect
658 658 -------
659 659
660 660 When a client connects to the request/reply socket of the kernel, it can issue
661 661 a connect request to get basic information about the kernel, such as the ports
662 662 the other ZeroMQ sockets are listening on. This allows clients to only have
663 663 to know about a single port (the shell channel) to connect to a kernel.
664 664
665 665 Message type: ``connect_request``::
666 666
667 667 content = {
668 668 }
669 669
670 670 Message type: ``connect_reply``::
671 671
672 672 content = {
673 673 'shell_port' : int # The port the shell ROUTER socket is listening on.
674 674 'iopub_port' : int # The port the PUB socket is listening on.
675 675 'stdin_port' : int # The port the stdin ROUTER socket is listening on.
676 676 'hb_port' : int # The port the heartbeat socket is listening on.
677 677 }
678 678
679 679
680 680 Kernel info
681 681 -----------
682 682
683 683 If a client needs to know what protocol the kernel supports, it can
684 684 ask version number of the messaging protocol supported by the kernel.
685 685 This message can be used to fetch other core information of the
686 686 kernel, including language (e.g., Python), language version number and
687 687 IPython version number.
688 688
689 689 Message type: ``kernel_info_request``::
690 690
691 691 content = {
692 692 }
693 693
694 694 Message type: ``kernel_info_reply``::
695 695
696 696 content = {
697 697 # Version of messaging protocol (mandatory).
698 698 # The first integer indicates major version. It is incremented when
699 699 # there is any backward incompatible change.
700 700 # The second integer indicates minor version. It is incremented when
701 701 # there is any backward compatible change.
702 702 'protocol_version': [int, int],
703 703
704 704 # IPython version number (optional).
705 705 # Non-python kernel backend may not have this version number.
706 706 # The last component is an extra field, which may be 'dev' or
707 707 # 'rc1' in development version. It is an empty string for
708 708 # released version.
709 709 'ipython_version': [int, int, int, str],
710 710
711 711 # Language version number (mandatory).
712 712 # It is Python version number (e.g., [2, 7, 3]) for the kernel
713 713 # included in IPython.
714 714 'language_version': [int, ...],
715 715
716 716 # Programming language in which kernel is implemented (mandatory).
717 717 # Kernel included in IPython returns 'python'.
718 718 'language': str,
719 719 }
720 720
721 721
722 722 Kernel shutdown
723 723 ---------------
724 724
725 725 The clients can request the kernel to shut itself down; this is used in
726 726 multiple cases:
727 727
728 728 - when the user chooses to close the client application via a menu or window
729 729 control.
730 730 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
731 731 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
732 732 IPythonQt client) to force a kernel restart to get a clean kernel without
733 733 losing client-side state like history or inlined figures.
734 734
735 735 The client sends a shutdown request to the kernel, and once it receives the
736 736 reply message (which is otherwise empty), it can assume that the kernel has
737 737 completed shutdown safely.
738 738
739 739 Upon their own shutdown, client applications will typically execute a last
740 740 minute sanity check and forcefully terminate any kernel that is still alive, to
741 741 avoid leaving stray processes in the user's machine.
742 742
743 743 For both shutdown request and reply, there is no actual content that needs to
744 744 be sent, so the content dict is empty.
745 745
746 746 Message type: ``shutdown_request``::
747 747
748 748 content = {
749 749 'restart' : bool # whether the shutdown is final, or precedes a restart
750 750 }
751 751
752 752 Message type: ``shutdown_reply``::
753 753
754 754 content = {
755 755 'restart' : bool # whether the shutdown is final, or precedes a restart
756 756 }
757 757
758 758 .. Note::
759 759
760 760 When the clients detect a dead kernel thanks to inactivity on the heartbeat
761 761 socket, they simply send a forceful process termination signal, since a dead
762 762 process is unlikely to respond in any useful way to messages.
763 763
764 764
765 765 Messages on the PUB/SUB socket
766 766 ==============================
767 767
768 768 Streams (stdout, stderr, etc)
769 769 ------------------------------
770 770
771 771 Message type: ``stream``::
772 772
773 773 content = {
774 774 # The name of the stream is one of 'stdin', 'stdout', 'stderr'
775 775 'name' : str,
776 776
777 777 # The data is an arbitrary string to be written to that stream
778 778 'data' : str,
779 779 }
780 780
781 781 When a kernel receives a raw_input call, it should also broadcast it on the pub
782 782 socket with the names 'stdin' and 'stdin_reply'. This will allow other clients
783 783 to monitor/display kernel interactions and possibly replay them to their user
784 784 or otherwise expose them.
785 785
786 786 Display Data
787 787 ------------
788 788
789 789 This type of message is used to bring back data that should be diplayed (text,
790 790 html, svg, etc.) in the frontends. This data is published to all frontends.
791 791 Each message can have multiple representations of the data; it is up to the
792 792 frontend to decide which to use and how. A single message should contain all
793 793 possible representations of the same information. Each representation should
794 794 be a JSON'able data structure, and should be a valid MIME type.
795 795
796 796 Some questions remain about this design:
797 797
798 798 * Do we use this message type for pyout/displayhook? Probably not, because
799 799 the displayhook also has to handle the Out prompt display. On the other hand
800 800 we could put that information into the metadata secion.
801 801
802 802 Message type: ``display_data``::
803 803
804 804 content = {
805 805
806 806 # Who create the data
807 807 'source' : str,
808 808
809 809 # The data dict contains key/value pairs, where the kids are MIME
810 810 # types and the values are the raw data of the representation in that
811 811 # format. The data dict must minimally contain the ``text/plain``
812 812 # MIME type which is used as a backup representation.
813 813 'data' : dict,
814 814
815 815 # Any metadata that describes the data
816 816 'metadata' : dict
817 817 }
818 818
819 819
820 820 Raw Data Publication
821 821 --------------------
822 822
823 823 ``display_data`` lets you publish *representations* of data, such as images and html.
824 824 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
825 825
826 826 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
827 827
828 828 .. sourcecode:: python
829 829
830 830 from IPython.kernel.zmq.datapub import publish_data
831 831 ns = dict(x=my_array)
832 832 publish_data(ns)
833 833
834 834
835 835 Message type: ``data_pub``::
836 836
837 837 content = {
838 838 # the keys of the data dict, after it has been unserialized
839 839 keys = ['a', 'b']
840 840 }
841 841 # the namespace dict will be serialized in the message buffers,
842 842 # which will have a length of at least one
843 843 buffers = ['pdict', ...]
844 844
845 845
846 846 The interpretation of a sequence of data_pub messages for a given parent request should be
847 847 to update a single namespace with subsequent results.
848 848
849 849 .. note::
850 850
851 851 No frontends directly handle data_pub messages at this time.
852 852 It is currently only used by the client/engines in :mod:`IPython.parallel`,
853 853 where engines may publish *data* to the Client,
854 854 of which the Client can then publish *representations* via ``display_data``
855 855 to various frontends.
856 856
857 857 Python inputs
858 858 -------------
859 859
860 860 These messages are the re-broadcast of the ``execute_request``.
861 861
862 862 Message type: ``pyin``::
863 863
864 864 content = {
865 865 'code' : str, # Source code to be executed, one or more lines
866 866
867 867 # The counter for this execution is also provided so that clients can
868 868 # display it, since IPython automatically creates variables called _iN
869 869 # (for input prompt In[N]).
870 870 'execution_count' : int
871 871 }
872 872
873 873 Python outputs
874 874 --------------
875 875
876 876 When Python produces output from code that has been compiled in with the
877 877 'single' flag to :func:`compile`, any expression that produces a value (such as
878 878 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
879 879 this value whatever it wants. The default behavior of ``sys.displayhook`` in
880 880 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
881 881 the value as long as it is not ``None`` (which isn't printed at all). In our
882 882 case, the kernel instantiates as ``sys.displayhook`` an object which has
883 883 similar behavior, but which instead of printing to stdout, broadcasts these
884 884 values as ``pyout`` messages for clients to display appropriately.
885 885
886 886 IPython's displayhook can handle multiple simultaneous formats depending on its
887 887 configuration. The default pretty-printed repr text is always given with the
888 888 ``data`` entry in this message. Any other formats are provided in the
889 889 ``extra_formats`` list. Frontends are free to display any or all of these
890 890 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
891 891 string, a type string, and the data. The ID is unique to the formatter
892 892 implementation that created the data. Frontends will typically ignore the ID
893 893 unless if it has requested a particular formatter. The type string tells the
894 894 frontend how to interpret the data. It is often, but not always a MIME type.
895 895 Frontends should ignore types that it does not understand. The data itself is
896 896 any JSON object and depends on the format. It is often, but not always a string.
897 897
898 898 Message type: ``pyout``::
899 899
900 900 content = {
901 901
902 902 # The counter for this execution is also provided so that clients can
903 903 # display it, since IPython automatically creates variables called _N
904 904 # (for prompt N).
905 905 'execution_count' : int,
906 906
907 907 # The data dict contains key/value pairs, where the kids are MIME
908 908 # types and the values are the raw data of the representation in that
909 909 # format. The data dict must minimally contain the ``text/plain``
910 910 # MIME type which is used as a backup representation.
911 911 'data' : dict,
912 912
913 913 }
914 914
915 915 Python errors
916 916 -------------
917 917
918 918 When an error occurs during code execution
919 919
920 920 Message type: ``pyerr``::
921 921
922 922 content = {
923 923 # Similar content to the execute_reply messages for the 'error' case,
924 924 # except the 'status' field is omitted.
925 925 }
926 926
927 927 Kernel status
928 928 -------------
929 929
930 930 This message type is used by frontends to monitor the status of the kernel.
931 931
932 932 Message type: ``status``::
933 933
934 934 content = {
935 935 # When the kernel starts to execute code, it will enter the 'busy'
936 936 # state and when it finishes, it will enter the 'idle' state.
937 937 execution_state : ('busy', 'idle')
938 938 }
939 939
940 940 Kernel crashes
941 941 --------------
942 942
943 943 When the kernel has an unexpected exception, caught by the last-resort
944 944 sys.excepthook, we should broadcast the crash handler's output before exiting.
945 945 This will allow clients to notice that a kernel died, inform the user and
946 946 propose further actions.
947 947
948 948 Message type: ``crash``::
949 949
950 950 content = {
951 951 # Similarly to the 'error' case for execute_reply messages, this will
952 # contain ename, etype and traceback fields.
952 # contain ename, evalue and traceback fields.
953 953
954 954 # An additional field with supplementary information such as where to
955 955 # send the crash message
956 956 'info' : str,
957 957 }
958 958
959 959
960 960 Future ideas
961 961 ------------
962 962
963 963 Other potential message types, currently unimplemented, listed below as ideas.
964 964
965 965 Message type: ``file``::
966 966
967 967 content = {
968 968 'path' : 'cool.jpg',
969 969 'mimetype' : str,
970 970 'data' : str,
971 971 }
972 972
973 973
974 974 Messages on the stdin ROUTER/DEALER sockets
975 975 ===========================================
976 976
977 977 This is a socket where the request/reply pattern goes in the opposite direction:
978 978 from the kernel to a *single* frontend, and its purpose is to allow
979 979 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
980 980 to be fulfilled by the client. The request should be made to the frontend that
981 981 made the execution request that prompted ``raw_input`` to be called. For now we
982 982 will keep these messages as simple as possible, since they only mean to convey
983 983 the ``raw_input(prompt)`` call.
984 984
985 985 Message type: ``input_request``::
986 986
987 987 content = { 'prompt' : str }
988 988
989 989 Message type: ``input_reply``::
990 990
991 991 content = { 'value' : str }
992 992
993 993 .. Note::
994 994
995 995 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
996 996 practice the kernel should behave like an interactive program. When a
997 997 program is opened on the console, the keyboard effectively takes over the
998 998 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
999 999 Since the IPython kernel effectively behaves like a console program (albeit
1000 1000 one whose "keyboard" is actually living in a separate process and
1001 1001 transported over the zmq connection), raw ``stdin`` isn't expected to be
1002 1002 available.
1003 1003
1004 1004
1005 1005 Heartbeat for kernels
1006 1006 =====================
1007 1007
1008 1008 Initially we had considered using messages like those above over ZMQ for a
1009 1009 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
1010 1010 alive at all, even if it may be busy executing user code). But this has the
1011 1011 problem that if the kernel is locked inside extension code, it wouldn't execute
1012 1012 the python heartbeat code. But it turns out that we can implement a basic
1013 1013 heartbeat with pure ZMQ, without using any Python messaging at all.
1014 1014
1015 1015 The monitor sends out a single zmq message (right now, it is a str of the
1016 1016 monitor's lifetime in seconds), and gets the same message right back, prefixed
1017 1017 with the zmq identity of the DEALER socket in the heartbeat process. This can be
1018 1018 a uuid, or even a full message, but there doesn't seem to be a need for packing
1019 1019 up a message when the sender and receiver are the exact same Python object.
1020 1020
1021 1021 The model is this::
1022 1022
1023 1023 monitor.send(str(self.lifetime)) # '1.2345678910'
1024 1024
1025 1025 and the monitor receives some number of messages of the form::
1026 1026
1027 1027 ['uuid-abcd-dead-beef', '1.2345678910']
1028 1028
1029 1029 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
1030 1030 the rest is the message sent by the monitor. No Python code ever has any
1031 1031 access to the message between the monitor's send, and the monitor's recv.
1032 1032
1033 1033
1034 1034 ToDo
1035 1035 ====
1036 1036
1037 1037 Missing things include:
1038 1038
1039 1039 * Important: finish thinking through the payload concept and API.
1040 1040
1041 1041 * Important: ensure that we have a good solution for magics like %edit. It's
1042 1042 likely that with the payload concept we can build a full solution, but not
1043 1043 100% clear yet.
1044 1044
1045 1045 * Finishing the details of the heartbeat protocol.
1046 1046
1047 1047 * Signal handling: specify what kind of information kernel should broadcast (or
1048 1048 not) when it receives signals.
1049 1049
1050 1050 .. include:: ../links.rst
General Comments 0
You need to be logged in to leave comments. Login now