##// END OF EJS Templates
Added wait flag to clear_output.
Jonathan Frederic -
Show More
@@ -1,671 +1,675 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
2 """Top-level display functions for displaying object in different formats.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2013 The IPython Development Team
10 # Copyright (C) 2013 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 from __future__ import print_function
20 from __future__ import print_function
21
21
22 import os
22 import os
23 import struct
23 import struct
24
24
25 from IPython.utils.py3compat import string_types, cast_bytes_py2, cast_unicode
25 from IPython.utils.py3compat import string_types, cast_bytes_py2, cast_unicode
26
26
27 from .displaypub import publish_display_data
27 from .displaypub import publish_display_data
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # utility functions
30 # utility functions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 def _safe_exists(path):
33 def _safe_exists(path):
34 """Check path, but don't let exceptions raise"""
34 """Check path, but don't let exceptions raise"""
35 try:
35 try:
36 return os.path.exists(path)
36 return os.path.exists(path)
37 except Exception:
37 except Exception:
38 return False
38 return False
39
39
40 def _merge(d1, d2):
40 def _merge(d1, d2):
41 """Like update, but merges sub-dicts instead of clobbering at the top level.
41 """Like update, but merges sub-dicts instead of clobbering at the top level.
42
42
43 Updates d1 in-place
43 Updates d1 in-place
44 """
44 """
45
45
46 if not isinstance(d2, dict) or not isinstance(d1, dict):
46 if not isinstance(d2, dict) or not isinstance(d1, dict):
47 return d2
47 return d2
48 for key, value in d2.items():
48 for key, value in d2.items():
49 d1[key] = _merge(d1.get(key), value)
49 d1[key] = _merge(d1.get(key), value)
50 return d1
50 return d1
51
51
52 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
52 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
53 """internal implementation of all display_foo methods
53 """internal implementation of all display_foo methods
54
54
55 Parameters
55 Parameters
56 ----------
56 ----------
57 mimetype : str
57 mimetype : str
58 The mimetype to be published (e.g. 'image/png')
58 The mimetype to be published (e.g. 'image/png')
59 objs : tuple of objects
59 objs : tuple of objects
60 The Python objects to display, or if raw=True raw text data to
60 The Python objects to display, or if raw=True raw text data to
61 display.
61 display.
62 raw : bool
62 raw : bool
63 Are the data objects raw data or Python objects that need to be
63 Are the data objects raw data or Python objects that need to be
64 formatted before display? [default: False]
64 formatted before display? [default: False]
65 metadata : dict (optional)
65 metadata : dict (optional)
66 Metadata to be associated with the specific mimetype output.
66 Metadata to be associated with the specific mimetype output.
67 """
67 """
68 if metadata:
68 if metadata:
69 metadata = {mimetype: metadata}
69 metadata = {mimetype: metadata}
70 if raw:
70 if raw:
71 # turn list of pngdata into list of { 'image/png': pngdata }
71 # turn list of pngdata into list of { 'image/png': pngdata }
72 objs = [ {mimetype: obj} for obj in objs ]
72 objs = [ {mimetype: obj} for obj in objs ]
73 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
73 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
74
74
75 #-----------------------------------------------------------------------------
75 #-----------------------------------------------------------------------------
76 # Main functions
76 # Main functions
77 #-----------------------------------------------------------------------------
77 #-----------------------------------------------------------------------------
78
78
79 def display(*objs, **kwargs):
79 def display(*objs, **kwargs):
80 """Display a Python object in all frontends.
80 """Display a Python object in all frontends.
81
81
82 By default all representations will be computed and sent to the frontends.
82 By default all representations will be computed and sent to the frontends.
83 Frontends can decide which representation is used and how.
83 Frontends can decide which representation is used and how.
84
84
85 Parameters
85 Parameters
86 ----------
86 ----------
87 objs : tuple of objects
87 objs : tuple of objects
88 The Python objects to display.
88 The Python objects to display.
89 raw : bool, optional
89 raw : bool, optional
90 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
90 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
91 or Python objects that need to be formatted before display? [default: False]
91 or Python objects that need to be formatted before display? [default: False]
92 include : list or tuple, optional
92 include : list or tuple, optional
93 A list of format type strings (MIME types) to include in the
93 A list of format type strings (MIME types) to include in the
94 format data dict. If this is set *only* the format types included
94 format data dict. If this is set *only* the format types included
95 in this list will be computed.
95 in this list will be computed.
96 exclude : list or tuple, optional
96 exclude : list or tuple, optional
97 A list of format type strings (MIME types) to exclude in the format
97 A list of format type strings (MIME types) to exclude in the format
98 data dict. If this is set all format types will be computed,
98 data dict. If this is set all format types will be computed,
99 except for those included in this argument.
99 except for those included in this argument.
100 metadata : dict, optional
100 metadata : dict, optional
101 A dictionary of metadata to associate with the output.
101 A dictionary of metadata to associate with the output.
102 mime-type keys in this dictionary will be associated with the individual
102 mime-type keys in this dictionary will be associated with the individual
103 representation formats, if they exist.
103 representation formats, if they exist.
104 """
104 """
105 raw = kwargs.get('raw', False)
105 raw = kwargs.get('raw', False)
106 include = kwargs.get('include')
106 include = kwargs.get('include')
107 exclude = kwargs.get('exclude')
107 exclude = kwargs.get('exclude')
108 metadata = kwargs.get('metadata')
108 metadata = kwargs.get('metadata')
109
109
110 from IPython.core.interactiveshell import InteractiveShell
110 from IPython.core.interactiveshell import InteractiveShell
111
111
112 if raw:
112 if raw:
113 for obj in objs:
113 for obj in objs:
114 publish_display_data('display', obj, metadata)
114 publish_display_data('display', obj, metadata)
115 else:
115 else:
116 format = InteractiveShell.instance().display_formatter.format
116 format = InteractiveShell.instance().display_formatter.format
117 for obj in objs:
117 for obj in objs:
118 format_dict, md_dict = format(obj, include=include, exclude=exclude)
118 format_dict, md_dict = format(obj, include=include, exclude=exclude)
119 if metadata:
119 if metadata:
120 # kwarg-specified metadata gets precedence
120 # kwarg-specified metadata gets precedence
121 _merge(md_dict, metadata)
121 _merge(md_dict, metadata)
122 publish_display_data('display', format_dict, md_dict)
122 publish_display_data('display', format_dict, md_dict)
123
123
124
124
125 def display_pretty(*objs, **kwargs):
125 def display_pretty(*objs, **kwargs):
126 """Display the pretty (default) representation of an object.
126 """Display the pretty (default) representation of an object.
127
127
128 Parameters
128 Parameters
129 ----------
129 ----------
130 objs : tuple of objects
130 objs : tuple of objects
131 The Python objects to display, or if raw=True raw text data to
131 The Python objects to display, or if raw=True raw text data to
132 display.
132 display.
133 raw : bool
133 raw : bool
134 Are the data objects raw data or Python objects that need to be
134 Are the data objects raw data or Python objects that need to be
135 formatted before display? [default: False]
135 formatted before display? [default: False]
136 metadata : dict (optional)
136 metadata : dict (optional)
137 Metadata to be associated with the specific mimetype output.
137 Metadata to be associated with the specific mimetype output.
138 """
138 """
139 _display_mimetype('text/plain', objs, **kwargs)
139 _display_mimetype('text/plain', objs, **kwargs)
140
140
141
141
142 def display_html(*objs, **kwargs):
142 def display_html(*objs, **kwargs):
143 """Display the HTML representation of an object.
143 """Display the HTML representation of an object.
144
144
145 Parameters
145 Parameters
146 ----------
146 ----------
147 objs : tuple of objects
147 objs : tuple of objects
148 The Python objects to display, or if raw=True raw HTML data to
148 The Python objects to display, or if raw=True raw HTML data to
149 display.
149 display.
150 raw : bool
150 raw : bool
151 Are the data objects raw data or Python objects that need to be
151 Are the data objects raw data or Python objects that need to be
152 formatted before display? [default: False]
152 formatted before display? [default: False]
153 metadata : dict (optional)
153 metadata : dict (optional)
154 Metadata to be associated with the specific mimetype output.
154 Metadata to be associated with the specific mimetype output.
155 """
155 """
156 _display_mimetype('text/html', objs, **kwargs)
156 _display_mimetype('text/html', objs, **kwargs)
157
157
158
158
159 def display_svg(*objs, **kwargs):
159 def display_svg(*objs, **kwargs):
160 """Display the SVG representation of an object.
160 """Display the SVG representation of an object.
161
161
162 Parameters
162 Parameters
163 ----------
163 ----------
164 objs : tuple of objects
164 objs : tuple of objects
165 The Python objects to display, or if raw=True raw svg data to
165 The Python objects to display, or if raw=True raw svg data to
166 display.
166 display.
167 raw : bool
167 raw : bool
168 Are the data objects raw data or Python objects that need to be
168 Are the data objects raw data or Python objects that need to be
169 formatted before display? [default: False]
169 formatted before display? [default: False]
170 metadata : dict (optional)
170 metadata : dict (optional)
171 Metadata to be associated with the specific mimetype output.
171 Metadata to be associated with the specific mimetype output.
172 """
172 """
173 _display_mimetype('image/svg+xml', objs, **kwargs)
173 _display_mimetype('image/svg+xml', objs, **kwargs)
174
174
175
175
176 def display_png(*objs, **kwargs):
176 def display_png(*objs, **kwargs):
177 """Display the PNG representation of an object.
177 """Display the PNG representation of an object.
178
178
179 Parameters
179 Parameters
180 ----------
180 ----------
181 objs : tuple of objects
181 objs : tuple of objects
182 The Python objects to display, or if raw=True raw png data to
182 The Python objects to display, or if raw=True raw png data to
183 display.
183 display.
184 raw : bool
184 raw : bool
185 Are the data objects raw data or Python objects that need to be
185 Are the data objects raw data or Python objects that need to be
186 formatted before display? [default: False]
186 formatted before display? [default: False]
187 metadata : dict (optional)
187 metadata : dict (optional)
188 Metadata to be associated with the specific mimetype output.
188 Metadata to be associated with the specific mimetype output.
189 """
189 """
190 _display_mimetype('image/png', objs, **kwargs)
190 _display_mimetype('image/png', objs, **kwargs)
191
191
192
192
193 def display_jpeg(*objs, **kwargs):
193 def display_jpeg(*objs, **kwargs):
194 """Display the JPEG representation of an object.
194 """Display the JPEG representation of an object.
195
195
196 Parameters
196 Parameters
197 ----------
197 ----------
198 objs : tuple of objects
198 objs : tuple of objects
199 The Python objects to display, or if raw=True raw JPEG data to
199 The Python objects to display, or if raw=True raw JPEG data to
200 display.
200 display.
201 raw : bool
201 raw : bool
202 Are the data objects raw data or Python objects that need to be
202 Are the data objects raw data or Python objects that need to be
203 formatted before display? [default: False]
203 formatted before display? [default: False]
204 metadata : dict (optional)
204 metadata : dict (optional)
205 Metadata to be associated with the specific mimetype output.
205 Metadata to be associated with the specific mimetype output.
206 """
206 """
207 _display_mimetype('image/jpeg', objs, **kwargs)
207 _display_mimetype('image/jpeg', objs, **kwargs)
208
208
209
209
210 def display_latex(*objs, **kwargs):
210 def display_latex(*objs, **kwargs):
211 """Display the LaTeX representation of an object.
211 """Display the LaTeX representation of an object.
212
212
213 Parameters
213 Parameters
214 ----------
214 ----------
215 objs : tuple of objects
215 objs : tuple of objects
216 The Python objects to display, or if raw=True raw latex data to
216 The Python objects to display, or if raw=True raw latex data to
217 display.
217 display.
218 raw : bool
218 raw : bool
219 Are the data objects raw data or Python objects that need to be
219 Are the data objects raw data or Python objects that need to be
220 formatted before display? [default: False]
220 formatted before display? [default: False]
221 metadata : dict (optional)
221 metadata : dict (optional)
222 Metadata to be associated with the specific mimetype output.
222 Metadata to be associated with the specific mimetype output.
223 """
223 """
224 _display_mimetype('text/latex', objs, **kwargs)
224 _display_mimetype('text/latex', objs, **kwargs)
225
225
226
226
227 def display_json(*objs, **kwargs):
227 def display_json(*objs, **kwargs):
228 """Display the JSON representation of an object.
228 """Display the JSON representation of an object.
229
229
230 Note that not many frontends support displaying JSON.
230 Note that not many frontends support displaying JSON.
231
231
232 Parameters
232 Parameters
233 ----------
233 ----------
234 objs : tuple of objects
234 objs : tuple of objects
235 The Python objects to display, or if raw=True raw json data to
235 The Python objects to display, or if raw=True raw json data to
236 display.
236 display.
237 raw : bool
237 raw : bool
238 Are the data objects raw data or Python objects that need to be
238 Are the data objects raw data or Python objects that need to be
239 formatted before display? [default: False]
239 formatted before display? [default: False]
240 metadata : dict (optional)
240 metadata : dict (optional)
241 Metadata to be associated with the specific mimetype output.
241 Metadata to be associated with the specific mimetype output.
242 """
242 """
243 _display_mimetype('application/json', objs, **kwargs)
243 _display_mimetype('application/json', objs, **kwargs)
244
244
245
245
246 def display_javascript(*objs, **kwargs):
246 def display_javascript(*objs, **kwargs):
247 """Display the Javascript representation of an object.
247 """Display the Javascript representation of an object.
248
248
249 Parameters
249 Parameters
250 ----------
250 ----------
251 objs : tuple of objects
251 objs : tuple of objects
252 The Python objects to display, or if raw=True raw javascript data to
252 The Python objects to display, or if raw=True raw javascript data to
253 display.
253 display.
254 raw : bool
254 raw : bool
255 Are the data objects raw data or Python objects that need to be
255 Are the data objects raw data or Python objects that need to be
256 formatted before display? [default: False]
256 formatted before display? [default: False]
257 metadata : dict (optional)
257 metadata : dict (optional)
258 Metadata to be associated with the specific mimetype output.
258 Metadata to be associated with the specific mimetype output.
259 """
259 """
260 _display_mimetype('application/javascript', objs, **kwargs)
260 _display_mimetype('application/javascript', objs, **kwargs)
261
261
262 #-----------------------------------------------------------------------------
262 #-----------------------------------------------------------------------------
263 # Smart classes
263 # Smart classes
264 #-----------------------------------------------------------------------------
264 #-----------------------------------------------------------------------------
265
265
266
266
267 class DisplayObject(object):
267 class DisplayObject(object):
268 """An object that wraps data to be displayed."""
268 """An object that wraps data to be displayed."""
269
269
270 _read_flags = 'r'
270 _read_flags = 'r'
271
271
272 def __init__(self, data=None, url=None, filename=None):
272 def __init__(self, data=None, url=None, filename=None):
273 """Create a display object given raw data.
273 """Create a display object given raw data.
274
274
275 When this object is returned by an expression or passed to the
275 When this object is returned by an expression or passed to the
276 display function, it will result in the data being displayed
276 display function, it will result in the data being displayed
277 in the frontend. The MIME type of the data should match the
277 in the frontend. The MIME type of the data should match the
278 subclasses used, so the Png subclass should be used for 'image/png'
278 subclasses used, so the Png subclass should be used for 'image/png'
279 data. If the data is a URL, the data will first be downloaded
279 data. If the data is a URL, the data will first be downloaded
280 and then displayed. If
280 and then displayed. If
281
281
282 Parameters
282 Parameters
283 ----------
283 ----------
284 data : unicode, str or bytes
284 data : unicode, str or bytes
285 The raw data or a URL or file to load the data from
285 The raw data or a URL or file to load the data from
286 url : unicode
286 url : unicode
287 A URL to download the data from.
287 A URL to download the data from.
288 filename : unicode
288 filename : unicode
289 Path to a local file to load the data from.
289 Path to a local file to load the data from.
290 """
290 """
291 if data is not None and isinstance(data, string_types):
291 if data is not None and isinstance(data, string_types):
292 if data.startswith('http') and url is None:
292 if data.startswith('http') and url is None:
293 url = data
293 url = data
294 filename = None
294 filename = None
295 data = None
295 data = None
296 elif _safe_exists(data) and filename is None:
296 elif _safe_exists(data) and filename is None:
297 url = None
297 url = None
298 filename = data
298 filename = data
299 data = None
299 data = None
300
300
301 self.data = data
301 self.data = data
302 self.url = url
302 self.url = url
303 self.filename = None if filename is None else unicode(filename)
303 self.filename = None if filename is None else unicode(filename)
304
304
305 self.reload()
305 self.reload()
306
306
307 def reload(self):
307 def reload(self):
308 """Reload the raw data from file or URL."""
308 """Reload the raw data from file or URL."""
309 if self.filename is not None:
309 if self.filename is not None:
310 with open(self.filename, self._read_flags) as f:
310 with open(self.filename, self._read_flags) as f:
311 self.data = f.read()
311 self.data = f.read()
312 elif self.url is not None:
312 elif self.url is not None:
313 try:
313 try:
314 import urllib2
314 import urllib2
315 response = urllib2.urlopen(self.url)
315 response = urllib2.urlopen(self.url)
316 self.data = response.read()
316 self.data = response.read()
317 # extract encoding from header, if there is one:
317 # extract encoding from header, if there is one:
318 encoding = None
318 encoding = None
319 for sub in response.headers['content-type'].split(';'):
319 for sub in response.headers['content-type'].split(';'):
320 sub = sub.strip()
320 sub = sub.strip()
321 if sub.startswith('charset'):
321 if sub.startswith('charset'):
322 encoding = sub.split('=')[-1].strip()
322 encoding = sub.split('=')[-1].strip()
323 break
323 break
324 # decode data, if an encoding was specified
324 # decode data, if an encoding was specified
325 if encoding:
325 if encoding:
326 self.data = self.data.decode(encoding, 'replace')
326 self.data = self.data.decode(encoding, 'replace')
327 except:
327 except:
328 self.data = None
328 self.data = None
329
329
330 class Pretty(DisplayObject):
330 class Pretty(DisplayObject):
331
331
332 def _repr_pretty_(self):
332 def _repr_pretty_(self):
333 return self.data
333 return self.data
334
334
335
335
336 class HTML(DisplayObject):
336 class HTML(DisplayObject):
337
337
338 def _repr_html_(self):
338 def _repr_html_(self):
339 return self.data
339 return self.data
340
340
341 def __html__(self):
341 def __html__(self):
342 """
342 """
343 This method exists to inform other HTML-using modules (e.g. Markupsafe,
343 This method exists to inform other HTML-using modules (e.g. Markupsafe,
344 htmltag, etc) that this object is HTML and does not need things like
344 htmltag, etc) that this object is HTML and does not need things like
345 special characters (<>&) escaped.
345 special characters (<>&) escaped.
346 """
346 """
347 return self._repr_html_()
347 return self._repr_html_()
348
348
349
349
350 class Math(DisplayObject):
350 class Math(DisplayObject):
351
351
352 def _repr_latex_(self):
352 def _repr_latex_(self):
353 s = self.data.strip('$')
353 s = self.data.strip('$')
354 return "$$%s$$" % s
354 return "$$%s$$" % s
355
355
356
356
357 class Latex(DisplayObject):
357 class Latex(DisplayObject):
358
358
359 def _repr_latex_(self):
359 def _repr_latex_(self):
360 return self.data
360 return self.data
361
361
362
362
363 class SVG(DisplayObject):
363 class SVG(DisplayObject):
364
364
365 # wrap data in a property, which extracts the <svg> tag, discarding
365 # wrap data in a property, which extracts the <svg> tag, discarding
366 # document headers
366 # document headers
367 _data = None
367 _data = None
368
368
369 @property
369 @property
370 def data(self):
370 def data(self):
371 return self._data
371 return self._data
372
372
373 @data.setter
373 @data.setter
374 def data(self, svg):
374 def data(self, svg):
375 if svg is None:
375 if svg is None:
376 self._data = None
376 self._data = None
377 return
377 return
378 # parse into dom object
378 # parse into dom object
379 from xml.dom import minidom
379 from xml.dom import minidom
380 svg = cast_bytes_py2(svg)
380 svg = cast_bytes_py2(svg)
381 x = minidom.parseString(svg)
381 x = minidom.parseString(svg)
382 # get svg tag (should be 1)
382 # get svg tag (should be 1)
383 found_svg = x.getElementsByTagName('svg')
383 found_svg = x.getElementsByTagName('svg')
384 if found_svg:
384 if found_svg:
385 svg = found_svg[0].toxml()
385 svg = found_svg[0].toxml()
386 else:
386 else:
387 # fallback on the input, trust the user
387 # fallback on the input, trust the user
388 # but this is probably an error.
388 # but this is probably an error.
389 pass
389 pass
390 svg = cast_unicode(svg)
390 svg = cast_unicode(svg)
391 self._data = svg
391 self._data = svg
392
392
393 def _repr_svg_(self):
393 def _repr_svg_(self):
394 return self.data
394 return self.data
395
395
396
396
397 class JSON(DisplayObject):
397 class JSON(DisplayObject):
398
398
399 def _repr_json_(self):
399 def _repr_json_(self):
400 return self.data
400 return self.data
401
401
402 css_t = """$("head").append($("<link/>").attr({
402 css_t = """$("head").append($("<link/>").attr({
403 rel: "stylesheet",
403 rel: "stylesheet",
404 type: "text/css",
404 type: "text/css",
405 href: "%s"
405 href: "%s"
406 }));
406 }));
407 """
407 """
408
408
409 lib_t1 = """$.getScript("%s", function () {
409 lib_t1 = """$.getScript("%s", function () {
410 """
410 """
411 lib_t2 = """});
411 lib_t2 = """});
412 """
412 """
413
413
414 class Javascript(DisplayObject):
414 class Javascript(DisplayObject):
415
415
416 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
416 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
417 """Create a Javascript display object given raw data.
417 """Create a Javascript display object given raw data.
418
418
419 When this object is returned by an expression or passed to the
419 When this object is returned by an expression or passed to the
420 display function, it will result in the data being displayed
420 display function, it will result in the data being displayed
421 in the frontend. If the data is a URL, the data will first be
421 in the frontend. If the data is a URL, the data will first be
422 downloaded and then displayed.
422 downloaded and then displayed.
423
423
424 In the Notebook, the containing element will be available as `element`,
424 In the Notebook, the containing element will be available as `element`,
425 and jQuery will be available. The output area starts hidden, so if
425 and jQuery will be available. The output area starts hidden, so if
426 the js appends content to `element` that should be visible, then
426 the js appends content to `element` that should be visible, then
427 it must call `container.show()` to unhide the area.
427 it must call `container.show()` to unhide the area.
428
428
429 Parameters
429 Parameters
430 ----------
430 ----------
431 data : unicode, str or bytes
431 data : unicode, str or bytes
432 The Javascript source code or a URL to download it from.
432 The Javascript source code or a URL to download it from.
433 url : unicode
433 url : unicode
434 A URL to download the data from.
434 A URL to download the data from.
435 filename : unicode
435 filename : unicode
436 Path to a local file to load the data from.
436 Path to a local file to load the data from.
437 lib : list or str
437 lib : list or str
438 A sequence of Javascript library URLs to load asynchronously before
438 A sequence of Javascript library URLs to load asynchronously before
439 running the source code. The full URLs of the libraries should
439 running the source code. The full URLs of the libraries should
440 be given. A single Javascript library URL can also be given as a
440 be given. A single Javascript library URL can also be given as a
441 string.
441 string.
442 css: : list or str
442 css: : list or str
443 A sequence of css files to load before running the source code.
443 A sequence of css files to load before running the source code.
444 The full URLs of the css files should be given. A single css URL
444 The full URLs of the css files should be given. A single css URL
445 can also be given as a string.
445 can also be given as a string.
446 """
446 """
447 if isinstance(lib, basestring):
447 if isinstance(lib, basestring):
448 lib = [lib]
448 lib = [lib]
449 elif lib is None:
449 elif lib is None:
450 lib = []
450 lib = []
451 if isinstance(css, basestring):
451 if isinstance(css, basestring):
452 css = [css]
452 css = [css]
453 elif css is None:
453 elif css is None:
454 css = []
454 css = []
455 if not isinstance(lib, (list,tuple)):
455 if not isinstance(lib, (list,tuple)):
456 raise TypeError('expected sequence, got: %r' % lib)
456 raise TypeError('expected sequence, got: %r' % lib)
457 if not isinstance(css, (list,tuple)):
457 if not isinstance(css, (list,tuple)):
458 raise TypeError('expected sequence, got: %r' % css)
458 raise TypeError('expected sequence, got: %r' % css)
459 self.lib = lib
459 self.lib = lib
460 self.css = css
460 self.css = css
461 super(Javascript, self).__init__(data=data, url=url, filename=filename)
461 super(Javascript, self).__init__(data=data, url=url, filename=filename)
462
462
463 def _repr_javascript_(self):
463 def _repr_javascript_(self):
464 r = ''
464 r = ''
465 for c in self.css:
465 for c in self.css:
466 r += css_t % c
466 r += css_t % c
467 for l in self.lib:
467 for l in self.lib:
468 r += lib_t1 % l
468 r += lib_t1 % l
469 r += self.data
469 r += self.data
470 r += lib_t2*len(self.lib)
470 r += lib_t2*len(self.lib)
471 return r
471 return r
472
472
473 # constants for identifying png/jpeg data
473 # constants for identifying png/jpeg data
474 _PNG = b'\x89PNG\r\n\x1a\n'
474 _PNG = b'\x89PNG\r\n\x1a\n'
475 _JPEG = b'\xff\xd8'
475 _JPEG = b'\xff\xd8'
476
476
477 def _pngxy(data):
477 def _pngxy(data):
478 """read the (width, height) from a PNG header"""
478 """read the (width, height) from a PNG header"""
479 ihdr = data.index(b'IHDR')
479 ihdr = data.index(b'IHDR')
480 # next 8 bytes are width/height
480 # next 8 bytes are width/height
481 w4h4 = data[ihdr+4:ihdr+12]
481 w4h4 = data[ihdr+4:ihdr+12]
482 return struct.unpack('>ii', w4h4)
482 return struct.unpack('>ii', w4h4)
483
483
484 def _jpegxy(data):
484 def _jpegxy(data):
485 """read the (width, height) from a JPEG header"""
485 """read the (width, height) from a JPEG header"""
486 # adapted from http://www.64lines.com/jpeg-width-height
486 # adapted from http://www.64lines.com/jpeg-width-height
487
487
488 idx = 4
488 idx = 4
489 while True:
489 while True:
490 block_size = struct.unpack('>H', data[idx:idx+2])[0]
490 block_size = struct.unpack('>H', data[idx:idx+2])[0]
491 idx = idx + block_size
491 idx = idx + block_size
492 if data[idx:idx+2] == b'\xFF\xC0':
492 if data[idx:idx+2] == b'\xFF\xC0':
493 # found Start of Frame
493 # found Start of Frame
494 iSOF = idx
494 iSOF = idx
495 break
495 break
496 else:
496 else:
497 # read another block
497 # read another block
498 idx += 2
498 idx += 2
499
499
500 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
500 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
501 return w, h
501 return w, h
502
502
503 class Image(DisplayObject):
503 class Image(DisplayObject):
504
504
505 _read_flags = 'rb'
505 _read_flags = 'rb'
506 _FMT_JPEG = u'jpeg'
506 _FMT_JPEG = u'jpeg'
507 _FMT_PNG = u'png'
507 _FMT_PNG = u'png'
508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
508 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
509
509
510 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
510 def __init__(self, data=None, url=None, filename=None, format=u'png', embed=None, width=None, height=None, retina=False):
511 """Create a PNG/JPEG image object given raw data.
511 """Create a PNG/JPEG image object given raw data.
512
512
513 When this object is returned by an input cell or passed to the
513 When this object is returned by an input cell or passed to the
514 display function, it will result in the image being displayed
514 display function, it will result in the image being displayed
515 in the frontend.
515 in the frontend.
516
516
517 Parameters
517 Parameters
518 ----------
518 ----------
519 data : unicode, str or bytes
519 data : unicode, str or bytes
520 The raw image data or a URL or filename to load the data from.
520 The raw image data or a URL or filename to load the data from.
521 This always results in embedded image data.
521 This always results in embedded image data.
522 url : unicode
522 url : unicode
523 A URL to download the data from. If you specify `url=`,
523 A URL to download the data from. If you specify `url=`,
524 the image data will not be embedded unless you also specify `embed=True`.
524 the image data will not be embedded unless you also specify `embed=True`.
525 filename : unicode
525 filename : unicode
526 Path to a local file to load the data from.
526 Path to a local file to load the data from.
527 Images from a file are always embedded.
527 Images from a file are always embedded.
528 format : unicode
528 format : unicode
529 The format of the image data (png/jpeg/jpg). If a filename or URL is given
529 The format of the image data (png/jpeg/jpg). If a filename or URL is given
530 for format will be inferred from the filename extension.
530 for format will be inferred from the filename extension.
531 embed : bool
531 embed : bool
532 Should the image data be embedded using a data URI (True) or be
532 Should the image data be embedded using a data URI (True) or be
533 loaded using an <img> tag. Set this to True if you want the image
533 loaded using an <img> tag. Set this to True if you want the image
534 to be viewable later with no internet connection in the notebook.
534 to be viewable later with no internet connection in the notebook.
535
535
536 Default is `True`, unless the keyword argument `url` is set, then
536 Default is `True`, unless the keyword argument `url` is set, then
537 default value is `False`.
537 default value is `False`.
538
538
539 Note that QtConsole is not able to display images if `embed` is set to `False`
539 Note that QtConsole is not able to display images if `embed` is set to `False`
540 width : int
540 width : int
541 Width to which to constrain the image in html
541 Width to which to constrain the image in html
542 height : int
542 height : int
543 Height to which to constrain the image in html
543 Height to which to constrain the image in html
544 retina : bool
544 retina : bool
545 Automatically set the width and height to half of the measured
545 Automatically set the width and height to half of the measured
546 width and height.
546 width and height.
547 This only works for embedded images because it reads the width/height
547 This only works for embedded images because it reads the width/height
548 from image data.
548 from image data.
549 For non-embedded images, you can just set the desired display width
549 For non-embedded images, you can just set the desired display width
550 and height directly.
550 and height directly.
551
551
552 Examples
552 Examples
553 --------
553 --------
554 # embedded image data, works in qtconsole and notebook
554 # embedded image data, works in qtconsole and notebook
555 # when passed positionally, the first arg can be any of raw image data,
555 # when passed positionally, the first arg can be any of raw image data,
556 # a URL, or a filename from which to load image data.
556 # a URL, or a filename from which to load image data.
557 # The result is always embedding image data for inline images.
557 # The result is always embedding image data for inline images.
558 Image('http://www.google.fr/images/srpr/logo3w.png')
558 Image('http://www.google.fr/images/srpr/logo3w.png')
559 Image('/path/to/image.jpg')
559 Image('/path/to/image.jpg')
560 Image(b'RAW_PNG_DATA...')
560 Image(b'RAW_PNG_DATA...')
561
561
562 # Specifying Image(url=...) does not embed the image data,
562 # Specifying Image(url=...) does not embed the image data,
563 # it only generates `<img>` tag with a link to the source.
563 # it only generates `<img>` tag with a link to the source.
564 # This will not work in the qtconsole or offline.
564 # This will not work in the qtconsole or offline.
565 Image(url='http://www.google.fr/images/srpr/logo3w.png')
565 Image(url='http://www.google.fr/images/srpr/logo3w.png')
566
566
567 """
567 """
568 if filename is not None:
568 if filename is not None:
569 ext = self._find_ext(filename)
569 ext = self._find_ext(filename)
570 elif url is not None:
570 elif url is not None:
571 ext = self._find_ext(url)
571 ext = self._find_ext(url)
572 elif data is None:
572 elif data is None:
573 raise ValueError("No image data found. Expecting filename, url, or data.")
573 raise ValueError("No image data found. Expecting filename, url, or data.")
574 elif isinstance(data, string_types) and (
574 elif isinstance(data, string_types) and (
575 data.startswith('http') or _safe_exists(data)
575 data.startswith('http') or _safe_exists(data)
576 ):
576 ):
577 ext = self._find_ext(data)
577 ext = self._find_ext(data)
578 else:
578 else:
579 ext = None
579 ext = None
580
580
581 if ext is not None:
581 if ext is not None:
582 format = ext.lower()
582 format = ext.lower()
583 if ext == u'jpg' or ext == u'jpeg':
583 if ext == u'jpg' or ext == u'jpeg':
584 format = self._FMT_JPEG
584 format = self._FMT_JPEG
585 if ext == u'png':
585 if ext == u'png':
586 format = self._FMT_PNG
586 format = self._FMT_PNG
587 elif isinstance(data, bytes) and format == 'png':
587 elif isinstance(data, bytes) and format == 'png':
588 # infer image type from image data header,
588 # infer image type from image data header,
589 # only if format might not have been specified.
589 # only if format might not have been specified.
590 if data[:2] == _JPEG:
590 if data[:2] == _JPEG:
591 format = 'jpeg'
591 format = 'jpeg'
592
592
593 self.format = unicode(format).lower()
593 self.format = unicode(format).lower()
594 self.embed = embed if embed is not None else (url is None)
594 self.embed = embed if embed is not None else (url is None)
595
595
596 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
596 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
597 raise ValueError("Cannot embed the '%s' image format" % (self.format))
597 raise ValueError("Cannot embed the '%s' image format" % (self.format))
598 self.width = width
598 self.width = width
599 self.height = height
599 self.height = height
600 self.retina = retina
600 self.retina = retina
601 super(Image, self).__init__(data=data, url=url, filename=filename)
601 super(Image, self).__init__(data=data, url=url, filename=filename)
602
602
603 if retina:
603 if retina:
604 self._retina_shape()
604 self._retina_shape()
605
605
606 def _retina_shape(self):
606 def _retina_shape(self):
607 """load pixel-doubled width and height from image data"""
607 """load pixel-doubled width and height from image data"""
608 if not self.embed:
608 if not self.embed:
609 return
609 return
610 if self.format == 'png':
610 if self.format == 'png':
611 w, h = _pngxy(self.data)
611 w, h = _pngxy(self.data)
612 elif self.format == 'jpeg':
612 elif self.format == 'jpeg':
613 w, h = _jpegxy(self.data)
613 w, h = _jpegxy(self.data)
614 else:
614 else:
615 # retina only supports png
615 # retina only supports png
616 return
616 return
617 self.width = w // 2
617 self.width = w // 2
618 self.height = h // 2
618 self.height = h // 2
619
619
620 def reload(self):
620 def reload(self):
621 """Reload the raw data from file or URL."""
621 """Reload the raw data from file or URL."""
622 if self.embed:
622 if self.embed:
623 super(Image,self).reload()
623 super(Image,self).reload()
624 if self.retina:
624 if self.retina:
625 self._retina_shape()
625 self._retina_shape()
626
626
627 def _repr_html_(self):
627 def _repr_html_(self):
628 if not self.embed:
628 if not self.embed:
629 width = height = ''
629 width = height = ''
630 if self.width:
630 if self.width:
631 width = ' width="%d"' % self.width
631 width = ' width="%d"' % self.width
632 if self.height:
632 if self.height:
633 height = ' height="%d"' % self.height
633 height = ' height="%d"' % self.height
634 return u'<img src="%s"%s%s/>' % (self.url, width, height)
634 return u'<img src="%s"%s%s/>' % (self.url, width, height)
635
635
636 def _data_and_metadata(self):
636 def _data_and_metadata(self):
637 """shortcut for returning metadata with shape information, if defined"""
637 """shortcut for returning metadata with shape information, if defined"""
638 md = {}
638 md = {}
639 if self.width:
639 if self.width:
640 md['width'] = self.width
640 md['width'] = self.width
641 if self.height:
641 if self.height:
642 md['height'] = self.height
642 md['height'] = self.height
643 if md:
643 if md:
644 return self.data, md
644 return self.data, md
645 else:
645 else:
646 return self.data
646 return self.data
647
647
648 def _repr_png_(self):
648 def _repr_png_(self):
649 if self.embed and self.format == u'png':
649 if self.embed and self.format == u'png':
650 return self._data_and_metadata()
650 return self._data_and_metadata()
651
651
652 def _repr_jpeg_(self):
652 def _repr_jpeg_(self):
653 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
653 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
654 return self._data_and_metadata()
654 return self._data_and_metadata()
655
655
656 def _find_ext(self, s):
656 def _find_ext(self, s):
657 return unicode(s.split('.')[-1].lower())
657 return unicode(s.split('.')[-1].lower())
658
658
659
659
660 def clear_output():
660 def clear_output(wait=False):
661 """Clear the output of the current cell receiving output."""
661 """Clear the output of the current cell receiving output.
662
663 Parameters
664 ----------
665 wait : bool [default: false]
666 Wait to clear the output until new output is available to replace it."""
662 from IPython.core.interactiveshell import InteractiveShell
667 from IPython.core.interactiveshell import InteractiveShell
663 if InteractiveShell.initialized():
668 if InteractiveShell.initialized():
664 InteractiveShell.instance().display_pub.clear_output()
669 InteractiveShell.instance().display_pub.clear_output(wait)
665 else:
670 else:
666 from IPython.utils import io
671 from IPython.utils import io
667 print('\033[2K\r', file=io.stdout, end='')
672 print('\033[2K\r', file=io.stdout, end='')
668 io.stdout.flush()
673 io.stdout.flush()
669 print('\033[2K\r', file=io.stderr, end='')
674 print('\033[2K\r', file=io.stderr, end='')
670 io.stderr.flush()
675 io.stderr.flush()
671
@@ -1,176 +1,176 b''
1 """An interface for publishing rich data to frontends.
1 """An interface for publishing rich data to frontends.
2
2
3 There are two components of the display system:
3 There are two components of the display system:
4
4
5 * Display formatters, which take a Python object and compute the
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVG, etc.).
6 representation of the object in various formats (text, HTML, SVG, etc.).
7 * The display publisher that is used to send the representation data to the
7 * The display publisher that is used to send the representation data to the
8 various frontends.
8 various frontends.
9
9
10 This module defines the logic display publishing. The display publisher uses
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
12 spec.
13
13
14 Authors:
14 Authors:
15
15
16 * Brian Granger
16 * Brian Granger
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2011 The IPython Development Team
20 # Copyright (C) 2008-2011 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 from __future__ import print_function
30 from __future__ import print_function
31
31
32 from IPython.config.configurable import Configurable
32 from IPython.config.configurable import Configurable
33 from IPython.utils import io
33 from IPython.utils import io
34 from IPython.utils.traitlets import List
34 from IPython.utils.traitlets import List
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 # Main payload class
37 # Main payload class
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40 class DisplayPublisher(Configurable):
40 class DisplayPublisher(Configurable):
41 """A traited class that publishes display data to frontends.
41 """A traited class that publishes display data to frontends.
42
42
43 Instances of this class are created by the main IPython object and should
43 Instances of this class are created by the main IPython object and should
44 be accessed there.
44 be accessed there.
45 """
45 """
46
46
47 def _validate_data(self, source, data, metadata=None):
47 def _validate_data(self, source, data, metadata=None):
48 """Validate the display data.
48 """Validate the display data.
49
49
50 Parameters
50 Parameters
51 ----------
51 ----------
52 source : str
52 source : str
53 The fully dotted name of the callable that created the data, like
53 The fully dotted name of the callable that created the data, like
54 :func:`foo.bar.my_formatter`.
54 :func:`foo.bar.my_formatter`.
55 data : dict
55 data : dict
56 The formata data dictionary.
56 The formata data dictionary.
57 metadata : dict
57 metadata : dict
58 Any metadata for the data.
58 Any metadata for the data.
59 """
59 """
60
60
61 if not isinstance(source, basestring):
61 if not isinstance(source, basestring):
62 raise TypeError('source must be a str, got: %r' % source)
62 raise TypeError('source must be a str, got: %r' % source)
63 if not isinstance(data, dict):
63 if not isinstance(data, dict):
64 raise TypeError('data must be a dict, got: %r' % data)
64 raise TypeError('data must be a dict, got: %r' % data)
65 if metadata is not None:
65 if metadata is not None:
66 if not isinstance(metadata, dict):
66 if not isinstance(metadata, dict):
67 raise TypeError('metadata must be a dict, got: %r' % data)
67 raise TypeError('metadata must be a dict, got: %r' % data)
68
68
69 def publish(self, source, data, metadata=None):
69 def publish(self, source, data, metadata=None):
70 """Publish data and metadata to all frontends.
70 """Publish data and metadata to all frontends.
71
71
72 See the ``display_data`` message in the messaging documentation for
72 See the ``display_data`` message in the messaging documentation for
73 more details about this message type.
73 more details about this message type.
74
74
75 The following MIME types are currently implemented:
75 The following MIME types are currently implemented:
76
76
77 * text/plain
77 * text/plain
78 * text/html
78 * text/html
79 * text/latex
79 * text/latex
80 * application/json
80 * application/json
81 * application/javascript
81 * application/javascript
82 * image/png
82 * image/png
83 * image/jpeg
83 * image/jpeg
84 * image/svg+xml
84 * image/svg+xml
85
85
86 Parameters
86 Parameters
87 ----------
87 ----------
88 source : str
88 source : str
89 A string that give the function or method that created the data,
89 A string that give the function or method that created the data,
90 such as 'IPython.core.page'.
90 such as 'IPython.core.page'.
91 data : dict
91 data : dict
92 A dictionary having keys that are valid MIME types (like
92 A dictionary having keys that are valid MIME types (like
93 'text/plain' or 'image/svg+xml') and values that are the data for
93 'text/plain' or 'image/svg+xml') and values that are the data for
94 that MIME type. The data itself must be a JSON'able data
94 that MIME type. The data itself must be a JSON'able data
95 structure. Minimally all data should have the 'text/plain' data,
95 structure. Minimally all data should have the 'text/plain' data,
96 which can be displayed by all frontends. If more than the plain
96 which can be displayed by all frontends. If more than the plain
97 text is given, it is up to the frontend to decide which
97 text is given, it is up to the frontend to decide which
98 representation to use.
98 representation to use.
99 metadata : dict
99 metadata : dict
100 A dictionary for metadata related to the data. This can contain
100 A dictionary for metadata related to the data. This can contain
101 arbitrary key, value pairs that frontends can use to interpret
101 arbitrary key, value pairs that frontends can use to interpret
102 the data. Metadata specific to each mime-type can be specified
102 the data. Metadata specific to each mime-type can be specified
103 in the metadata dict with the same mime-type keys as
103 in the metadata dict with the same mime-type keys as
104 the data itself.
104 the data itself.
105 """
105 """
106
106
107 # The default is to simply write the plain text data using io.stdout.
107 # The default is to simply write the plain text data using io.stdout.
108 if 'text/plain' in data:
108 if 'text/plain' in data:
109 print(data['text/plain'], file=io.stdout)
109 print(data['text/plain'], file=io.stdout)
110
110
111 def clear_output(self):
111 def clear_output(self, wait=False):
112 """Clear the output of the cell receiving output."""
112 """Clear the output of the cell receiving output."""
113 print('\033[2K\r', file=io.stdout, end='')
113 print('\033[2K\r', file=io.stdout, end='')
114 io.stdout.flush()
114 io.stdout.flush()
115 print('\033[2K\r', file=io.stderr, end='')
115 print('\033[2K\r', file=io.stderr, end='')
116 io.stderr.flush()
116 io.stderr.flush()
117
117
118
118
119 class CapturingDisplayPublisher(DisplayPublisher):
119 class CapturingDisplayPublisher(DisplayPublisher):
120 """A DisplayPublisher that stores"""
120 """A DisplayPublisher that stores"""
121 outputs = List()
121 outputs = List()
122
122
123 def publish(self, source, data, metadata=None):
123 def publish(self, source, data, metadata=None):
124 self.outputs.append((source, data, metadata))
124 self.outputs.append((source, data, metadata))
125
125
126 def clear_output(self):
126 def clear_output(self, wait=False):
127 super(CapturingDisplayPublisher, self).clear_output()
127 super(CapturingDisplayPublisher, self).clear_output(wait)
128 if other:
128 if other:
129 # empty the list, *do not* reassign a new list
129 # empty the list, *do not* reassign a new list
130 del self.outputs[:]
130 del self.outputs[:]
131
131
132
132
133 def publish_display_data(source, data, metadata=None):
133 def publish_display_data(source, data, metadata=None):
134 """Publish data and metadata to all frontends.
134 """Publish data and metadata to all frontends.
135
135
136 See the ``display_data`` message in the messaging documentation for
136 See the ``display_data`` message in the messaging documentation for
137 more details about this message type.
137 more details about this message type.
138
138
139 The following MIME types are currently implemented:
139 The following MIME types are currently implemented:
140
140
141 * text/plain
141 * text/plain
142 * text/html
142 * text/html
143 * text/latex
143 * text/latex
144 * application/json
144 * application/json
145 * application/javascript
145 * application/javascript
146 * image/png
146 * image/png
147 * image/jpeg
147 * image/jpeg
148 * image/svg+xml
148 * image/svg+xml
149
149
150 Parameters
150 Parameters
151 ----------
151 ----------
152 source : str
152 source : str
153 A string that give the function or method that created the data,
153 A string that give the function or method that created the data,
154 such as 'IPython.core.page'.
154 such as 'IPython.core.page'.
155 data : dict
155 data : dict
156 A dictionary having keys that are valid MIME types (like
156 A dictionary having keys that are valid MIME types (like
157 'text/plain' or 'image/svg+xml') and values that are the data for
157 'text/plain' or 'image/svg+xml') and values that are the data for
158 that MIME type. The data itself must be a JSON'able data
158 that MIME type. The data itself must be a JSON'able data
159 structure. Minimally all data should have the 'text/plain' data,
159 structure. Minimally all data should have the 'text/plain' data,
160 which can be displayed by all frontends. If more than the plain
160 which can be displayed by all frontends. If more than the plain
161 text is given, it is up to the frontend to decide which
161 text is given, it is up to the frontend to decide which
162 representation to use.
162 representation to use.
163 metadata : dict
163 metadata : dict
164 A dictionary for metadata related to the data. This can contain
164 A dictionary for metadata related to the data. This can contain
165 arbitrary key, value pairs that frontends can use to interpret
165 arbitrary key, value pairs that frontends can use to interpret
166 the data. mime-type keys matching those in data can be used
166 the data. mime-type keys matching those in data can be used
167 to specify metadata about particular representations.
167 to specify metadata about particular representations.
168 """
168 """
169 from IPython.core.interactiveshell import InteractiveShell
169 from IPython.core.interactiveshell import InteractiveShell
170 InteractiveShell.instance().display_pub.publish(
170 InteractiveShell.instance().display_pub.publish(
171 source,
171 source,
172 data,
172 data,
173 metadata
173 metadata
174 )
174 )
175
175
176
176
@@ -1,441 +1,441 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * An extendable module that provide base functionnality to create cell for notebook.
12 * An extendable module that provide base functionnality to create cell for notebook.
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule CodeCell
15 * @submodule CodeCell
16 */
16 */
17
17
18
18
19 /* local util for codemirror */
19 /* local util for codemirror */
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
21
21
22 /**
22 /**
23 *
23 *
24 * function to delete until previous non blanking space character
24 * function to delete until previous non blanking space character
25 * or first multiple of 4 tabstop.
25 * or first multiple of 4 tabstop.
26 * @private
26 * @private
27 */
27 */
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
32 var tabsize = cm.getOption('tabSize');
32 var tabsize = cm.getOption('tabSize');
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
35 var select = cm.getRange(from,cur)
35 var select = cm.getRange(from,cur)
36 if( select.match(/^\ +$/) != null){
36 if( select.match(/^\ +$/) != null){
37 cm.replaceRange("",from,cur)
37 cm.replaceRange("",from,cur)
38 } else {
38 } else {
39 cm.deleteH(-1,"char")
39 cm.deleteH(-1,"char")
40 }
40 }
41 };
41 };
42
42
43
43
44 var IPython = (function (IPython) {
44 var IPython = (function (IPython) {
45 "use strict";
45 "use strict";
46
46
47 var utils = IPython.utils;
47 var utils = IPython.utils;
48 var key = IPython.utils.keycodes;
48 var key = IPython.utils.keycodes;
49
49
50 /**
50 /**
51 * A Cell conceived to write code.
51 * A Cell conceived to write code.
52 *
52 *
53 * The kernel doesn't have to be set at creation time, in that case
53 * The kernel doesn't have to be set at creation time, in that case
54 * it will be null and set_kernel has to be called later.
54 * it will be null and set_kernel has to be called later.
55 * @class CodeCell
55 * @class CodeCell
56 * @extends IPython.Cell
56 * @extends IPython.Cell
57 *
57 *
58 * @constructor
58 * @constructor
59 * @param {Object|null} kernel
59 * @param {Object|null} kernel
60 * @param {object|undefined} [options]
60 * @param {object|undefined} [options]
61 * @param [options.cm_config] {object} config to pass to CodeMirror
61 * @param [options.cm_config] {object} config to pass to CodeMirror
62 */
62 */
63 var CodeCell = function (kernel, options) {
63 var CodeCell = function (kernel, options) {
64 this.kernel = kernel || null;
64 this.kernel = kernel || null;
65 this.code_mirror = null;
65 this.code_mirror = null;
66 this.input_prompt_number = null;
66 this.input_prompt_number = null;
67 this.collapsed = false;
67 this.collapsed = false;
68 this.cell_type = "code";
68 this.cell_type = "code";
69
69
70
70
71 var cm_overwrite_options = {
71 var cm_overwrite_options = {
72 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
72 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
73 };
73 };
74
74
75 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
75 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
76
76
77 IPython.Cell.apply(this,[options]);
77 IPython.Cell.apply(this,[options]);
78
78
79 var that = this;
79 var that = this;
80 this.element.focusout(
80 this.element.focusout(
81 function() { that.auto_highlight(); }
81 function() { that.auto_highlight(); }
82 );
82 );
83 };
83 };
84
84
85 CodeCell.options_default = {
85 CodeCell.options_default = {
86 cm_config : {
86 cm_config : {
87 extraKeys: {
87 extraKeys: {
88 "Tab" : "indentMore",
88 "Tab" : "indentMore",
89 "Shift-Tab" : "indentLess",
89 "Shift-Tab" : "indentLess",
90 "Backspace" : "delSpaceToPrevTabStop",
90 "Backspace" : "delSpaceToPrevTabStop",
91 "Cmd-/" : "toggleComment",
91 "Cmd-/" : "toggleComment",
92 "Ctrl-/" : "toggleComment"
92 "Ctrl-/" : "toggleComment"
93 },
93 },
94 mode: 'ipython',
94 mode: 'ipython',
95 theme: 'ipython',
95 theme: 'ipython',
96 matchBrackets: true
96 matchBrackets: true
97 }
97 }
98 };
98 };
99
99
100
100
101 CodeCell.prototype = new IPython.Cell();
101 CodeCell.prototype = new IPython.Cell();
102
102
103 /**
103 /**
104 * @method auto_highlight
104 * @method auto_highlight
105 */
105 */
106 CodeCell.prototype.auto_highlight = function () {
106 CodeCell.prototype.auto_highlight = function () {
107 this._auto_highlight(IPython.config.cell_magic_highlight)
107 this._auto_highlight(IPython.config.cell_magic_highlight)
108 };
108 };
109
109
110 /** @method create_element */
110 /** @method create_element */
111 CodeCell.prototype.create_element = function () {
111 CodeCell.prototype.create_element = function () {
112 IPython.Cell.prototype.create_element.apply(this, arguments);
112 IPython.Cell.prototype.create_element.apply(this, arguments);
113
113
114 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
114 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
115 cell.attr('tabindex','2');
115 cell.attr('tabindex','2');
116
116
117 this.celltoolbar = new IPython.CellToolbar(this);
117 this.celltoolbar = new IPython.CellToolbar(this);
118
118
119 var input = $('<div></div>').addClass('input');
119 var input = $('<div></div>').addClass('input');
120 var vbox = $('<div/>').addClass('vbox box-flex1')
120 var vbox = $('<div/>').addClass('vbox box-flex1')
121 input.append($('<div/>').addClass('prompt input_prompt'));
121 input.append($('<div/>').addClass('prompt input_prompt'));
122 vbox.append(this.celltoolbar.element);
122 vbox.append(this.celltoolbar.element);
123 var input_area = $('<div/>').addClass('input_area');
123 var input_area = $('<div/>').addClass('input_area');
124 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
124 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
125 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
125 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
126 vbox.append(input_area);
126 vbox.append(input_area);
127 input.append(vbox);
127 input.append(vbox);
128 var output = $('<div></div>');
128 var output = $('<div></div>');
129 cell.append(input).append(output);
129 cell.append(input).append(output);
130 this.element = cell;
130 this.element = cell;
131 this.output_area = new IPython.OutputArea(output, true);
131 this.output_area = new IPython.OutputArea(output, true);
132
132
133 // construct a completer only if class exist
133 // construct a completer only if class exist
134 // otherwise no print view
134 // otherwise no print view
135 if (IPython.Completer !== undefined)
135 if (IPython.Completer !== undefined)
136 {
136 {
137 this.completer = new IPython.Completer(this);
137 this.completer = new IPython.Completer(this);
138 }
138 }
139 };
139 };
140
140
141 /**
141 /**
142 * This method gets called in CodeMirror's onKeyDown/onKeyPress
142 * This method gets called in CodeMirror's onKeyDown/onKeyPress
143 * handlers and is used to provide custom key handling. Its return
143 * handlers and is used to provide custom key handling. Its return
144 * value is used to determine if CodeMirror should ignore the event:
144 * value is used to determine if CodeMirror should ignore the event:
145 * true = ignore, false = don't ignore.
145 * true = ignore, false = don't ignore.
146 * @method handle_codemirror_keyevent
146 * @method handle_codemirror_keyevent
147 */
147 */
148 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
148 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
149
149
150 var that = this;
150 var that = this;
151 // whatever key is pressed, first, cancel the tooltip request before
151 // whatever key is pressed, first, cancel the tooltip request before
152 // they are sent, and remove tooltip if any, except for tab again
152 // they are sent, and remove tooltip if any, except for tab again
153 if (event.type === 'keydown' && event.which != key.TAB ) {
153 if (event.type === 'keydown' && event.which != key.TAB ) {
154 IPython.tooltip.remove_and_cancel_tooltip();
154 IPython.tooltip.remove_and_cancel_tooltip();
155 };
155 };
156
156
157 var cur = editor.getCursor();
157 var cur = editor.getCursor();
158 if (event.keyCode === key.ENTER){
158 if (event.keyCode === key.ENTER){
159 this.auto_highlight();
159 this.auto_highlight();
160 }
160 }
161
161
162 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
162 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
163 // Always ignore shift-enter in CodeMirror as we handle it.
163 // Always ignore shift-enter in CodeMirror as we handle it.
164 return true;
164 return true;
165 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
165 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
166 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
166 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
167 // browser and keyboard layout !
167 // browser and keyboard layout !
168 // Pressing '(' , request tooltip, don't forget to reappend it
168 // Pressing '(' , request tooltip, don't forget to reappend it
169 // The second argument says to hide the tooltip if the docstring
169 // The second argument says to hide the tooltip if the docstring
170 // is actually empty
170 // is actually empty
171 IPython.tooltip.pending(that, true);
171 IPython.tooltip.pending(that, true);
172 } else if (event.which === key.UPARROW && event.type === 'keydown') {
172 } else if (event.which === key.UPARROW && event.type === 'keydown') {
173 // If we are not at the top, let CM handle the up arrow and
173 // If we are not at the top, let CM handle the up arrow and
174 // prevent the global keydown handler from handling it.
174 // prevent the global keydown handler from handling it.
175 if (!that.at_top()) {
175 if (!that.at_top()) {
176 event.stop();
176 event.stop();
177 return false;
177 return false;
178 } else {
178 } else {
179 return true;
179 return true;
180 };
180 };
181 } else if (event.which === key.ESC) {
181 } else if (event.which === key.ESC) {
182 return IPython.tooltip.remove_and_cancel_tooltip(true);
182 return IPython.tooltip.remove_and_cancel_tooltip(true);
183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
184 // If we are not at the bottom, let CM handle the down arrow and
184 // If we are not at the bottom, let CM handle the down arrow and
185 // prevent the global keydown handler from handling it.
185 // prevent the global keydown handler from handling it.
186 if (!that.at_bottom()) {
186 if (!that.at_bottom()) {
187 event.stop();
187 event.stop();
188 return false;
188 return false;
189 } else {
189 } else {
190 return true;
190 return true;
191 };
191 };
192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
193 if (editor.somethingSelected()){
193 if (editor.somethingSelected()){
194 var anchor = editor.getCursor("anchor");
194 var anchor = editor.getCursor("anchor");
195 var head = editor.getCursor("head");
195 var head = editor.getCursor("head");
196 if( anchor.line != head.line){
196 if( anchor.line != head.line){
197 return false;
197 return false;
198 }
198 }
199 }
199 }
200 IPython.tooltip.request(that);
200 IPython.tooltip.request(that);
201 event.stop();
201 event.stop();
202 return true;
202 return true;
203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
204 // Tab completion.
204 // Tab completion.
205 //Do not trim here because of tooltip
205 //Do not trim here because of tooltip
206 if (editor.somethingSelected()){return false}
206 if (editor.somethingSelected()){return false}
207 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
207 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
208 if (pre_cursor.trim() === "") {
208 if (pre_cursor.trim() === "") {
209 // Don't autocomplete if the part of the line before the cursor
209 // Don't autocomplete if the part of the line before the cursor
210 // is empty. In this case, let CodeMirror handle indentation.
210 // is empty. In this case, let CodeMirror handle indentation.
211 return false;
211 return false;
212 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
212 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
213 IPython.tooltip.request(that);
213 IPython.tooltip.request(that);
214 // Prevent the event from bubbling up.
214 // Prevent the event from bubbling up.
215 event.stop();
215 event.stop();
216 // Prevent CodeMirror from handling the tab.
216 // Prevent CodeMirror from handling the tab.
217 return true;
217 return true;
218 } else {
218 } else {
219 event.stop();
219 event.stop();
220 this.completer.startCompletion();
220 this.completer.startCompletion();
221 return true;
221 return true;
222 };
222 };
223 } else {
223 } else {
224 // keypress/keyup also trigger on TAB press, and we don't want to
224 // keypress/keyup also trigger on TAB press, and we don't want to
225 // use those to disable tab completion.
225 // use those to disable tab completion.
226 return false;
226 return false;
227 };
227 };
228 return false;
228 return false;
229 };
229 };
230
230
231
231
232 // Kernel related calls.
232 // Kernel related calls.
233
233
234 CodeCell.prototype.set_kernel = function (kernel) {
234 CodeCell.prototype.set_kernel = function (kernel) {
235 this.kernel = kernel;
235 this.kernel = kernel;
236 }
236 }
237
237
238 /**
238 /**
239 * Execute current code cell to the kernel
239 * Execute current code cell to the kernel
240 * @method execute
240 * @method execute
241 */
241 */
242 CodeCell.prototype.execute = function () {
242 CodeCell.prototype.execute = function () {
243 this.output_area.clear_output();
243 this.output_area.clear_output();
244 this.set_input_prompt('*');
244 this.set_input_prompt('*');
245 this.element.addClass("running");
245 this.element.addClass("running");
246 var callbacks = {
246 var callbacks = {
247 'execute_reply': $.proxy(this._handle_execute_reply, this),
247 'execute_reply': $.proxy(this._handle_execute_reply, this),
248 'output': $.proxy(this.output_area.handle_output, this.output_area),
248 'output': $.proxy(this.output_area.handle_output, this.output_area),
249 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
249 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
250 'set_next_input': $.proxy(this._handle_set_next_input, this),
250 'set_next_input': $.proxy(this._handle_set_next_input, this),
251 'input_request': $.proxy(this._handle_input_request, this)
251 'input_request': $.proxy(this._handle_input_request, this)
252 };
252 };
253 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
253 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
254 };
254 };
255
255
256 /**
256 /**
257 * @method _handle_execute_reply
257 * @method _handle_execute_reply
258 * @private
258 * @private
259 */
259 */
260 CodeCell.prototype._handle_execute_reply = function (content) {
260 CodeCell.prototype._handle_execute_reply = function (content) {
261 this.set_input_prompt(content.execution_count);
261 this.set_input_prompt(content.execution_count);
262 this.element.removeClass("running");
262 this.element.removeClass("running");
263 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
263 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
264 }
264 }
265
265
266 /**
266 /**
267 * @method _handle_set_next_input
267 * @method _handle_set_next_input
268 * @private
268 * @private
269 */
269 */
270 CodeCell.prototype._handle_set_next_input = function (text) {
270 CodeCell.prototype._handle_set_next_input = function (text) {
271 var data = {'cell': this, 'text': text}
271 var data = {'cell': this, 'text': text}
272 $([IPython.events]).trigger('set_next_input.Notebook', data);
272 $([IPython.events]).trigger('set_next_input.Notebook', data);
273 }
273 }
274
274
275 /**
275 /**
276 * @method _handle_input_request
276 * @method _handle_input_request
277 * @private
277 * @private
278 */
278 */
279 CodeCell.prototype._handle_input_request = function (content) {
279 CodeCell.prototype._handle_input_request = function (content) {
280 this.output_area.append_raw_input(content);
280 this.output_area.append_raw_input(content);
281 }
281 }
282
282
283
283
284 // Basic cell manipulation.
284 // Basic cell manipulation.
285
285
286 CodeCell.prototype.select = function () {
286 CodeCell.prototype.select = function () {
287 IPython.Cell.prototype.select.apply(this);
287 IPython.Cell.prototype.select.apply(this);
288 this.code_mirror.refresh();
288 this.code_mirror.refresh();
289 this.code_mirror.focus();
289 this.code_mirror.focus();
290 this.auto_highlight();
290 this.auto_highlight();
291 // We used to need an additional refresh() after the focus, but
291 // We used to need an additional refresh() after the focus, but
292 // it appears that this has been fixed in CM. This bug would show
292 // it appears that this has been fixed in CM. This bug would show
293 // up on FF when a newly loaded markdown cell was edited.
293 // up on FF when a newly loaded markdown cell was edited.
294 };
294 };
295
295
296
296
297 CodeCell.prototype.select_all = function () {
297 CodeCell.prototype.select_all = function () {
298 var start = {line: 0, ch: 0};
298 var start = {line: 0, ch: 0};
299 var nlines = this.code_mirror.lineCount();
299 var nlines = this.code_mirror.lineCount();
300 var last_line = this.code_mirror.getLine(nlines-1);
300 var last_line = this.code_mirror.getLine(nlines-1);
301 var end = {line: nlines-1, ch: last_line.length};
301 var end = {line: nlines-1, ch: last_line.length};
302 this.code_mirror.setSelection(start, end);
302 this.code_mirror.setSelection(start, end);
303 };
303 };
304
304
305
305
306 CodeCell.prototype.collapse = function () {
306 CodeCell.prototype.collapse = function () {
307 this.collapsed = true;
307 this.collapsed = true;
308 this.output_area.collapse();
308 this.output_area.collapse();
309 };
309 };
310
310
311
311
312 CodeCell.prototype.expand = function () {
312 CodeCell.prototype.expand = function () {
313 this.collapsed = false;
313 this.collapsed = false;
314 this.output_area.expand();
314 this.output_area.expand();
315 };
315 };
316
316
317
317
318 CodeCell.prototype.toggle_output = function () {
318 CodeCell.prototype.toggle_output = function () {
319 this.collapsed = Boolean(1 - this.collapsed);
319 this.collapsed = Boolean(1 - this.collapsed);
320 this.output_area.toggle_output();
320 this.output_area.toggle_output();
321 };
321 };
322
322
323
323
324 CodeCell.prototype.toggle_output_scroll = function () {
324 CodeCell.prototype.toggle_output_scroll = function () {
325 this.output_area.toggle_scroll();
325 this.output_area.toggle_scroll();
326 };
326 };
327
327
328
328
329 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
329 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
330 var ns = prompt_value || "&nbsp;";
330 var ns = prompt_value || "&nbsp;";
331 return 'In&nbsp;[' + ns + ']:'
331 return 'In&nbsp;[' + ns + ']:'
332 };
332 };
333
333
334 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
334 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
335 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
335 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
336 for(var i=1; i < lines_number; i++){html.push(['...:'])};
336 for(var i=1; i < lines_number; i++){html.push(['...:'])};
337 return html.join('</br>')
337 return html.join('</br>')
338 };
338 };
339
339
340 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
340 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
341
341
342
342
343 CodeCell.prototype.set_input_prompt = function (number) {
343 CodeCell.prototype.set_input_prompt = function (number) {
344 var nline = 1
344 var nline = 1
345 if( this.code_mirror != undefined) {
345 if( this.code_mirror != undefined) {
346 nline = this.code_mirror.lineCount();
346 nline = this.code_mirror.lineCount();
347 }
347 }
348 this.input_prompt_number = number;
348 this.input_prompt_number = number;
349 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
349 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
350 this.element.find('div.input_prompt').html(prompt_html);
350 this.element.find('div.input_prompt').html(prompt_html);
351 };
351 };
352
352
353
353
354 CodeCell.prototype.clear_input = function () {
354 CodeCell.prototype.clear_input = function () {
355 this.code_mirror.setValue('');
355 this.code_mirror.setValue('');
356 };
356 };
357
357
358
358
359 CodeCell.prototype.get_text = function () {
359 CodeCell.prototype.get_text = function () {
360 return this.code_mirror.getValue();
360 return this.code_mirror.getValue();
361 };
361 };
362
362
363
363
364 CodeCell.prototype.set_text = function (code) {
364 CodeCell.prototype.set_text = function (code) {
365 return this.code_mirror.setValue(code);
365 return this.code_mirror.setValue(code);
366 };
366 };
367
367
368
368
369 CodeCell.prototype.at_top = function () {
369 CodeCell.prototype.at_top = function () {
370 var cursor = this.code_mirror.getCursor();
370 var cursor = this.code_mirror.getCursor();
371 if (cursor.line === 0 && cursor.ch === 0) {
371 if (cursor.line === 0 && cursor.ch === 0) {
372 return true;
372 return true;
373 } else {
373 } else {
374 return false;
374 return false;
375 }
375 }
376 };
376 };
377
377
378
378
379 CodeCell.prototype.at_bottom = function () {
379 CodeCell.prototype.at_bottom = function () {
380 var cursor = this.code_mirror.getCursor();
380 var cursor = this.code_mirror.getCursor();
381 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
381 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
382 return true;
382 return true;
383 } else {
383 } else {
384 return false;
384 return false;
385 }
385 }
386 };
386 };
387
387
388
388
389 CodeCell.prototype.clear_output = function () {
389 CodeCell.prototype.clear_output = function (wait) {
390 this.output_area.clear_output();
390 this.output_area.clear_output(wait);
391 };
391 };
392
392
393
393
394 // JSON serialization
394 // JSON serialization
395
395
396 CodeCell.prototype.fromJSON = function (data) {
396 CodeCell.prototype.fromJSON = function (data) {
397 IPython.Cell.prototype.fromJSON.apply(this, arguments);
397 IPython.Cell.prototype.fromJSON.apply(this, arguments);
398 if (data.cell_type === 'code') {
398 if (data.cell_type === 'code') {
399 if (data.input !== undefined) {
399 if (data.input !== undefined) {
400 this.set_text(data.input);
400 this.set_text(data.input);
401 // make this value the starting point, so that we can only undo
401 // make this value the starting point, so that we can only undo
402 // to this state, instead of a blank cell
402 // to this state, instead of a blank cell
403 this.code_mirror.clearHistory();
403 this.code_mirror.clearHistory();
404 this.auto_highlight();
404 this.auto_highlight();
405 }
405 }
406 if (data.prompt_number !== undefined) {
406 if (data.prompt_number !== undefined) {
407 this.set_input_prompt(data.prompt_number);
407 this.set_input_prompt(data.prompt_number);
408 } else {
408 } else {
409 this.set_input_prompt();
409 this.set_input_prompt();
410 };
410 };
411 this.output_area.fromJSON(data.outputs);
411 this.output_area.fromJSON(data.outputs);
412 if (data.collapsed !== undefined) {
412 if (data.collapsed !== undefined) {
413 if (data.collapsed) {
413 if (data.collapsed) {
414 this.collapse();
414 this.collapse();
415 } else {
415 } else {
416 this.expand();
416 this.expand();
417 };
417 };
418 };
418 };
419 };
419 };
420 };
420 };
421
421
422
422
423 CodeCell.prototype.toJSON = function () {
423 CodeCell.prototype.toJSON = function () {
424 var data = IPython.Cell.prototype.toJSON.apply(this);
424 var data = IPython.Cell.prototype.toJSON.apply(this);
425 data.input = this.get_text();
425 data.input = this.get_text();
426 data.cell_type = 'code';
426 data.cell_type = 'code';
427 if (this.input_prompt_number) {
427 if (this.input_prompt_number) {
428 data.prompt_number = this.input_prompt_number;
428 data.prompt_number = this.input_prompt_number;
429 };
429 };
430 var outputs = this.output_area.toJSON();
430 var outputs = this.output_area.toJSON();
431 data.outputs = outputs;
431 data.outputs = outputs;
432 data.language = 'python';
432 data.language = 'python';
433 data.collapsed = this.collapsed;
433 data.collapsed = this.collapsed;
434 return data;
434 return data;
435 };
435 };
436
436
437
437
438 IPython.CodeCell = CodeCell;
438 IPython.CodeCell = CodeCell;
439
439
440 return IPython;
440 return IPython;
441 }(IPython));
441 }(IPython));
@@ -1,651 +1,668 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008 The IPython Development Team
2 // Copyright (C) 2008 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // OutputArea
9 // OutputArea
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule OutputArea
15 * @submodule OutputArea
16 */
16 */
17 var IPython = (function (IPython) {
17 var IPython = (function (IPython) {
18 "use strict";
18 "use strict";
19
19
20 var utils = IPython.utils;
20 var utils = IPython.utils;
21
21
22 /**
22 /**
23 * @class OutputArea
23 * @class OutputArea
24 *
24 *
25 * @constructor
25 * @constructor
26 */
26 */
27
27
28 var OutputArea = function (selector, prompt_area) {
28 var OutputArea = function (selector, prompt_area) {
29 this.selector = selector;
29 this.selector = selector;
30 this.wrapper = $(selector);
30 this.wrapper = $(selector);
31 this.outputs = [];
31 this.outputs = [];
32 this.collapsed = false;
32 this.collapsed = false;
33 this.scrolled = false;
33 this.scrolled = false;
34 this.clear_out_timeout = null;
34 this.clear_queued = null;
35 if (prompt_area === undefined) {
35 if (prompt_area === undefined) {
36 this.prompt_area = true;
36 this.prompt_area = true;
37 } else {
37 } else {
38 this.prompt_area = prompt_area;
38 this.prompt_area = prompt_area;
39 }
39 }
40 this.create_elements();
40 this.create_elements();
41 this.style();
41 this.style();
42 this.bind_events();
42 this.bind_events();
43 };
43 };
44
44
45 OutputArea.prototype.create_elements = function () {
45 OutputArea.prototype.create_elements = function () {
46 this.element = $("<div/>");
46 this.element = $("<div/>");
47 this.collapse_button = $("<div/>");
47 this.collapse_button = $("<div/>");
48 this.prompt_overlay = $("<div/>");
48 this.prompt_overlay = $("<div/>");
49 this.wrapper.append(this.prompt_overlay);
49 this.wrapper.append(this.prompt_overlay);
50 this.wrapper.append(this.element);
50 this.wrapper.append(this.element);
51 this.wrapper.append(this.collapse_button);
51 this.wrapper.append(this.collapse_button);
52 };
52 };
53
53
54
54
55 OutputArea.prototype.style = function () {
55 OutputArea.prototype.style = function () {
56 this.collapse_button.hide();
56 this.collapse_button.hide();
57 this.prompt_overlay.hide();
57 this.prompt_overlay.hide();
58
58
59 this.wrapper.addClass('output_wrapper');
59 this.wrapper.addClass('output_wrapper');
60 this.element.addClass('output vbox');
60 this.element.addClass('output vbox');
61
61
62 this.collapse_button.addClass("btn output_collapsed");
62 this.collapse_button.addClass("btn output_collapsed");
63 this.collapse_button.attr('title', 'click to expand output');
63 this.collapse_button.attr('title', 'click to expand output');
64 this.collapse_button.html('. . .');
64 this.collapse_button.html('. . .');
65
65
66 this.prompt_overlay.addClass('out_prompt_overlay prompt');
66 this.prompt_overlay.addClass('out_prompt_overlay prompt');
67 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
67 this.prompt_overlay.attr('title', 'click to expand output; double click to hide output');
68
68
69 this.collapse();
69 this.collapse();
70 };
70 };
71
71
72 /**
72 /**
73 * Should the OutputArea scroll?
73 * Should the OutputArea scroll?
74 * Returns whether the height (in lines) exceeds a threshold.
74 * Returns whether the height (in lines) exceeds a threshold.
75 *
75 *
76 * @private
76 * @private
77 * @method _should_scroll
77 * @method _should_scroll
78 * @param [lines=100]{Integer}
78 * @param [lines=100]{Integer}
79 * @return {Bool}
79 * @return {Bool}
80 *
80 *
81 */
81 */
82 OutputArea.prototype._should_scroll = function (lines) {
82 OutputArea.prototype._should_scroll = function (lines) {
83 if (lines <=0 ){ return }
83 if (lines <=0 ){ return }
84 if (!lines) {
84 if (!lines) {
85 lines = 100;
85 lines = 100;
86 }
86 }
87 // line-height from http://stackoverflow.com/questions/1185151
87 // line-height from http://stackoverflow.com/questions/1185151
88 var fontSize = this.element.css('font-size');
88 var fontSize = this.element.css('font-size');
89 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
89 var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
90
90
91 return (this.element.height() > lines * lineHeight);
91 return (this.element.height() > lines * lineHeight);
92 };
92 };
93
93
94
94
95 OutputArea.prototype.bind_events = function () {
95 OutputArea.prototype.bind_events = function () {
96 var that = this;
96 var that = this;
97 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
97 this.prompt_overlay.dblclick(function () { that.toggle_output(); });
98 this.prompt_overlay.click(function () { that.toggle_scroll(); });
98 this.prompt_overlay.click(function () { that.toggle_scroll(); });
99
99
100 this.element.resize(function () {
100 this.element.resize(function () {
101 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
101 // FIXME: Firefox on Linux misbehaves, so automatic scrolling is disabled
102 if ( IPython.utils.browser[0] === "Firefox" ) {
102 if ( IPython.utils.browser[0] === "Firefox" ) {
103 return;
103 return;
104 }
104 }
105 // maybe scroll output,
105 // maybe scroll output,
106 // if it's grown large enough and hasn't already been scrolled.
106 // if it's grown large enough and hasn't already been scrolled.
107 if ( !that.scrolled && that._should_scroll(OutputArea.auto_scroll_threshold)) {
107 if ( !that.scrolled && that._should_scroll(OutputArea.auto_scroll_threshold)) {
108 that.scroll_area();
108 that.scroll_area();
109 }
109 }
110 });
110 });
111 this.collapse_button.click(function () {
111 this.collapse_button.click(function () {
112 that.expand();
112 that.expand();
113 });
113 });
114 };
114 };
115
115
116
116
117 OutputArea.prototype.collapse = function () {
117 OutputArea.prototype.collapse = function () {
118 if (!this.collapsed) {
118 if (!this.collapsed) {
119 this.element.hide();
119 this.element.hide();
120 this.prompt_overlay.hide();
120 this.prompt_overlay.hide();
121 if (this.element.html()){
121 if (this.element.html()){
122 this.collapse_button.show();
122 this.collapse_button.show();
123 }
123 }
124 this.collapsed = true;
124 this.collapsed = true;
125 }
125 }
126 };
126 };
127
127
128
128
129 OutputArea.prototype.expand = function () {
129 OutputArea.prototype.expand = function () {
130 if (this.collapsed) {
130 if (this.collapsed) {
131 this.collapse_button.hide();
131 this.collapse_button.hide();
132 this.element.show();
132 this.element.show();
133 this.prompt_overlay.show();
133 this.prompt_overlay.show();
134 this.collapsed = false;
134 this.collapsed = false;
135 }
135 }
136 };
136 };
137
137
138
138
139 OutputArea.prototype.toggle_output = function () {
139 OutputArea.prototype.toggle_output = function () {
140 if (this.collapsed) {
140 if (this.collapsed) {
141 this.expand();
141 this.expand();
142 } else {
142 } else {
143 this.collapse();
143 this.collapse();
144 }
144 }
145 };
145 };
146
146
147
147
148 OutputArea.prototype.scroll_area = function () {
148 OutputArea.prototype.scroll_area = function () {
149 this.element.addClass('output_scroll');
149 this.element.addClass('output_scroll');
150 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
150 this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
151 this.scrolled = true;
151 this.scrolled = true;
152 };
152 };
153
153
154
154
155 OutputArea.prototype.unscroll_area = function () {
155 OutputArea.prototype.unscroll_area = function () {
156 this.element.removeClass('output_scroll');
156 this.element.removeClass('output_scroll');
157 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
157 this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
158 this.scrolled = false;
158 this.scrolled = false;
159 };
159 };
160
160
161 /**
161 /**
162 * Threshold to trigger autoscroll when the OutputArea is resized,
162 * Threshold to trigger autoscroll when the OutputArea is resized,
163 * typically when new outputs are added.
163 * typically when new outputs are added.
164 *
164 *
165 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
165 * Behavior is undefined if autoscroll is lower than minimum_scroll_threshold,
166 * unless it is < 0, in which case autoscroll will never be triggered
166 * unless it is < 0, in which case autoscroll will never be triggered
167 *
167 *
168 * @property auto_scroll_threshold
168 * @property auto_scroll_threshold
169 * @type Number
169 * @type Number
170 * @default 100
170 * @default 100
171 *
171 *
172 **/
172 **/
173 OutputArea.auto_scroll_threshold = 100;
173 OutputArea.auto_scroll_threshold = 100;
174
174
175
175
176 /**
176 /**
177 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
177 * Lower limit (in lines) for OutputArea to be made scrollable. OutputAreas
178 * shorter than this are never scrolled.
178 * shorter than this are never scrolled.
179 *
179 *
180 * @property minimum_scroll_threshold
180 * @property minimum_scroll_threshold
181 * @type Number
181 * @type Number
182 * @default 20
182 * @default 20
183 *
183 *
184 **/
184 **/
185 OutputArea.minimum_scroll_threshold = 20;
185 OutputArea.minimum_scroll_threshold = 20;
186
186
187
187
188 /**
188 /**
189 *
189 *
190 * Scroll OutputArea if height supperior than a threshold (in lines).
190 * Scroll OutputArea if height supperior than a threshold (in lines).
191 *
191 *
192 * Threshold is a maximum number of lines. If unspecified, defaults to
192 * Threshold is a maximum number of lines. If unspecified, defaults to
193 * OutputArea.minimum_scroll_threshold.
193 * OutputArea.minimum_scroll_threshold.
194 *
194 *
195 * Negative threshold will prevent the OutputArea from ever scrolling.
195 * Negative threshold will prevent the OutputArea from ever scrolling.
196 *
196 *
197 * @method scroll_if_long
197 * @method scroll_if_long
198 *
198 *
199 * @param [lines=20]{Number} Default to 20 if not set,
199 * @param [lines=20]{Number} Default to 20 if not set,
200 * behavior undefined for value of `0`.
200 * behavior undefined for value of `0`.
201 *
201 *
202 **/
202 **/
203 OutputArea.prototype.scroll_if_long = function (lines) {
203 OutputArea.prototype.scroll_if_long = function (lines) {
204 var n = lines | OutputArea.minimum_scroll_threshold;
204 var n = lines | OutputArea.minimum_scroll_threshold;
205 if(n <= 0){
205 if(n <= 0){
206 return
206 return
207 }
207 }
208
208
209 if (this._should_scroll(n)) {
209 if (this._should_scroll(n)) {
210 // only allow scrolling long-enough output
210 // only allow scrolling long-enough output
211 this.scroll_area();
211 this.scroll_area();
212 }
212 }
213 };
213 };
214
214
215
215
216 OutputArea.prototype.toggle_scroll = function () {
216 OutputArea.prototype.toggle_scroll = function () {
217 if (this.scrolled) {
217 if (this.scrolled) {
218 this.unscroll_area();
218 this.unscroll_area();
219 } else {
219 } else {
220 // only allow scrolling long-enough output
220 // only allow scrolling long-enough output
221 this.scroll_if_long();
221 this.scroll_if_long();
222 }
222 }
223 };
223 };
224
224
225
225
226 // typeset with MathJax if MathJax is available
226 // typeset with MathJax if MathJax is available
227 OutputArea.prototype.typeset = function () {
227 OutputArea.prototype.typeset = function () {
228 if (window.MathJax){
228 if (window.MathJax){
229 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
229 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
230 }
230 }
231 };
231 };
232
232
233
233
234 OutputArea.prototype.handle_output = function (msg_type, content) {
234 OutputArea.prototype.handle_output = function (msg_type, content) {
235 var json = {};
235 var json = {};
236 json.output_type = msg_type;
236 json.output_type = msg_type;
237 if (msg_type === "stream") {
237 if (msg_type === "stream") {
238 json.text = content.data;
238 json.text = content.data;
239 json.stream = content.name;
239 json.stream = content.name;
240 } else if (msg_type === "display_data") {
240 } else if (msg_type === "display_data") {
241 json = this.convert_mime_types(json, content.data);
241 json = this.convert_mime_types(json, content.data);
242 json.metadata = this.convert_mime_types({}, content.metadata);
242 json.metadata = this.convert_mime_types({}, content.metadata);
243 } else if (msg_type === "pyout") {
243 } else if (msg_type === "pyout") {
244 json.prompt_number = content.execution_count;
244 json.prompt_number = content.execution_count;
245 json = this.convert_mime_types(json, content.data);
245 json = this.convert_mime_types(json, content.data);
246 json.metadata = this.convert_mime_types({}, content.metadata);
246 json.metadata = this.convert_mime_types({}, content.metadata);
247 } else if (msg_type === "pyerr") {
247 } else if (msg_type === "pyerr") {
248 json.ename = content.ename;
248 json.ename = content.ename;
249 json.evalue = content.evalue;
249 json.evalue = content.evalue;
250 json.traceback = content.traceback;
250 json.traceback = content.traceback;
251 }
251 }
252 // append with dynamic=true
252 // append with dynamic=true
253 this.append_output(json, true);
253 this.append_output(json, true);
254 };
254 };
255
255
256
256
257 OutputArea.prototype.convert_mime_types = function (json, data) {
257 OutputArea.prototype.convert_mime_types = function (json, data) {
258 if (data === undefined) {
258 if (data === undefined) {
259 return json;
259 return json;
260 }
260 }
261 if (data['text/plain'] !== undefined) {
261 if (data['text/plain'] !== undefined) {
262 json.text = data['text/plain'];
262 json.text = data['text/plain'];
263 }
263 }
264 if (data['text/html'] !== undefined) {
264 if (data['text/html'] !== undefined) {
265 json.html = data['text/html'];
265 json.html = data['text/html'];
266 }
266 }
267 if (data['image/svg+xml'] !== undefined) {
267 if (data['image/svg+xml'] !== undefined) {
268 json.svg = data['image/svg+xml'];
268 json.svg = data['image/svg+xml'];
269 }
269 }
270 if (data['image/png'] !== undefined) {
270 if (data['image/png'] !== undefined) {
271 json.png = data['image/png'];
271 json.png = data['image/png'];
272 }
272 }
273 if (data['image/jpeg'] !== undefined) {
273 if (data['image/jpeg'] !== undefined) {
274 json.jpeg = data['image/jpeg'];
274 json.jpeg = data['image/jpeg'];
275 }
275 }
276 if (data['text/latex'] !== undefined) {
276 if (data['text/latex'] !== undefined) {
277 json.latex = data['text/latex'];
277 json.latex = data['text/latex'];
278 }
278 }
279 if (data['application/json'] !== undefined) {
279 if (data['application/json'] !== undefined) {
280 json.json = data['application/json'];
280 json.json = data['application/json'];
281 }
281 }
282 if (data['application/javascript'] !== undefined) {
282 if (data['application/javascript'] !== undefined) {
283 json.javascript = data['application/javascript'];
283 json.javascript = data['application/javascript'];
284 }
284 }
285 return json;
285 return json;
286 };
286 };
287
287
288
288
289 OutputArea.prototype.append_output = function (json, dynamic) {
289 OutputArea.prototype.append_output = function (json, dynamic) {
290 // If dynamic is true, javascript output will be eval'd.
290 // If dynamic is true, javascript output will be eval'd.
291 this.expand();
291 this.expand();
292
293 // Clear the output if clear is queued.
294 if (this.clear_queued) {
295 this.clear_output(false);
296 }
297
292 if (json.output_type === 'pyout') {
298 if (json.output_type === 'pyout') {
293 this.append_pyout(json, dynamic);
299 this.append_pyout(json, dynamic);
294 } else if (json.output_type === 'pyerr') {
300 } else if (json.output_type === 'pyerr') {
295 this.append_pyerr(json);
301 this.append_pyerr(json);
296 } else if (json.output_type === 'display_data') {
302 } else if (json.output_type === 'display_data') {
297 this.append_display_data(json, dynamic);
303 this.append_display_data(json, dynamic);
298 } else if (json.output_type === 'stream') {
304 } else if (json.output_type === 'stream') {
299 this.append_stream(json);
305 this.append_stream(json);
300 }
306 }
301 this.outputs.push(json);
307 this.outputs.push(json);
302 this.element.height('auto');
308 this.element.height('auto');
303 var that = this;
309 var that = this;
304 setTimeout(function(){that.element.trigger('resize');}, 100);
310 setTimeout(function(){that.element.trigger('resize');}, 100);
305 };
311 };
306
312
307
313
308 OutputArea.prototype.create_output_area = function () {
314 OutputArea.prototype.create_output_area = function () {
309 var oa = $("<div/>").addClass("output_area");
315 var oa = $("<div/>").addClass("output_area");
310 if (this.prompt_area) {
316 if (this.prompt_area) {
311 oa.append($('<div/>').addClass('prompt'));
317 oa.append($('<div/>').addClass('prompt'));
312 }
318 }
313 return oa;
319 return oa;
314 };
320 };
315
321
316 OutputArea.prototype._append_javascript_error = function (err, container) {
322 OutputArea.prototype._append_javascript_error = function (err, container) {
317 // display a message when a javascript error occurs in display output
323 // display a message when a javascript error occurs in display output
318 var msg = "Javascript error adding output!"
324 var msg = "Javascript error adding output!"
319 console.log(msg, err);
325 console.log(msg, err);
320 if ( container === undefined ) return;
326 if ( container === undefined ) return;
321 container.append(
327 container.append(
322 $('<div/>').html(msg + "<br/>" +
328 $('<div/>').html(msg + "<br/>" +
323 err.toString() +
329 err.toString() +
324 '<br/>See your browser Javascript console for more details.'
330 '<br/>See your browser Javascript console for more details.'
325 ).addClass('js-error')
331 ).addClass('js-error')
326 );
332 );
327 container.show();
333 container.show();
328 };
334 };
329
335
330 OutputArea.prototype._safe_append = function (toinsert) {
336 OutputArea.prototype._safe_append = function (toinsert) {
331 // safely append an item to the document
337 // safely append an item to the document
332 // this is an object created by user code,
338 // this is an object created by user code,
333 // and may have errors, which should not be raised
339 // and may have errors, which should not be raised
334 // under any circumstances.
340 // under any circumstances.
335 try {
341 try {
336 this.element.append(toinsert);
342 this.element.append(toinsert);
337 } catch(err) {
343 } catch(err) {
338 console.log(err);
344 console.log(err);
339 this._append_javascript_error(err, this.element);
345 this._append_javascript_error(err, this.element);
340 }
346 }
341 };
347 };
342
348
343
349
344 OutputArea.prototype.append_pyout = function (json, dynamic) {
350 OutputArea.prototype.append_pyout = function (json, dynamic) {
345 var n = json.prompt_number || ' ';
351 var n = json.prompt_number || ' ';
346 var toinsert = this.create_output_area();
352 var toinsert = this.create_output_area();
347 if (this.prompt_area) {
353 if (this.prompt_area) {
348 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
354 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
349 }
355 }
350 this.append_mime_type(json, toinsert, dynamic);
356 this.append_mime_type(json, toinsert, dynamic);
351 this._safe_append(toinsert);
357 this._safe_append(toinsert);
352 // If we just output latex, typeset it.
358 // If we just output latex, typeset it.
353 if ((json.latex !== undefined) || (json.html !== undefined)) {
359 if ((json.latex !== undefined) || (json.html !== undefined)) {
354 this.typeset();
360 this.typeset();
355 }
361 }
356 };
362 };
357
363
358
364
359 OutputArea.prototype.append_pyerr = function (json) {
365 OutputArea.prototype.append_pyerr = function (json) {
360 var tb = json.traceback;
366 var tb = json.traceback;
361 if (tb !== undefined && tb.length > 0) {
367 if (tb !== undefined && tb.length > 0) {
362 var s = '';
368 var s = '';
363 var len = tb.length;
369 var len = tb.length;
364 for (var i=0; i<len; i++) {
370 for (var i=0; i<len; i++) {
365 s = s + tb[i] + '\n';
371 s = s + tb[i] + '\n';
366 }
372 }
367 s = s + '\n';
373 s = s + '\n';
368 var toinsert = this.create_output_area();
374 var toinsert = this.create_output_area();
369 this.append_text(s, {}, toinsert);
375 this.append_text(s, {}, toinsert);
370 this._safe_append(toinsert);
376 this._safe_append(toinsert);
371 }
377 }
372 };
378 };
373
379
374
380
375 OutputArea.prototype.append_stream = function (json) {
381 OutputArea.prototype.append_stream = function (json) {
376 // temporary fix: if stream undefined (json file written prior to this patch),
382 // temporary fix: if stream undefined (json file written prior to this patch),
377 // default to most likely stdout:
383 // default to most likely stdout:
378 if (json.stream == undefined){
384 if (json.stream == undefined){
379 json.stream = 'stdout';
385 json.stream = 'stdout';
380 }
386 }
381 var text = json.text;
387 var text = json.text;
382 var subclass = "output_"+json.stream;
388 var subclass = "output_"+json.stream;
383 if (this.outputs.length > 0){
389 if (this.outputs.length > 0){
384 // have at least one output to consider
390 // have at least one output to consider
385 var last = this.outputs[this.outputs.length-1];
391 var last = this.outputs[this.outputs.length-1];
386 if (last.output_type == 'stream' && json.stream == last.stream){
392 if (last.output_type == 'stream' && json.stream == last.stream){
387 // latest output was in the same stream,
393 // latest output was in the same stream,
388 // so append directly into its pre tag
394 // so append directly into its pre tag
389 // escape ANSI & HTML specials:
395 // escape ANSI & HTML specials:
390 var pre = this.element.find('div.'+subclass).last().find('pre');
396 var pre = this.element.find('div.'+subclass).last().find('pre');
391 var html = utils.fixCarriageReturn(
397 var html = utils.fixCarriageReturn(
392 pre.html() + utils.fixConsole(text));
398 pre.html() + utils.fixConsole(text));
393 pre.html(html);
399 pre.html(html);
394 return;
400 return;
395 }
401 }
396 }
402 }
397
403
398 if (!text.replace("\r", "")) {
404 if (!text.replace("\r", "")) {
399 // text is nothing (empty string, \r, etc.)
405 // text is nothing (empty string, \r, etc.)
400 // so don't append any elements, which might add undesirable space
406 // so don't append any elements, which might add undesirable space
401 return;
407 return;
402 }
408 }
403
409
404 // If we got here, attach a new div
410 // If we got here, attach a new div
405 var toinsert = this.create_output_area();
411 var toinsert = this.create_output_area();
406 this.append_text(text, {}, toinsert, "output_stream "+subclass);
412 this.append_text(text, {}, toinsert, "output_stream "+subclass);
407 this._safe_append(toinsert);
413 this._safe_append(toinsert);
408 };
414 };
409
415
410
416
411 OutputArea.prototype.append_display_data = function (json, dynamic) {
417 OutputArea.prototype.append_display_data = function (json, dynamic) {
412 var toinsert = this.create_output_area();
418 var toinsert = this.create_output_area();
413 this.append_mime_type(json, toinsert, dynamic);
419 this.append_mime_type(json, toinsert, dynamic);
414 this._safe_append(toinsert);
420 this._safe_append(toinsert);
415 // If we just output latex, typeset it.
421 // If we just output latex, typeset it.
416 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
422 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
417 this.typeset();
423 this.typeset();
418 }
424 }
419 };
425 };
420
426
421 OutputArea.display_order = ['javascript','html','latex','svg','png','jpeg','text'];
427 OutputArea.display_order = ['javascript','html','latex','svg','png','jpeg','text'];
422
428
423 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
429 OutputArea.prototype.append_mime_type = function (json, element, dynamic) {
424 for(var type_i in OutputArea.display_order){
430 for(var type_i in OutputArea.display_order){
425 var type = OutputArea.display_order[type_i];
431 var type = OutputArea.display_order[type_i];
426 if(json[type] != undefined ){
432 if(json[type] != undefined ){
427 var md = {};
433 var md = {};
428 if (json.metadata && json.metadata[type]) {
434 if (json.metadata && json.metadata[type]) {
429 md = json.metadata[type];
435 md = json.metadata[type];
430 };
436 };
431 if(type == 'javascript'){
437 if(type == 'javascript'){
432 if (dynamic) {
438 if (dynamic) {
433 this.append_javascript(json.javascript, md, element, dynamic);
439 this.append_javascript(json.javascript, md, element, dynamic);
434 }
440 }
435 } else {
441 } else {
436 this['append_'+type](json[type], md, element);
442 this['append_'+type](json[type], md, element);
437 }
443 }
438 return;
444 return;
439 }
445 }
440 }
446 }
441 };
447 };
442
448
443
449
444 OutputArea.prototype.append_html = function (html, md, element) {
450 OutputArea.prototype.append_html = function (html, md, element) {
445 var toinsert = $("<div/>").addClass("output_subarea output_html rendered_html");
451 var toinsert = $("<div/>").addClass("output_subarea output_html rendered_html");
446 toinsert.append(html);
452 toinsert.append(html);
447 element.append(toinsert);
453 element.append(toinsert);
448 };
454 };
449
455
450
456
451 OutputArea.prototype.append_javascript = function (js, md, container) {
457 OutputArea.prototype.append_javascript = function (js, md, container) {
452 // We just eval the JS code, element appears in the local scope.
458 // We just eval the JS code, element appears in the local scope.
453 var element = $("<div/>").addClass("output_subarea");
459 var element = $("<div/>").addClass("output_subarea");
454 container.append(element);
460 container.append(element);
455 // Div for js shouldn't be drawn, as it will add empty height to the area.
461 // Div for js shouldn't be drawn, as it will add empty height to the area.
456 container.hide();
462 container.hide();
457 // If the Javascript appends content to `element` that should be drawn, then
463 // If the Javascript appends content to `element` that should be drawn, then
458 // it must also call `container.show()`.
464 // it must also call `container.show()`.
459 try {
465 try {
460 eval(js);
466 eval(js);
461 } catch(err) {
467 } catch(err) {
462 this._append_javascript_error(err, container);
468 this._append_javascript_error(err, container);
463 }
469 }
464 };
470 };
465
471
466
472
467 OutputArea.prototype.append_text = function (data, md, element, extra_class) {
473 OutputArea.prototype.append_text = function (data, md, element, extra_class) {
468 var toinsert = $("<div/>").addClass("output_subarea output_text");
474 var toinsert = $("<div/>").addClass("output_subarea output_text");
469 // escape ANSI & HTML specials in plaintext:
475 // escape ANSI & HTML specials in plaintext:
470 data = utils.fixConsole(data);
476 data = utils.fixConsole(data);
471 data = utils.fixCarriageReturn(data);
477 data = utils.fixCarriageReturn(data);
472 data = utils.autoLinkUrls(data);
478 data = utils.autoLinkUrls(data);
473 if (extra_class){
479 if (extra_class){
474 toinsert.addClass(extra_class);
480 toinsert.addClass(extra_class);
475 }
481 }
476 toinsert.append($("<pre/>").html(data));
482 toinsert.append($("<pre/>").html(data));
477 element.append(toinsert);
483 element.append(toinsert);
478 };
484 };
479
485
480
486
481 OutputArea.prototype.append_svg = function (svg, md, element) {
487 OutputArea.prototype.append_svg = function (svg, md, element) {
482 var toinsert = $("<div/>").addClass("output_subarea output_svg");
488 var toinsert = $("<div/>").addClass("output_subarea output_svg");
483 toinsert.append(svg);
489 toinsert.append(svg);
484 element.append(toinsert);
490 element.append(toinsert);
485 };
491 };
486
492
487
493
488 OutputArea.prototype._dblclick_to_reset_size = function (img) {
494 OutputArea.prototype._dblclick_to_reset_size = function (img) {
489 // schedule wrapping image in resizable after a delay,
495 // schedule wrapping image in resizable after a delay,
490 // so we don't end up calling resize on a zero-size object
496 // so we don't end up calling resize on a zero-size object
491 var that = this;
497 var that = this;
492 setTimeout(function () {
498 setTimeout(function () {
493 var h0 = img.height();
499 var h0 = img.height();
494 var w0 = img.width();
500 var w0 = img.width();
495 if (!(h0 && w0)) {
501 if (!(h0 && w0)) {
496 // zero size, schedule another timeout
502 // zero size, schedule another timeout
497 that._dblclick_to_reset_size(img);
503 that._dblclick_to_reset_size(img);
498 return;
504 return;
499 }
505 }
500 img.resizable({
506 img.resizable({
501 aspectRatio: true,
507 aspectRatio: true,
502 autoHide: true
508 autoHide: true
503 });
509 });
504 img.dblclick(function () {
510 img.dblclick(function () {
505 // resize wrapper & image together for some reason:
511 // resize wrapper & image together for some reason:
506 img.parent().height(h0);
512 img.parent().height(h0);
507 img.height(h0);
513 img.height(h0);
508 img.parent().width(w0);
514 img.parent().width(w0);
509 img.width(w0);
515 img.width(w0);
510 });
516 });
511 }, 250);
517 }, 250);
512 };
518 };
513
519
514
520
515 OutputArea.prototype.append_png = function (png, md, element) {
521 OutputArea.prototype.append_png = function (png, md, element) {
516 var toinsert = $("<div/>").addClass("output_subarea output_png");
522 var toinsert = $("<div/>").addClass("output_subarea output_png");
517 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
523 var img = $("<img/>").attr('src','data:image/png;base64,'+png);
518 if (md['height']) {
524 if (md['height']) {
519 img.attr('height', md['height']);
525 img.attr('height', md['height']);
520 }
526 }
521 if (md['width']) {
527 if (md['width']) {
522 img.attr('width', md['width']);
528 img.attr('width', md['width']);
523 }
529 }
524 this._dblclick_to_reset_size(img);
530 this._dblclick_to_reset_size(img);
525 toinsert.append(img);
531 toinsert.append(img);
526 element.append(toinsert);
532 element.append(toinsert);
527 };
533 };
528
534
529
535
530 OutputArea.prototype.append_jpeg = function (jpeg, md, element) {
536 OutputArea.prototype.append_jpeg = function (jpeg, md, element) {
531 var toinsert = $("<div/>").addClass("output_subarea output_jpeg");
537 var toinsert = $("<div/>").addClass("output_subarea output_jpeg");
532 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
538 var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg);
533 if (md['height']) {
539 if (md['height']) {
534 img.attr('height', md['height']);
540 img.attr('height', md['height']);
535 }
541 }
536 if (md['width']) {
542 if (md['width']) {
537 img.attr('width', md['width']);
543 img.attr('width', md['width']);
538 }
544 }
539 this._dblclick_to_reset_size(img);
545 this._dblclick_to_reset_size(img);
540 toinsert.append(img);
546 toinsert.append(img);
541 element.append(toinsert);
547 element.append(toinsert);
542 };
548 };
543
549
544
550
545 OutputArea.prototype.append_latex = function (latex, md, element) {
551 OutputArea.prototype.append_latex = function (latex, md, element) {
546 // This method cannot do the typesetting because the latex first has to
552 // This method cannot do the typesetting because the latex first has to
547 // be on the page.
553 // be on the page.
548 var toinsert = $("<div/>").addClass("output_subarea output_latex");
554 var toinsert = $("<div/>").addClass("output_subarea output_latex");
549 toinsert.append(latex);
555 toinsert.append(latex);
550 element.append(toinsert);
556 element.append(toinsert);
551 };
557 };
552
558
553 OutputArea.prototype.append_raw_input = function (content) {
559 OutputArea.prototype.append_raw_input = function (content) {
554 var that = this;
560 var that = this;
555 this.expand();
561 this.expand();
556 var area = this.create_output_area();
562 var area = this.create_output_area();
557
563
558 // disable any other raw_inputs, if they are left around
564 // disable any other raw_inputs, if they are left around
559 $("div.output_subarea.raw_input").remove();
565 $("div.output_subarea.raw_input").remove();
560
566
561 area.append(
567 area.append(
562 $("<div/>")
568 $("<div/>")
563 .addClass("box-flex1 output_subarea raw_input")
569 .addClass("box-flex1 output_subarea raw_input")
564 .append(
570 .append(
565 $("<span/>")
571 $("<span/>")
566 .addClass("input_prompt")
572 .addClass("input_prompt")
567 .text(content.prompt)
573 .text(content.prompt)
568 )
574 )
569 .append(
575 .append(
570 $("<input/>")
576 $("<input/>")
571 .addClass("raw_input")
577 .addClass("raw_input")
572 .attr('type', 'text')
578 .attr('type', 'text')
573 .attr("size", 47)
579 .attr("size", 47)
574 .keydown(function (event, ui) {
580 .keydown(function (event, ui) {
575 // make sure we submit on enter,
581 // make sure we submit on enter,
576 // and don't re-execute the *cell* on shift-enter
582 // and don't re-execute the *cell* on shift-enter
577 if (event.which === utils.keycodes.ENTER) {
583 if (event.which === utils.keycodes.ENTER) {
578 that._submit_raw_input();
584 that._submit_raw_input();
579 return false;
585 return false;
580 }
586 }
581 })
587 })
582 )
588 )
583 );
589 );
584 this.element.append(area);
590 this.element.append(area);
585 // weirdly need double-focus now,
591 // weirdly need double-focus now,
586 // otherwise only the cell will be focused
592 // otherwise only the cell will be focused
587 area.find("input.raw_input").focus().focus();
593 area.find("input.raw_input").focus().focus();
588 }
594 }
589 OutputArea.prototype._submit_raw_input = function (evt) {
595 OutputArea.prototype._submit_raw_input = function (evt) {
590 var container = this.element.find("div.raw_input");
596 var container = this.element.find("div.raw_input");
591 var theprompt = container.find("span.input_prompt");
597 var theprompt = container.find("span.input_prompt");
592 var theinput = container.find("input.raw_input");
598 var theinput = container.find("input.raw_input");
593 var value = theinput.val();
599 var value = theinput.val();
594 var content = {
600 var content = {
595 output_type : 'stream',
601 output_type : 'stream',
596 name : 'stdout',
602 name : 'stdout',
597 text : theprompt.text() + value + '\n'
603 text : theprompt.text() + value + '\n'
598 }
604 }
599 // remove form container
605 // remove form container
600 container.parent().remove();
606 container.parent().remove();
601 // replace with plaintext version in stdout
607 // replace with plaintext version in stdout
602 this.append_output(content, false);
608 this.append_output(content, false);
603 $([IPython.events]).trigger('send_input_reply.Kernel', value);
609 $([IPython.events]).trigger('send_input_reply.Kernel', value);
604 }
610 }
605
611
606
612
607 OutputArea.prototype.handle_clear_output = function (content) {
613 OutputArea.prototype.handle_clear_output = function (content) {
608 this.clear_output();
614 this.clear_output(content.wait);
609 };
615 };
610
616
611
617
612 OutputArea.prototype.clear_output = function() {
618 OutputArea.prototype.clear_output = function(wait) {
613
619 if (wait) {
614 // Fix the output div's height
615 var height = this.element.height();
616 this.element.height(height);
617
620
618 // clear all, no need for logic
621 // If a clear is queued, clear before adding another to the queue.
619 this.element.html("");
622 if (this.clear_queued) {
620 this.outputs = [];
623 this.clear_output(false);
621 this.unscroll_area();
624 };
622 return;
625
626 this.clear_queued = true;
627 } else {
628 this.clear_queued = false;
629
630 // Fix the output div's height
631 var height = this.element.height();
632 this.element.height(height);
633
634 // clear all, no need for logic
635 this.element.html("");
636 this.outputs = [];
637 this.unscroll_area();
638 return;
639 };
623 };
640 };
624
641
625
642
626 // JSON serialization
643 // JSON serialization
627
644
628 OutputArea.prototype.fromJSON = function (outputs) {
645 OutputArea.prototype.fromJSON = function (outputs) {
629 var len = outputs.length;
646 var len = outputs.length;
630 for (var i=0; i<len; i++) {
647 for (var i=0; i<len; i++) {
631 // append with dynamic=false.
648 // append with dynamic=false.
632 this.append_output(outputs[i], false);
649 this.append_output(outputs[i], false);
633 }
650 }
634 };
651 };
635
652
636
653
637 OutputArea.prototype.toJSON = function () {
654 OutputArea.prototype.toJSON = function () {
638 var outputs = [];
655 var outputs = [];
639 var len = this.outputs.length;
656 var len = this.outputs.length;
640 for (var i=0; i<len; i++) {
657 for (var i=0; i<len; i++) {
641 outputs[i] = this.outputs[i];
658 outputs[i] = this.outputs[i];
642 }
659 }
643 return outputs;
660 return outputs;
644 };
661 };
645
662
646
663
647 IPython.OutputArea = OutputArea;
664 IPython.OutputArea = OutputArea;
648
665
649 return IPython;
666 return IPython;
650
667
651 }(IPython));
668 }(IPython));
@@ -1,599 +1,599 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
12 """
12 """
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import os
19 import os
20 import sys
20 import sys
21 import time
21 import time
22
22
23 # System library imports
23 # System library imports
24 from zmq.eventloop import ioloop
24 from zmq.eventloop import ioloop
25
25
26 # Our own
26 # Our own
27 from IPython.core.interactiveshell import (
27 from IPython.core.interactiveshell import (
28 InteractiveShell, InteractiveShellABC
28 InteractiveShell, InteractiveShellABC
29 )
29 )
30 from IPython.core import page
30 from IPython.core import page
31 from IPython.core.autocall import ZMQExitAutocall
31 from IPython.core.autocall import ZMQExitAutocall
32 from IPython.core.displaypub import DisplayPublisher
32 from IPython.core.displaypub import DisplayPublisher
33 from IPython.core.error import UsageError
33 from IPython.core.error import UsageError
34 from IPython.core.magics import MacroToEdit, CodeMagics
34 from IPython.core.magics import MacroToEdit, CodeMagics
35 from IPython.core.magic import magics_class, line_magic, Magics
35 from IPython.core.magic import magics_class, line_magic, Magics
36 from IPython.core.payloadpage import install_payload_page
36 from IPython.core.payloadpage import install_payload_page
37 from IPython.display import display, Javascript
37 from IPython.display import display, Javascript
38 from IPython.kernel.inprocess.socket import SocketABC
38 from IPython.kernel.inprocess.socket import SocketABC
39 from IPython.kernel import (
39 from IPython.kernel import (
40 get_connection_file, get_connection_info, connect_qtconsole
40 get_connection_file, get_connection_info, connect_qtconsole
41 )
41 )
42 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
43 from IPython.utils import openpy
43 from IPython.utils import openpy
44 from IPython.utils.jsonutil import json_clean, encode_images
44 from IPython.utils.jsonutil import json_clean, encode_images
45 from IPython.utils.process import arg_split
45 from IPython.utils.process import arg_split
46 from IPython.utils import py3compat
46 from IPython.utils import py3compat
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes
48 from IPython.utils.warn import error
48 from IPython.utils.warn import error
49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
49 from IPython.kernel.zmq.displayhook import ZMQShellDisplayHook
50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
50 from IPython.kernel.zmq.datapub import ZMQDataPublisher
51 from IPython.kernel.zmq.session import extract_header
51 from IPython.kernel.zmq.session import extract_header
52 from session import Session
52 from session import Session
53
53
54 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
55 # Functions and classes
55 # Functions and classes
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57
57
58 class ZMQDisplayPublisher(DisplayPublisher):
58 class ZMQDisplayPublisher(DisplayPublisher):
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
59 """A display publisher that publishes data using a ZeroMQ PUB socket."""
60
60
61 session = Instance(Session)
61 session = Instance(Session)
62 pub_socket = Instance(SocketABC)
62 pub_socket = Instance(SocketABC)
63 parent_header = Dict({})
63 parent_header = Dict({})
64 topic = CBytes(b'display_data')
64 topic = CBytes(b'display_data')
65
65
66 def set_parent(self, parent):
66 def set_parent(self, parent):
67 """Set the parent for outbound messages."""
67 """Set the parent for outbound messages."""
68 self.parent_header = extract_header(parent)
68 self.parent_header = extract_header(parent)
69
69
70 def _flush_streams(self):
70 def _flush_streams(self):
71 """flush IO Streams prior to display"""
71 """flush IO Streams prior to display"""
72 sys.stdout.flush()
72 sys.stdout.flush()
73 sys.stderr.flush()
73 sys.stderr.flush()
74
74
75 def publish(self, source, data, metadata=None):
75 def publish(self, source, data, metadata=None):
76 self._flush_streams()
76 self._flush_streams()
77 if metadata is None:
77 if metadata is None:
78 metadata = {}
78 metadata = {}
79 self._validate_data(source, data, metadata)
79 self._validate_data(source, data, metadata)
80 content = {}
80 content = {}
81 content['source'] = source
81 content['source'] = source
82 content['data'] = encode_images(data)
82 content['data'] = encode_images(data)
83 content['metadata'] = metadata
83 content['metadata'] = metadata
84 self.session.send(
84 self.session.send(
85 self.pub_socket, u'display_data', json_clean(content),
85 self.pub_socket, u'display_data', json_clean(content),
86 parent=self.parent_header, ident=self.topic,
86 parent=self.parent_header, ident=self.topic,
87 )
87 )
88
88
89 def clear_output(self):
89 def clear_output(self, wait=False):
90 content = {}
90 content = dict(wait=wait)
91
91
92 print('\r', file=sys.stdout, end='')
92 print('\r', file=sys.stdout, end='')
93 print('\r', file=sys.stderr, end='')
93 print('\r', file=sys.stderr, end='')
94 self._flush_streams()
94 self._flush_streams()
95
95
96 self.session.send(
96 self.session.send(
97 self.pub_socket, u'clear_output', content,
97 self.pub_socket, u'clear_output', content,
98 parent=self.parent_header, ident=self.topic,
98 parent=self.parent_header, ident=self.topic,
99 )
99 )
100
100
101 @magics_class
101 @magics_class
102 class KernelMagics(Magics):
102 class KernelMagics(Magics):
103 #------------------------------------------------------------------------
103 #------------------------------------------------------------------------
104 # Magic overrides
104 # Magic overrides
105 #------------------------------------------------------------------------
105 #------------------------------------------------------------------------
106 # Once the base class stops inheriting from magic, this code needs to be
106 # Once the base class stops inheriting from magic, this code needs to be
107 # moved into a separate machinery as well. For now, at least isolate here
107 # moved into a separate machinery as well. For now, at least isolate here
108 # the magics which this class needs to implement differently from the base
108 # the magics which this class needs to implement differently from the base
109 # class, or that are unique to it.
109 # class, or that are unique to it.
110
110
111 @line_magic
111 @line_magic
112 def doctest_mode(self, parameter_s=''):
112 def doctest_mode(self, parameter_s=''):
113 """Toggle doctest mode on and off.
113 """Toggle doctest mode on and off.
114
114
115 This mode is intended to make IPython behave as much as possible like a
115 This mode is intended to make IPython behave as much as possible like a
116 plain Python shell, from the perspective of how its prompts, exceptions
116 plain Python shell, from the perspective of how its prompts, exceptions
117 and output look. This makes it easy to copy and paste parts of a
117 and output look. This makes it easy to copy and paste parts of a
118 session into doctests. It does so by:
118 session into doctests. It does so by:
119
119
120 - Changing the prompts to the classic ``>>>`` ones.
120 - Changing the prompts to the classic ``>>>`` ones.
121 - Changing the exception reporting mode to 'Plain'.
121 - Changing the exception reporting mode to 'Plain'.
122 - Disabling pretty-printing of output.
122 - Disabling pretty-printing of output.
123
123
124 Note that IPython also supports the pasting of code snippets that have
124 Note that IPython also supports the pasting of code snippets that have
125 leading '>>>' and '...' prompts in them. This means that you can paste
125 leading '>>>' and '...' prompts in them. This means that you can paste
126 doctests from files or docstrings (even if they have leading
126 doctests from files or docstrings (even if they have leading
127 whitespace), and the code will execute correctly. You can then use
127 whitespace), and the code will execute correctly. You can then use
128 '%history -t' to see the translated history; this will give you the
128 '%history -t' to see the translated history; this will give you the
129 input after removal of all the leading prompts and whitespace, which
129 input after removal of all the leading prompts and whitespace, which
130 can be pasted back into an editor.
130 can be pasted back into an editor.
131
131
132 With these features, you can switch into this mode easily whenever you
132 With these features, you can switch into this mode easily whenever you
133 need to do testing and changes to doctests, without having to leave
133 need to do testing and changes to doctests, without having to leave
134 your existing IPython session.
134 your existing IPython session.
135 """
135 """
136
136
137 from IPython.utils.ipstruct import Struct
137 from IPython.utils.ipstruct import Struct
138
138
139 # Shorthands
139 # Shorthands
140 shell = self.shell
140 shell = self.shell
141 disp_formatter = self.shell.display_formatter
141 disp_formatter = self.shell.display_formatter
142 ptformatter = disp_formatter.formatters['text/plain']
142 ptformatter = disp_formatter.formatters['text/plain']
143 # dstore is a data store kept in the instance metadata bag to track any
143 # dstore is a data store kept in the instance metadata bag to track any
144 # changes we make, so we can undo them later.
144 # changes we make, so we can undo them later.
145 dstore = shell.meta.setdefault('doctest_mode', Struct())
145 dstore = shell.meta.setdefault('doctest_mode', Struct())
146 save_dstore = dstore.setdefault
146 save_dstore = dstore.setdefault
147
147
148 # save a few values we'll need to recover later
148 # save a few values we'll need to recover later
149 mode = save_dstore('mode', False)
149 mode = save_dstore('mode', False)
150 save_dstore('rc_pprint', ptformatter.pprint)
150 save_dstore('rc_pprint', ptformatter.pprint)
151 save_dstore('rc_active_types',disp_formatter.active_types)
151 save_dstore('rc_active_types',disp_formatter.active_types)
152 save_dstore('xmode', shell.InteractiveTB.mode)
152 save_dstore('xmode', shell.InteractiveTB.mode)
153
153
154 if mode == False:
154 if mode == False:
155 # turn on
155 # turn on
156 ptformatter.pprint = False
156 ptformatter.pprint = False
157 disp_formatter.active_types = ['text/plain']
157 disp_formatter.active_types = ['text/plain']
158 shell.magic('xmode Plain')
158 shell.magic('xmode Plain')
159 else:
159 else:
160 # turn off
160 # turn off
161 ptformatter.pprint = dstore.rc_pprint
161 ptformatter.pprint = dstore.rc_pprint
162 disp_formatter.active_types = dstore.rc_active_types
162 disp_formatter.active_types = dstore.rc_active_types
163 shell.magic("xmode " + dstore.xmode)
163 shell.magic("xmode " + dstore.xmode)
164
164
165 # Store new mode and inform on console
165 # Store new mode and inform on console
166 dstore.mode = bool(1-int(mode))
166 dstore.mode = bool(1-int(mode))
167 mode_label = ['OFF','ON'][dstore.mode]
167 mode_label = ['OFF','ON'][dstore.mode]
168 print('Doctest mode is:', mode_label)
168 print('Doctest mode is:', mode_label)
169
169
170 # Send the payload back so that clients can modify their prompt display
170 # Send the payload back so that clients can modify their prompt display
171 payload = dict(
171 payload = dict(
172 source='doctest_mode',
172 source='doctest_mode',
173 mode=dstore.mode)
173 mode=dstore.mode)
174 shell.payload_manager.write_payload(payload)
174 shell.payload_manager.write_payload(payload)
175
175
176
176
177 _find_edit_target = CodeMagics._find_edit_target
177 _find_edit_target = CodeMagics._find_edit_target
178
178
179 @skip_doctest
179 @skip_doctest
180 @line_magic
180 @line_magic
181 def edit(self, parameter_s='', last_call=['','']):
181 def edit(self, parameter_s='', last_call=['','']):
182 """Bring up an editor and execute the resulting code.
182 """Bring up an editor and execute the resulting code.
183
183
184 Usage:
184 Usage:
185 %edit [options] [args]
185 %edit [options] [args]
186
186
187 %edit runs an external text editor. You will need to set the command for
187 %edit runs an external text editor. You will need to set the command for
188 this editor via the ``TerminalInteractiveShell.editor`` option in your
188 this editor via the ``TerminalInteractiveShell.editor`` option in your
189 configuration file before it will work.
189 configuration file before it will work.
190
190
191 This command allows you to conveniently edit multi-line code right in
191 This command allows you to conveniently edit multi-line code right in
192 your IPython session.
192 your IPython session.
193
193
194 If called without arguments, %edit opens up an empty editor with a
194 If called without arguments, %edit opens up an empty editor with a
195 temporary file and will execute the contents of this file when you
195 temporary file and will execute the contents of this file when you
196 close it (don't forget to save it!).
196 close it (don't forget to save it!).
197
197
198
198
199 Options:
199 Options:
200
200
201 -n <number>: open the editor at a specified line number. By default,
201 -n <number>: open the editor at a specified line number. By default,
202 the IPython editor hook uses the unix syntax 'editor +N filename', but
202 the IPython editor hook uses the unix syntax 'editor +N filename', but
203 you can configure this by providing your own modified hook if your
203 you can configure this by providing your own modified hook if your
204 favorite editor supports line-number specifications with a different
204 favorite editor supports line-number specifications with a different
205 syntax.
205 syntax.
206
206
207 -p: this will call the editor with the same data as the previous time
207 -p: this will call the editor with the same data as the previous time
208 it was used, regardless of how long ago (in your current session) it
208 it was used, regardless of how long ago (in your current session) it
209 was.
209 was.
210
210
211 -r: use 'raw' input. This option only applies to input taken from the
211 -r: use 'raw' input. This option only applies to input taken from the
212 user's history. By default, the 'processed' history is used, so that
212 user's history. By default, the 'processed' history is used, so that
213 magics are loaded in their transformed version to valid Python. If
213 magics are loaded in their transformed version to valid Python. If
214 this option is given, the raw input as typed as the command line is
214 this option is given, the raw input as typed as the command line is
215 used instead. When you exit the editor, it will be executed by
215 used instead. When you exit the editor, it will be executed by
216 IPython's own processor.
216 IPython's own processor.
217
217
218 -x: do not execute the edited code immediately upon exit. This is
218 -x: do not execute the edited code immediately upon exit. This is
219 mainly useful if you are editing programs which need to be called with
219 mainly useful if you are editing programs which need to be called with
220 command line arguments, which you can then do using %run.
220 command line arguments, which you can then do using %run.
221
221
222
222
223 Arguments:
223 Arguments:
224
224
225 If arguments are given, the following possibilites exist:
225 If arguments are given, the following possibilites exist:
226
226
227 - The arguments are numbers or pairs of colon-separated numbers (like
227 - The arguments are numbers or pairs of colon-separated numbers (like
228 1 4:8 9). These are interpreted as lines of previous input to be
228 1 4:8 9). These are interpreted as lines of previous input to be
229 loaded into the editor. The syntax is the same of the %macro command.
229 loaded into the editor. The syntax is the same of the %macro command.
230
230
231 - If the argument doesn't start with a number, it is evaluated as a
231 - If the argument doesn't start with a number, it is evaluated as a
232 variable and its contents loaded into the editor. You can thus edit
232 variable and its contents loaded into the editor. You can thus edit
233 any string which contains python code (including the result of
233 any string which contains python code (including the result of
234 previous edits).
234 previous edits).
235
235
236 - If the argument is the name of an object (other than a string),
236 - If the argument is the name of an object (other than a string),
237 IPython will try to locate the file where it was defined and open the
237 IPython will try to locate the file where it was defined and open the
238 editor at the point where it is defined. You can use `%edit function`
238 editor at the point where it is defined. You can use `%edit function`
239 to load an editor exactly at the point where 'function' is defined,
239 to load an editor exactly at the point where 'function' is defined,
240 edit it and have the file be executed automatically.
240 edit it and have the file be executed automatically.
241
241
242 If the object is a macro (see %macro for details), this opens up your
242 If the object is a macro (see %macro for details), this opens up your
243 specified editor with a temporary file containing the macro's data.
243 specified editor with a temporary file containing the macro's data.
244 Upon exit, the macro is reloaded with the contents of the file.
244 Upon exit, the macro is reloaded with the contents of the file.
245
245
246 Note: opening at an exact line is only supported under Unix, and some
246 Note: opening at an exact line is only supported under Unix, and some
247 editors (like kedit and gedit up to Gnome 2.8) do not understand the
247 editors (like kedit and gedit up to Gnome 2.8) do not understand the
248 '+NUMBER' parameter necessary for this feature. Good editors like
248 '+NUMBER' parameter necessary for this feature. Good editors like
249 (X)Emacs, vi, jed, pico and joe all do.
249 (X)Emacs, vi, jed, pico and joe all do.
250
250
251 - If the argument is not found as a variable, IPython will look for a
251 - If the argument is not found as a variable, IPython will look for a
252 file with that name (adding .py if necessary) and load it into the
252 file with that name (adding .py if necessary) and load it into the
253 editor. It will execute its contents with execfile() when you exit,
253 editor. It will execute its contents with execfile() when you exit,
254 loading any code in the file into your interactive namespace.
254 loading any code in the file into your interactive namespace.
255
255
256 After executing your code, %edit will return as output the code you
256 After executing your code, %edit will return as output the code you
257 typed in the editor (except when it was an existing file). This way
257 typed in the editor (except when it was an existing file). This way
258 you can reload the code in further invocations of %edit as a variable,
258 you can reload the code in further invocations of %edit as a variable,
259 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
259 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
260 the output.
260 the output.
261
261
262 Note that %edit is also available through the alias %ed.
262 Note that %edit is also available through the alias %ed.
263
263
264 This is an example of creating a simple function inside the editor and
264 This is an example of creating a simple function inside the editor and
265 then modifying it. First, start up the editor:
265 then modifying it. First, start up the editor:
266
266
267 In [1]: ed
267 In [1]: ed
268 Editing... done. Executing edited code...
268 Editing... done. Executing edited code...
269 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
269 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
270
270
271 We can then call the function foo():
271 We can then call the function foo():
272
272
273 In [2]: foo()
273 In [2]: foo()
274 foo() was defined in an editing session
274 foo() was defined in an editing session
275
275
276 Now we edit foo. IPython automatically loads the editor with the
276 Now we edit foo. IPython automatically loads the editor with the
277 (temporary) file where foo() was previously defined:
277 (temporary) file where foo() was previously defined:
278
278
279 In [3]: ed foo
279 In [3]: ed foo
280 Editing... done. Executing edited code...
280 Editing... done. Executing edited code...
281
281
282 And if we call foo() again we get the modified version:
282 And if we call foo() again we get the modified version:
283
283
284 In [4]: foo()
284 In [4]: foo()
285 foo() has now been changed!
285 foo() has now been changed!
286
286
287 Here is an example of how to edit a code snippet successive
287 Here is an example of how to edit a code snippet successive
288 times. First we call the editor:
288 times. First we call the editor:
289
289
290 In [5]: ed
290 In [5]: ed
291 Editing... done. Executing edited code...
291 Editing... done. Executing edited code...
292 hello
292 hello
293 Out[5]: "print 'hello'n"
293 Out[5]: "print 'hello'n"
294
294
295 Now we call it again with the previous output (stored in _):
295 Now we call it again with the previous output (stored in _):
296
296
297 In [6]: ed _
297 In [6]: ed _
298 Editing... done. Executing edited code...
298 Editing... done. Executing edited code...
299 hello world
299 hello world
300 Out[6]: "print 'hello world'n"
300 Out[6]: "print 'hello world'n"
301
301
302 Now we call it with the output #8 (stored in _8, also as Out[8]):
302 Now we call it with the output #8 (stored in _8, also as Out[8]):
303
303
304 In [7]: ed _8
304 In [7]: ed _8
305 Editing... done. Executing edited code...
305 Editing... done. Executing edited code...
306 hello again
306 hello again
307 Out[7]: "print 'hello again'n"
307 Out[7]: "print 'hello again'n"
308 """
308 """
309
309
310 opts,args = self.parse_options(parameter_s,'prn:')
310 opts,args = self.parse_options(parameter_s,'prn:')
311
311
312 try:
312 try:
313 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
313 filename, lineno, _ = CodeMagics._find_edit_target(self.shell, args, opts, last_call)
314 except MacroToEdit as e:
314 except MacroToEdit as e:
315 # TODO: Implement macro editing over 2 processes.
315 # TODO: Implement macro editing over 2 processes.
316 print("Macro editing not yet implemented in 2-process model.")
316 print("Macro editing not yet implemented in 2-process model.")
317 return
317 return
318
318
319 # Make sure we send to the client an absolute path, in case the working
319 # Make sure we send to the client an absolute path, in case the working
320 # directory of client and kernel don't match
320 # directory of client and kernel don't match
321 filename = os.path.abspath(filename)
321 filename = os.path.abspath(filename)
322
322
323 payload = {
323 payload = {
324 'source' : 'edit_magic',
324 'source' : 'edit_magic',
325 'filename' : filename,
325 'filename' : filename,
326 'line_number' : lineno
326 'line_number' : lineno
327 }
327 }
328 self.shell.payload_manager.write_payload(payload)
328 self.shell.payload_manager.write_payload(payload)
329
329
330 # A few magics that are adapted to the specifics of using pexpect and a
330 # A few magics that are adapted to the specifics of using pexpect and a
331 # remote terminal
331 # remote terminal
332
332
333 @line_magic
333 @line_magic
334 def clear(self, arg_s):
334 def clear(self, arg_s):
335 """Clear the terminal."""
335 """Clear the terminal."""
336 if os.name == 'posix':
336 if os.name == 'posix':
337 self.shell.system("clear")
337 self.shell.system("clear")
338 else:
338 else:
339 self.shell.system("cls")
339 self.shell.system("cls")
340
340
341 if os.name == 'nt':
341 if os.name == 'nt':
342 # This is the usual name in windows
342 # This is the usual name in windows
343 cls = line_magic('cls')(clear)
343 cls = line_magic('cls')(clear)
344
344
345 # Terminal pagers won't work over pexpect, but we do have our own pager
345 # Terminal pagers won't work over pexpect, but we do have our own pager
346
346
347 @line_magic
347 @line_magic
348 def less(self, arg_s):
348 def less(self, arg_s):
349 """Show a file through the pager.
349 """Show a file through the pager.
350
350
351 Files ending in .py are syntax-highlighted."""
351 Files ending in .py are syntax-highlighted."""
352 if not arg_s:
352 if not arg_s:
353 raise UsageError('Missing filename.')
353 raise UsageError('Missing filename.')
354
354
355 cont = open(arg_s).read()
355 cont = open(arg_s).read()
356 if arg_s.endswith('.py'):
356 if arg_s.endswith('.py'):
357 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
357 cont = self.shell.pycolorize(openpy.read_py_file(arg_s, skip_encoding_cookie=False))
358 else:
358 else:
359 cont = open(arg_s).read()
359 cont = open(arg_s).read()
360 page.page(cont)
360 page.page(cont)
361
361
362 more = line_magic('more')(less)
362 more = line_magic('more')(less)
363
363
364 # Man calls a pager, so we also need to redefine it
364 # Man calls a pager, so we also need to redefine it
365 if os.name == 'posix':
365 if os.name == 'posix':
366 @line_magic
366 @line_magic
367 def man(self, arg_s):
367 def man(self, arg_s):
368 """Find the man page for the given command and display in pager."""
368 """Find the man page for the given command and display in pager."""
369 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
369 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
370 split=False))
370 split=False))
371
371
372 @line_magic
372 @line_magic
373 def connect_info(self, arg_s):
373 def connect_info(self, arg_s):
374 """Print information for connecting other clients to this kernel
374 """Print information for connecting other clients to this kernel
375
375
376 It will print the contents of this session's connection file, as well as
376 It will print the contents of this session's connection file, as well as
377 shortcuts for local clients.
377 shortcuts for local clients.
378
378
379 In the simplest case, when called from the most recently launched kernel,
379 In the simplest case, when called from the most recently launched kernel,
380 secondary clients can be connected, simply with:
380 secondary clients can be connected, simply with:
381
381
382 $> ipython <app> --existing
382 $> ipython <app> --existing
383
383
384 """
384 """
385
385
386 from IPython.core.application import BaseIPythonApplication as BaseIPApp
386 from IPython.core.application import BaseIPythonApplication as BaseIPApp
387
387
388 if BaseIPApp.initialized():
388 if BaseIPApp.initialized():
389 app = BaseIPApp.instance()
389 app = BaseIPApp.instance()
390 security_dir = app.profile_dir.security_dir
390 security_dir = app.profile_dir.security_dir
391 profile = app.profile
391 profile = app.profile
392 else:
392 else:
393 profile = 'default'
393 profile = 'default'
394 security_dir = ''
394 security_dir = ''
395
395
396 try:
396 try:
397 connection_file = get_connection_file()
397 connection_file = get_connection_file()
398 info = get_connection_info(unpack=False)
398 info = get_connection_info(unpack=False)
399 except Exception as e:
399 except Exception as e:
400 error("Could not get connection info: %r" % e)
400 error("Could not get connection info: %r" % e)
401 return
401 return
402
402
403 # add profile flag for non-default profile
403 # add profile flag for non-default profile
404 profile_flag = "--profile %s" % profile if profile != 'default' else ""
404 profile_flag = "--profile %s" % profile if profile != 'default' else ""
405
405
406 # if it's in the security dir, truncate to basename
406 # if it's in the security dir, truncate to basename
407 if security_dir == os.path.dirname(connection_file):
407 if security_dir == os.path.dirname(connection_file):
408 connection_file = os.path.basename(connection_file)
408 connection_file = os.path.basename(connection_file)
409
409
410
410
411 print (info + '\n')
411 print (info + '\n')
412 print ("Paste the above JSON into a file, and connect with:\n"
412 print ("Paste the above JSON into a file, and connect with:\n"
413 " $> ipython <app> --existing <file>\n"
413 " $> ipython <app> --existing <file>\n"
414 "or, if you are local, you can connect with just:\n"
414 "or, if you are local, you can connect with just:\n"
415 " $> ipython <app> --existing {0} {1}\n"
415 " $> ipython <app> --existing {0} {1}\n"
416 "or even just:\n"
416 "or even just:\n"
417 " $> ipython <app> --existing {1}\n"
417 " $> ipython <app> --existing {1}\n"
418 "if this is the most recent IPython session you have started.".format(
418 "if this is the most recent IPython session you have started.".format(
419 connection_file, profile_flag
419 connection_file, profile_flag
420 )
420 )
421 )
421 )
422
422
423 @line_magic
423 @line_magic
424 def qtconsole(self, arg_s):
424 def qtconsole(self, arg_s):
425 """Open a qtconsole connected to this kernel.
425 """Open a qtconsole connected to this kernel.
426
426
427 Useful for connecting a qtconsole to running notebooks, for better
427 Useful for connecting a qtconsole to running notebooks, for better
428 debugging.
428 debugging.
429 """
429 """
430
430
431 # %qtconsole should imply bind_kernel for engines:
431 # %qtconsole should imply bind_kernel for engines:
432 try:
432 try:
433 from IPython.parallel import bind_kernel
433 from IPython.parallel import bind_kernel
434 except ImportError:
434 except ImportError:
435 # technically possible, because parallel has higher pyzmq min-version
435 # technically possible, because parallel has higher pyzmq min-version
436 pass
436 pass
437 else:
437 else:
438 bind_kernel()
438 bind_kernel()
439
439
440 try:
440 try:
441 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
441 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
442 except Exception as e:
442 except Exception as e:
443 error("Could not start qtconsole: %r" % e)
443 error("Could not start qtconsole: %r" % e)
444 return
444 return
445
445
446 @line_magic
446 @line_magic
447 def autosave(self, arg_s):
447 def autosave(self, arg_s):
448 """Set the autosave interval in the notebook (in seconds).
448 """Set the autosave interval in the notebook (in seconds).
449
449
450 The default value is 120, or two minutes.
450 The default value is 120, or two minutes.
451 ``%autosave 0`` will disable autosave.
451 ``%autosave 0`` will disable autosave.
452
452
453 This magic only has an effect when called from the notebook interface.
453 This magic only has an effect when called from the notebook interface.
454 It has no effect when called in a startup file.
454 It has no effect when called in a startup file.
455 """
455 """
456
456
457 try:
457 try:
458 interval = int(arg_s)
458 interval = int(arg_s)
459 except ValueError:
459 except ValueError:
460 raise UsageError("%%autosave requires an integer, got %r" % arg_s)
460 raise UsageError("%%autosave requires an integer, got %r" % arg_s)
461
461
462 # javascript wants milliseconds
462 # javascript wants milliseconds
463 milliseconds = 1000 * interval
463 milliseconds = 1000 * interval
464 display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds),
464 display(Javascript("IPython.notebook.set_autosave_interval(%i)" % milliseconds),
465 include=['application/javascript']
465 include=['application/javascript']
466 )
466 )
467 if interval:
467 if interval:
468 print("Autosaving every %i seconds" % interval)
468 print("Autosaving every %i seconds" % interval)
469 else:
469 else:
470 print("Autosave disabled")
470 print("Autosave disabled")
471
471
472
472
473 class ZMQInteractiveShell(InteractiveShell):
473 class ZMQInteractiveShell(InteractiveShell):
474 """A subclass of InteractiveShell for ZMQ."""
474 """A subclass of InteractiveShell for ZMQ."""
475
475
476 displayhook_class = Type(ZMQShellDisplayHook)
476 displayhook_class = Type(ZMQShellDisplayHook)
477 display_pub_class = Type(ZMQDisplayPublisher)
477 display_pub_class = Type(ZMQDisplayPublisher)
478 data_pub_class = Type(ZMQDataPublisher)
478 data_pub_class = Type(ZMQDataPublisher)
479
479
480 # Override the traitlet in the parent class, because there's no point using
480 # Override the traitlet in the parent class, because there's no point using
481 # readline for the kernel. Can be removed when the readline code is moved
481 # readline for the kernel. Can be removed when the readline code is moved
482 # to the terminal frontend.
482 # to the terminal frontend.
483 colors_force = CBool(True)
483 colors_force = CBool(True)
484 readline_use = CBool(False)
484 readline_use = CBool(False)
485 # autoindent has no meaning in a zmqshell, and attempting to enable it
485 # autoindent has no meaning in a zmqshell, and attempting to enable it
486 # will print a warning in the absence of readline.
486 # will print a warning in the absence of readline.
487 autoindent = CBool(False)
487 autoindent = CBool(False)
488
488
489 exiter = Instance(ZMQExitAutocall)
489 exiter = Instance(ZMQExitAutocall)
490 def _exiter_default(self):
490 def _exiter_default(self):
491 return ZMQExitAutocall(self)
491 return ZMQExitAutocall(self)
492
492
493 def _exit_now_changed(self, name, old, new):
493 def _exit_now_changed(self, name, old, new):
494 """stop eventloop when exit_now fires"""
494 """stop eventloop when exit_now fires"""
495 if new:
495 if new:
496 loop = ioloop.IOLoop.instance()
496 loop = ioloop.IOLoop.instance()
497 loop.add_timeout(time.time()+0.1, loop.stop)
497 loop.add_timeout(time.time()+0.1, loop.stop)
498
498
499 keepkernel_on_exit = None
499 keepkernel_on_exit = None
500
500
501 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
501 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
502 # interactive input being read; we provide event loop support in ipkernel
502 # interactive input being read; we provide event loop support in ipkernel
503 @staticmethod
503 @staticmethod
504 def enable_gui(gui):
504 def enable_gui(gui):
505 from .eventloops import enable_gui as real_enable_gui
505 from .eventloops import enable_gui as real_enable_gui
506 try:
506 try:
507 real_enable_gui(gui)
507 real_enable_gui(gui)
508 except ValueError as e:
508 except ValueError as e:
509 raise UsageError("%s" % e)
509 raise UsageError("%s" % e)
510
510
511 def init_environment(self):
511 def init_environment(self):
512 """Configure the user's environment.
512 """Configure the user's environment.
513
513
514 """
514 """
515 env = os.environ
515 env = os.environ
516 # These two ensure 'ls' produces nice coloring on BSD-derived systems
516 # These two ensure 'ls' produces nice coloring on BSD-derived systems
517 env['TERM'] = 'xterm-color'
517 env['TERM'] = 'xterm-color'
518 env['CLICOLOR'] = '1'
518 env['CLICOLOR'] = '1'
519 # Since normal pagers don't work at all (over pexpect we don't have
519 # Since normal pagers don't work at all (over pexpect we don't have
520 # single-key control of the subprocess), try to disable paging in
520 # single-key control of the subprocess), try to disable paging in
521 # subprocesses as much as possible.
521 # subprocesses as much as possible.
522 env['PAGER'] = 'cat'
522 env['PAGER'] = 'cat'
523 env['GIT_PAGER'] = 'cat'
523 env['GIT_PAGER'] = 'cat'
524
524
525 # And install the payload version of page.
525 # And install the payload version of page.
526 install_payload_page()
526 install_payload_page()
527
527
528 def auto_rewrite_input(self, cmd):
528 def auto_rewrite_input(self, cmd):
529 """Called to show the auto-rewritten input for autocall and friends.
529 """Called to show the auto-rewritten input for autocall and friends.
530
530
531 FIXME: this payload is currently not correctly processed by the
531 FIXME: this payload is currently not correctly processed by the
532 frontend.
532 frontend.
533 """
533 """
534 new = self.prompt_manager.render('rewrite') + cmd
534 new = self.prompt_manager.render('rewrite') + cmd
535 payload = dict(
535 payload = dict(
536 source='auto_rewrite_input',
536 source='auto_rewrite_input',
537 transformed_input=new,
537 transformed_input=new,
538 )
538 )
539 self.payload_manager.write_payload(payload)
539 self.payload_manager.write_payload(payload)
540
540
541 def ask_exit(self):
541 def ask_exit(self):
542 """Engage the exit actions."""
542 """Engage the exit actions."""
543 self.exit_now = True
543 self.exit_now = True
544 payload = dict(
544 payload = dict(
545 source='ask_exit',
545 source='ask_exit',
546 exit=True,
546 exit=True,
547 keepkernel=self.keepkernel_on_exit,
547 keepkernel=self.keepkernel_on_exit,
548 )
548 )
549 self.payload_manager.write_payload(payload)
549 self.payload_manager.write_payload(payload)
550
550
551 def _showtraceback(self, etype, evalue, stb):
551 def _showtraceback(self, etype, evalue, stb):
552
552
553 exc_content = {
553 exc_content = {
554 u'traceback' : stb,
554 u'traceback' : stb,
555 u'ename' : unicode(etype.__name__),
555 u'ename' : unicode(etype.__name__),
556 u'evalue' : py3compat.safe_unicode(evalue),
556 u'evalue' : py3compat.safe_unicode(evalue),
557 }
557 }
558
558
559 dh = self.displayhook
559 dh = self.displayhook
560 # Send exception info over pub socket for other clients than the caller
560 # Send exception info over pub socket for other clients than the caller
561 # to pick up
561 # to pick up
562 topic = None
562 topic = None
563 if dh.topic:
563 if dh.topic:
564 topic = dh.topic.replace(b'pyout', b'pyerr')
564 topic = dh.topic.replace(b'pyout', b'pyerr')
565
565
566 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
566 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
567
567
568 # FIXME - Hack: store exception info in shell object. Right now, the
568 # FIXME - Hack: store exception info in shell object. Right now, the
569 # caller is reading this info after the fact, we need to fix this logic
569 # caller is reading this info after the fact, we need to fix this logic
570 # to remove this hack. Even uglier, we need to store the error status
570 # to remove this hack. Even uglier, we need to store the error status
571 # here, because in the main loop, the logic that sets it is being
571 # here, because in the main loop, the logic that sets it is being
572 # skipped because runlines swallows the exceptions.
572 # skipped because runlines swallows the exceptions.
573 exc_content[u'status'] = u'error'
573 exc_content[u'status'] = u'error'
574 self._reply_content = exc_content
574 self._reply_content = exc_content
575 # /FIXME
575 # /FIXME
576
576
577 return exc_content
577 return exc_content
578
578
579 def set_next_input(self, text):
579 def set_next_input(self, text):
580 """Send the specified text to the frontend to be presented at the next
580 """Send the specified text to the frontend to be presented at the next
581 input cell."""
581 input cell."""
582 payload = dict(
582 payload = dict(
583 source='set_next_input',
583 source='set_next_input',
584 text=text
584 text=text
585 )
585 )
586 self.payload_manager.write_payload(payload)
586 self.payload_manager.write_payload(payload)
587
587
588 #-------------------------------------------------------------------------
588 #-------------------------------------------------------------------------
589 # Things related to magics
589 # Things related to magics
590 #-------------------------------------------------------------------------
590 #-------------------------------------------------------------------------
591
591
592 def init_magics(self):
592 def init_magics(self):
593 super(ZMQInteractiveShell, self).init_magics()
593 super(ZMQInteractiveShell, self).init_magics()
594 self.register_magics(KernelMagics)
594 self.register_magics(KernelMagics)
595 self.magics_manager.register_alias('ed', 'edit')
595 self.magics_manager.register_alias('ed', 'edit')
596
596
597
597
598
598
599 InteractiveShellABC.register(ZMQInteractiveShell)
599 InteractiveShellABC.register(ZMQInteractiveShell)
@@ -1,1064 +1,1078 b''
1 .. _messaging:
1 .. _messaging:
2
2
3 ======================
3 ======================
4 Messaging in IPython
4 Messaging in IPython
5 ======================
5 ======================
6
6
7
7
8 Introduction
8 Introduction
9 ============
9 ============
10
10
11 This document explains the basic communications design and messaging
11 This document explains the basic communications design and messaging
12 specification for how the various IPython objects interact over a network
12 specification for how the various IPython objects interact over a network
13 transport. The current implementation uses the ZeroMQ_ library for messaging
13 transport. The current implementation uses the ZeroMQ_ library for messaging
14 within and between hosts.
14 within and between hosts.
15
15
16 .. Note::
16 .. Note::
17
17
18 This document should be considered the authoritative description of the
18 This document should be considered the authoritative description of the
19 IPython messaging protocol, and all developers are strongly encouraged to
19 IPython messaging protocol, and all developers are strongly encouraged to
20 keep it updated as the implementation evolves, so that we have a single
20 keep it updated as the implementation evolves, so that we have a single
21 common reference for all protocol details.
21 common reference for all protocol details.
22
22
23 The basic design is explained in the following diagram:
23 The basic design is explained in the following diagram:
24
24
25 .. image:: figs/frontend-kernel.png
25 .. image:: figs/frontend-kernel.png
26 :width: 450px
26 :width: 450px
27 :alt: IPython kernel/frontend messaging architecture.
27 :alt: IPython kernel/frontend messaging architecture.
28 :align: center
28 :align: center
29 :target: ../_images/frontend-kernel.png
29 :target: ../_images/frontend-kernel.png
30
30
31 A single kernel can be simultaneously connected to one or more frontends. The
31 A single kernel can be simultaneously connected to one or more frontends. The
32 kernel has three sockets that serve the following functions:
32 kernel has three sockets that serve the following functions:
33
33
34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
34 1. stdin: this ROUTER socket is connected to all frontends, and it allows
35 the kernel to request input from the active frontend when :func:`raw_input` is called.
35 the kernel to request input from the active frontend when :func:`raw_input` is called.
36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
36 The frontend that executed the code has a DEALER socket that acts as a 'virtual keyboard'
37 for the kernel while this communication is happening (illustrated in the
37 for the kernel while this communication is happening (illustrated in the
38 figure by the black outline around the central keyboard). In practice,
38 figure by the black outline around the central keyboard). In practice,
39 frontends may display such kernel requests using a special input widget or
39 frontends may display such kernel requests using a special input widget or
40 otherwise indicating that the user is to type input for the kernel instead
40 otherwise indicating that the user is to type input for the kernel instead
41 of normal commands in the frontend.
41 of normal commands in the frontend.
42
42
43 2. Shell: this single ROUTER socket allows multiple incoming connections from
43 2. Shell: this single ROUTER socket allows multiple incoming connections from
44 frontends, and this is the socket where requests for code execution, object
44 frontends, and this is the socket where requests for code execution, object
45 information, prompts, etc. are made to the kernel by any frontend. The
45 information, prompts, etc. are made to the kernel by any frontend. The
46 communication on this socket is a sequence of request/reply actions from
46 communication on this socket is a sequence of request/reply actions from
47 each frontend and the kernel.
47 each frontend and the kernel.
48
48
49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
49 3. IOPub: this socket is the 'broadcast channel' where the kernel publishes all
50 side effects (stdout, stderr, etc.) as well as the requests coming from any
50 side effects (stdout, stderr, etc.) as well as the requests coming from any
51 client over the shell socket and its own requests on the stdin socket. There
51 client over the shell socket and its own requests on the stdin socket. There
52 are a number of actions in Python which generate side effects: :func:`print`
52 are a number of actions in Python which generate side effects: :func:`print`
53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
53 writes to ``sys.stdout``, errors generate tracebacks, etc. Additionally, in
54 a multi-client scenario, we want all frontends to be able to know what each
54 a multi-client scenario, we want all frontends to be able to know what each
55 other has sent to the kernel (this can be useful in collaborative scenarios,
55 other has sent to the kernel (this can be useful in collaborative scenarios,
56 for example). This socket allows both side effects and the information
56 for example). This socket allows both side effects and the information
57 about communications taking place with one client over the shell channel
57 about communications taking place with one client over the shell channel
58 to be made available to all clients in a uniform manner.
58 to be made available to all clients in a uniform manner.
59
59
60 All messages are tagged with enough information (details below) for clients
60 All messages are tagged with enough information (details below) for clients
61 to know which messages come from their own interaction with the kernel and
61 to know which messages come from their own interaction with the kernel and
62 which ones are from other clients, so they can display each type
62 which ones are from other clients, so they can display each type
63 appropriately.
63 appropriately.
64
64
65 The actual format of the messages allowed on each of these channels is
65 The actual format of the messages allowed on each of these channels is
66 specified below. Messages are dicts of dicts with string keys and values that
66 specified below. Messages are dicts of dicts with string keys and values that
67 are reasonably representable in JSON. Our current implementation uses JSON
67 are reasonably representable in JSON. Our current implementation uses JSON
68 explicitly as its message format, but this shouldn't be considered a permanent
68 explicitly as its message format, but this shouldn't be considered a permanent
69 feature. As we've discovered that JSON has non-trivial performance issues due
69 feature. As we've discovered that JSON has non-trivial performance issues due
70 to excessive copying, we may in the future move to a pure pickle-based raw
70 to excessive copying, we may in the future move to a pure pickle-based raw
71 message format. However, it should be possible to easily convert from the raw
71 message format. However, it should be possible to easily convert from the raw
72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
72 objects to JSON, since we may have non-python clients (e.g. a web frontend).
73 As long as it's easy to make a JSON version of the objects that is a faithful
73 As long as it's easy to make a JSON version of the objects that is a faithful
74 representation of all the data, we can communicate with such clients.
74 representation of all the data, we can communicate with such clients.
75
75
76 .. Note::
76 .. Note::
77
77
78 Not all of these have yet been fully fleshed out, but the key ones are, see
78 Not all of these have yet been fully fleshed out, but the key ones are, see
79 kernel and frontend files for actual implementation details.
79 kernel and frontend files for actual implementation details.
80
80
81 General Message Format
81 General Message Format
82 ======================
82 ======================
83
83
84 A message is defined by the following four-dictionary structure::
84 A message is defined by the following four-dictionary structure::
85
85
86 {
86 {
87 # The message header contains a pair of unique identifiers for the
87 # The message header contains a pair of unique identifiers for the
88 # originating session and the actual message id, in addition to the
88 # originating session and the actual message id, in addition to the
89 # username for the process that generated the message. This is useful in
89 # username for the process that generated the message. This is useful in
90 # collaborative settings where multiple users may be interacting with the
90 # collaborative settings where multiple users may be interacting with the
91 # same kernel simultaneously, so that frontends can label the various
91 # same kernel simultaneously, so that frontends can label the various
92 # messages in a meaningful way.
92 # messages in a meaningful way.
93 'header' : {
93 'header' : {
94 'msg_id' : uuid,
94 'msg_id' : uuid,
95 'username' : str,
95 'username' : str,
96 'session' : uuid,
96 'session' : uuid,
97 # All recognized message type strings are listed below.
97 # All recognized message type strings are listed below.
98 'msg_type' : str,
98 'msg_type' : str,
99 },
99 },
100
100
101 # In a chain of messages, the header from the parent is copied so that
101 # In a chain of messages, the header from the parent is copied so that
102 # clients can track where messages come from.
102 # clients can track where messages come from.
103 'parent_header' : dict,
103 'parent_header' : dict,
104
104
105 # Any metadata associated with the message.
105 # Any metadata associated with the message.
106 'metadata' : dict,
106 'metadata' : dict,
107
107
108 # The actual content of the message must be a dict, whose structure
108 # The actual content of the message must be a dict, whose structure
109 # depends on the message type.
109 # depends on the message type.
110 'content' : dict,
110 'content' : dict,
111 }
111 }
112
112
113 The Wire Protocol
113 The Wire Protocol
114 =================
114 =================
115
115
116
116
117 This message format exists at a high level,
117 This message format exists at a high level,
118 but does not describe the actual *implementation* at the wire level in zeromq.
118 but does not describe the actual *implementation* at the wire level in zeromq.
119 The canonical implementation of the message spec is our :class:`~IPython.kernel.zmq.session.Session` class.
119 The canonical implementation of the message spec is our :class:`~IPython.kernel.zmq.session.Session` class.
120
120
121 .. note::
121 .. note::
122
122
123 This section should only be relevant to non-Python consumers of the protocol.
123 This section should only be relevant to non-Python consumers of the protocol.
124 Python consumers should simply import and use IPython's own implementation of the wire protocol
124 Python consumers should simply import and use IPython's own implementation of the wire protocol
125 in the :class:`IPython.kernel.zmq.session.Session` object.
125 in the :class:`IPython.kernel.zmq.session.Session` object.
126
126
127 Every message is serialized to a sequence of at least six blobs of bytes:
127 Every message is serialized to a sequence of at least six blobs of bytes:
128
128
129 .. sourcecode:: python
129 .. sourcecode:: python
130
130
131 [
131 [
132 b'u-u-i-d', # zmq identity(ies)
132 b'u-u-i-d', # zmq identity(ies)
133 b'<IDS|MSG>', # delimiter
133 b'<IDS|MSG>', # delimiter
134 b'baddad42', # HMAC signature
134 b'baddad42', # HMAC signature
135 b'{header}', # serialized header dict
135 b'{header}', # serialized header dict
136 b'{parent_header}', # serialized parent header dict
136 b'{parent_header}', # serialized parent header dict
137 b'{metadata}', # serialized metadata dict
137 b'{metadata}', # serialized metadata dict
138 b'{content}, # serialized content dict
138 b'{content}, # serialized content dict
139 b'blob', # extra raw data buffer(s)
139 b'blob', # extra raw data buffer(s)
140 ...
140 ...
141 ]
141 ]
142
142
143 The front of the message is the ZeroMQ routing prefix,
143 The front of the message is the ZeroMQ routing prefix,
144 which can be zero or more socket identities.
144 which can be zero or more socket identities.
145 This is every piece of the message prior to the delimiter key ``<IDS|MSG>``.
145 This is every piece of the message prior to the delimiter key ``<IDS|MSG>``.
146 In the case of IOPub, there should be just one prefix component,
146 In the case of IOPub, there should be just one prefix component,
147 which is the topic for IOPub subscribers, e.g. ``pyout``, ``display_data``.
147 which is the topic for IOPub subscribers, e.g. ``pyout``, ``display_data``.
148
148
149 .. note::
149 .. note::
150
150
151 In most cases, the IOPub topics are irrelevant and completely ignored,
151 In most cases, the IOPub topics are irrelevant and completely ignored,
152 because frontends just subscribe to all topics.
152 because frontends just subscribe to all topics.
153 The convention used in the IPython kernel is to use the msg_type as the topic,
153 The convention used in the IPython kernel is to use the msg_type as the topic,
154 and possibly extra information about the message, e.g. ``pyout`` or ``stream.stdout``
154 and possibly extra information about the message, e.g. ``pyout`` or ``stream.stdout``
155
155
156 After the delimiter is the `HMAC`_ signature of the message, used for authentication.
156 After the delimiter is the `HMAC`_ signature of the message, used for authentication.
157 If authentication is disabled, this should be an empty string.
157 If authentication is disabled, this should be an empty string.
158 By default, the hashing function used for computing these signatures is sha256.
158 By default, the hashing function used for computing these signatures is sha256.
159
159
160 .. _HMAC: http://en.wikipedia.org/wiki/HMAC
160 .. _HMAC: http://en.wikipedia.org/wiki/HMAC
161
161
162 .. note::
162 .. note::
163
163
164 To disable authentication and signature checking,
164 To disable authentication and signature checking,
165 set the `key` field of a connection file to an empty string.
165 set the `key` field of a connection file to an empty string.
166
166
167 The signature is the HMAC hex digest of the concatenation of:
167 The signature is the HMAC hex digest of the concatenation of:
168
168
169 - A shared key (typically the ``key`` field of a connection file)
169 - A shared key (typically the ``key`` field of a connection file)
170 - The serialized header dict
170 - The serialized header dict
171 - The serialized parent header dict
171 - The serialized parent header dict
172 - The serialized metadata dict
172 - The serialized metadata dict
173 - The serialized content dict
173 - The serialized content dict
174
174
175 In Python, this is implemented via:
175 In Python, this is implemented via:
176
176
177 .. sourcecode:: python
177 .. sourcecode:: python
178
178
179 # once:
179 # once:
180 digester = HMAC(key, digestmod=hashlib.sha256)
180 digester = HMAC(key, digestmod=hashlib.sha256)
181
181
182 # for each message
182 # for each message
183 d = digester.copy()
183 d = digester.copy()
184 for serialized_dict in (header, parent, metadata, content):
184 for serialized_dict in (header, parent, metadata, content):
185 d.update(serialized_dict)
185 d.update(serialized_dict)
186 signature = d.hexdigest()
186 signature = d.hexdigest()
187
187
188 After the signature is the actual message, always in four frames of bytes.
188 After the signature is the actual message, always in four frames of bytes.
189 The four dictionaries that compose a message are serialized separately,
189 The four dictionaries that compose a message are serialized separately,
190 in the order of header, parent header, metadata, and content.
190 in the order of header, parent header, metadata, and content.
191 These can be serialized by any function that turns a dict into bytes.
191 These can be serialized by any function that turns a dict into bytes.
192 The default and most common serialization is JSON, but msgpack and pickle
192 The default and most common serialization is JSON, but msgpack and pickle
193 are common alternatives.
193 are common alternatives.
194
194
195 After the serialized dicts are zero to many raw data buffers,
195 After the serialized dicts are zero to many raw data buffers,
196 which can be used by message types that support binary data (mainly apply and data_pub).
196 which can be used by message types that support binary data (mainly apply and data_pub).
197
197
198
198
199 Python functional API
199 Python functional API
200 =====================
200 =====================
201
201
202 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
202 As messages are dicts, they map naturally to a ``func(**kw)`` call form. We
203 should develop, at a few key points, functional forms of all the requests that
203 should develop, at a few key points, functional forms of all the requests that
204 take arguments in this manner and automatically construct the necessary dict
204 take arguments in this manner and automatically construct the necessary dict
205 for sending.
205 for sending.
206
206
207 In addition, the Python implementation of the message specification extends
207 In addition, the Python implementation of the message specification extends
208 messages upon deserialization to the following form for convenience::
208 messages upon deserialization to the following form for convenience::
209
209
210 {
210 {
211 'header' : dict,
211 'header' : dict,
212 # The msg's unique identifier and type are always stored in the header,
212 # The msg's unique identifier and type are always stored in the header,
213 # but the Python implementation copies them to the top level.
213 # but the Python implementation copies them to the top level.
214 'msg_id' : uuid,
214 'msg_id' : uuid,
215 'msg_type' : str,
215 'msg_type' : str,
216 'parent_header' : dict,
216 'parent_header' : dict,
217 'content' : dict,
217 'content' : dict,
218 'metadata' : dict,
218 'metadata' : dict,
219 }
219 }
220
220
221 All messages sent to or received by any IPython process should have this
221 All messages sent to or received by any IPython process should have this
222 extended structure.
222 extended structure.
223
223
224
224
225 Messages on the shell ROUTER/DEALER sockets
225 Messages on the shell ROUTER/DEALER sockets
226 ===========================================
226 ===========================================
227
227
228 .. _execute:
228 .. _execute:
229
229
230 Execute
230 Execute
231 -------
231 -------
232
232
233 This message type is used by frontends to ask the kernel to execute code on
233 This message type is used by frontends to ask the kernel to execute code on
234 behalf of the user, in a namespace reserved to the user's variables (and thus
234 behalf of the user, in a namespace reserved to the user's variables (and thus
235 separate from the kernel's own internal code and variables).
235 separate from the kernel's own internal code and variables).
236
236
237 Message type: ``execute_request``::
237 Message type: ``execute_request``::
238
238
239 content = {
239 content = {
240 # Source code to be executed by the kernel, one or more lines.
240 # Source code to be executed by the kernel, one or more lines.
241 'code' : str,
241 'code' : str,
242
242
243 # A boolean flag which, if True, signals the kernel to execute
243 # A boolean flag which, if True, signals the kernel to execute
244 # this code as quietly as possible. This means that the kernel
244 # this code as quietly as possible. This means that the kernel
245 # will compile the code with 'exec' instead of 'single' (so
245 # will compile the code with 'exec' instead of 'single' (so
246 # sys.displayhook will not fire), forces store_history to be False,
246 # sys.displayhook will not fire), forces store_history to be False,
247 # and will *not*:
247 # and will *not*:
248 # - broadcast exceptions on the PUB socket
248 # - broadcast exceptions on the PUB socket
249 # - do any logging
249 # - do any logging
250 #
250 #
251 # The default is False.
251 # The default is False.
252 'silent' : bool,
252 'silent' : bool,
253
253
254 # A boolean flag which, if True, signals the kernel to populate history
254 # A boolean flag which, if True, signals the kernel to populate history
255 # The default is True if silent is False. If silent is True, store_history
255 # The default is True if silent is False. If silent is True, store_history
256 # is forced to be False.
256 # is forced to be False.
257 'store_history' : bool,
257 'store_history' : bool,
258
258
259 # A list of variable names from the user's namespace to be retrieved.
259 # A list of variable names from the user's namespace to be retrieved.
260 # What returns is a rich representation of each variable (dict keyed by name).
260 # What returns is a rich representation of each variable (dict keyed by name).
261 # See the display_data content for the structure of the representation data.
261 # See the display_data content for the structure of the representation data.
262 'user_variables' : list,
262 'user_variables' : list,
263
263
264 # Similarly, a dict mapping names to expressions to be evaluated in the
264 # Similarly, a dict mapping names to expressions to be evaluated in the
265 # user's dict.
265 # user's dict.
266 'user_expressions' : dict,
266 'user_expressions' : dict,
267
267
268 # Some frontends (e.g. the Notebook) do not support stdin requests. If
268 # Some frontends (e.g. the Notebook) do not support stdin requests. If
269 # raw_input is called from code executed from such a frontend, a
269 # raw_input is called from code executed from such a frontend, a
270 # StdinNotImplementedError will be raised.
270 # StdinNotImplementedError will be raised.
271 'allow_stdin' : True,
271 'allow_stdin' : True,
272
272
273 }
273 }
274
274
275 The ``code`` field contains a single string (possibly multiline). The kernel
275 The ``code`` field contains a single string (possibly multiline). The kernel
276 is responsible for splitting this into one or more independent execution blocks
276 is responsible for splitting this into one or more independent execution blocks
277 and deciding whether to compile these in 'single' or 'exec' mode (see below for
277 and deciding whether to compile these in 'single' or 'exec' mode (see below for
278 detailed execution semantics).
278 detailed execution semantics).
279
279
280 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
280 The ``user_`` fields deserve a detailed explanation. In the past, IPython had
281 the notion of a prompt string that allowed arbitrary code to be evaluated, and
281 the notion of a prompt string that allowed arbitrary code to be evaluated, and
282 this was put to good use by many in creating prompts that displayed system
282 this was put to good use by many in creating prompts that displayed system
283 status, path information, and even more esoteric uses like remote instrument
283 status, path information, and even more esoteric uses like remote instrument
284 status acquired over the network. But now that IPython has a clean separation
284 status acquired over the network. But now that IPython has a clean separation
285 between the kernel and the clients, the kernel has no prompt knowledge; prompts
285 between the kernel and the clients, the kernel has no prompt knowledge; prompts
286 are a frontend-side feature, and it should be even possible for different
286 are a frontend-side feature, and it should be even possible for different
287 frontends to display different prompts while interacting with the same kernel.
287 frontends to display different prompts while interacting with the same kernel.
288
288
289 The kernel now provides the ability to retrieve data from the user's namespace
289 The kernel now provides the ability to retrieve data from the user's namespace
290 after the execution of the main ``code``, thanks to two fields in the
290 after the execution of the main ``code``, thanks to two fields in the
291 ``execute_request`` message:
291 ``execute_request`` message:
292
292
293 - ``user_variables``: If only variables from the user's namespace are needed, a
293 - ``user_variables``: If only variables from the user's namespace are needed, a
294 list of variable names can be passed and a dict with these names as keys and
294 list of variable names can be passed and a dict with these names as keys and
295 their :func:`repr()` as values will be returned.
295 their :func:`repr()` as values will be returned.
296
296
297 - ``user_expressions``: For more complex expressions that require function
297 - ``user_expressions``: For more complex expressions that require function
298 evaluations, a dict can be provided with string keys and arbitrary python
298 evaluations, a dict can be provided with string keys and arbitrary python
299 expressions as values. The return message will contain also a dict with the
299 expressions as values. The return message will contain also a dict with the
300 same keys and the :func:`repr()` of the evaluated expressions as value.
300 same keys and the :func:`repr()` of the evaluated expressions as value.
301
301
302 With this information, frontends can display any status information they wish
302 With this information, frontends can display any status information they wish
303 in the form that best suits each frontend (a status line, a popup, inline for a
303 in the form that best suits each frontend (a status line, a popup, inline for a
304 terminal, etc).
304 terminal, etc).
305
305
306 .. Note::
306 .. Note::
307
307
308 In order to obtain the current execution counter for the purposes of
308 In order to obtain the current execution counter for the purposes of
309 displaying input prompts, frontends simply make an execution request with an
309 displaying input prompts, frontends simply make an execution request with an
310 empty code string and ``silent=True``.
310 empty code string and ``silent=True``.
311
311
312 Execution semantics
312 Execution semantics
313 ~~~~~~~~~~~~~~~~~~~
313 ~~~~~~~~~~~~~~~~~~~
314
314
315 When the silent flag is false, the execution of use code consists of the
315 When the silent flag is false, the execution of use code consists of the
316 following phases (in silent mode, only the ``code`` field is executed):
316 following phases (in silent mode, only the ``code`` field is executed):
317
317
318 1. Run the ``pre_runcode_hook``.
318 1. Run the ``pre_runcode_hook``.
319
319
320 2. Execute the ``code`` field, see below for details.
320 2. Execute the ``code`` field, see below for details.
321
321
322 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
322 3. If #2 succeeds, compute ``user_variables`` and ``user_expressions`` are
323 computed. This ensures that any error in the latter don't harm the main
323 computed. This ensures that any error in the latter don't harm the main
324 code execution.
324 code execution.
325
325
326 4. Call any method registered with :meth:`register_post_execute`.
326 4. Call any method registered with :meth:`register_post_execute`.
327
327
328 .. warning::
328 .. warning::
329
329
330 The API for running code before/after the main code block is likely to
330 The API for running code before/after the main code block is likely to
331 change soon. Both the ``pre_runcode_hook`` and the
331 change soon. Both the ``pre_runcode_hook`` and the
332 :meth:`register_post_execute` are susceptible to modification, as we find a
332 :meth:`register_post_execute` are susceptible to modification, as we find a
333 consistent model for both.
333 consistent model for both.
334
334
335 To understand how the ``code`` field is executed, one must know that Python
335 To understand how the ``code`` field is executed, one must know that Python
336 code can be compiled in one of three modes (controlled by the ``mode`` argument
336 code can be compiled in one of three modes (controlled by the ``mode`` argument
337 to the :func:`compile` builtin):
337 to the :func:`compile` builtin):
338
338
339 *single*
339 *single*
340 Valid for a single interactive statement (though the source can contain
340 Valid for a single interactive statement (though the source can contain
341 multiple lines, such as a for loop). When compiled in this mode, the
341 multiple lines, such as a for loop). When compiled in this mode, the
342 generated bytecode contains special instructions that trigger the calling of
342 generated bytecode contains special instructions that trigger the calling of
343 :func:`sys.displayhook` for any expression in the block that returns a value.
343 :func:`sys.displayhook` for any expression in the block that returns a value.
344 This means that a single statement can actually produce multiple calls to
344 This means that a single statement can actually produce multiple calls to
345 :func:`sys.displayhook`, if for example it contains a loop where each
345 :func:`sys.displayhook`, if for example it contains a loop where each
346 iteration computes an unassigned expression would generate 10 calls::
346 iteration computes an unassigned expression would generate 10 calls::
347
347
348 for i in range(10):
348 for i in range(10):
349 i**2
349 i**2
350
350
351 *exec*
351 *exec*
352 An arbitrary amount of source code, this is how modules are compiled.
352 An arbitrary amount of source code, this is how modules are compiled.
353 :func:`sys.displayhook` is *never* implicitly called.
353 :func:`sys.displayhook` is *never* implicitly called.
354
354
355 *eval*
355 *eval*
356 A single expression that returns a value. :func:`sys.displayhook` is *never*
356 A single expression that returns a value. :func:`sys.displayhook` is *never*
357 implicitly called.
357 implicitly called.
358
358
359
359
360 The ``code`` field is split into individual blocks each of which is valid for
360 The ``code`` field is split into individual blocks each of which is valid for
361 execution in 'single' mode, and then:
361 execution in 'single' mode, and then:
362
362
363 - If there is only a single block: it is executed in 'single' mode.
363 - If there is only a single block: it is executed in 'single' mode.
364
364
365 - If there is more than one block:
365 - If there is more than one block:
366
366
367 * if the last one is a single line long, run all but the last in 'exec' mode
367 * if the last one is a single line long, run all but the last in 'exec' mode
368 and the very last one in 'single' mode. This makes it easy to type simple
368 and the very last one in 'single' mode. This makes it easy to type simple
369 expressions at the end to see computed values.
369 expressions at the end to see computed values.
370
370
371 * if the last one is no more than two lines long, run all but the last in
371 * if the last one is no more than two lines long, run all but the last in
372 'exec' mode and the very last one in 'single' mode. This makes it easy to
372 'exec' mode and the very last one in 'single' mode. This makes it easy to
373 type simple expressions at the end to see computed values. - otherwise
373 type simple expressions at the end to see computed values. - otherwise
374 (last one is also multiline), run all in 'exec' mode
374 (last one is also multiline), run all in 'exec' mode
375
375
376 * otherwise (last one is also multiline), run all in 'exec' mode as a single
376 * otherwise (last one is also multiline), run all in 'exec' mode as a single
377 unit.
377 unit.
378
378
379 Any error in retrieving the ``user_variables`` or evaluating the
379 Any error in retrieving the ``user_variables`` or evaluating the
380 ``user_expressions`` will result in a simple error message in the return fields
380 ``user_expressions`` will result in a simple error message in the return fields
381 of the form::
381 of the form::
382
382
383 [ERROR] ExceptionType: Exception message
383 [ERROR] ExceptionType: Exception message
384
384
385 The user can simply send the same variable name or expression for evaluation to
385 The user can simply send the same variable name or expression for evaluation to
386 see a regular traceback.
386 see a regular traceback.
387
387
388 Errors in any registered post_execute functions are also reported similarly,
388 Errors in any registered post_execute functions are also reported similarly,
389 and the failing function is removed from the post_execution set so that it does
389 and the failing function is removed from the post_execution set so that it does
390 not continue triggering failures.
390 not continue triggering failures.
391
391
392 Upon completion of the execution request, the kernel *always* sends a reply,
392 Upon completion of the execution request, the kernel *always* sends a reply,
393 with a status code indicating what happened and additional data depending on
393 with a status code indicating what happened and additional data depending on
394 the outcome. See :ref:`below <execution_results>` for the possible return
394 the outcome. See :ref:`below <execution_results>` for the possible return
395 codes and associated data.
395 codes and associated data.
396
396
397
397
398 Execution counter (old prompt number)
398 Execution counter (old prompt number)
399 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
399 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
400
400
401 The kernel has a single, monotonically increasing counter of all execution
401 The kernel has a single, monotonically increasing counter of all execution
402 requests that are made with ``store_history=True``. This counter is used to populate
402 requests that are made with ``store_history=True``. This counter is used to populate
403 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
403 the ``In[n]``, ``Out[n]`` and ``_n`` variables, so clients will likely want to
404 display it in some form to the user, which will typically (but not necessarily)
404 display it in some form to the user, which will typically (but not necessarily)
405 be done in the prompts. The value of this counter will be returned as the
405 be done in the prompts. The value of this counter will be returned as the
406 ``execution_count`` field of all ``execute_reply`` messages.
406 ``execution_count`` field of all ``execute_reply`` messages.
407
407
408 .. _execution_results:
408 .. _execution_results:
409
409
410 Execution results
410 Execution results
411 ~~~~~~~~~~~~~~~~~
411 ~~~~~~~~~~~~~~~~~
412
412
413 Message type: ``execute_reply``::
413 Message type: ``execute_reply``::
414
414
415 content = {
415 content = {
416 # One of: 'ok' OR 'error' OR 'abort'
416 # One of: 'ok' OR 'error' OR 'abort'
417 'status' : str,
417 'status' : str,
418
418
419 # The global kernel counter that increases by one with each request that
419 # The global kernel counter that increases by one with each request that
420 # stores history. This will typically be used by clients to display
420 # stores history. This will typically be used by clients to display
421 # prompt numbers to the user. If the request did not store history, this will
421 # prompt numbers to the user. If the request did not store history, this will
422 # be the current value of the counter in the kernel.
422 # be the current value of the counter in the kernel.
423 'execution_count' : int,
423 'execution_count' : int,
424 }
424 }
425
425
426 When status is 'ok', the following extra fields are present::
426 When status is 'ok', the following extra fields are present::
427
427
428 {
428 {
429 # 'payload' will be a list of payload dicts.
429 # 'payload' will be a list of payload dicts.
430 # Each execution payload is a dict with string keys that may have been
430 # Each execution payload is a dict with string keys that may have been
431 # produced by the code being executed. It is retrieved by the kernel at
431 # produced by the code being executed. It is retrieved by the kernel at
432 # the end of the execution and sent back to the front end, which can take
432 # the end of the execution and sent back to the front end, which can take
433 # action on it as needed. See main text for further details.
433 # action on it as needed. See main text for further details.
434 'payload' : list(dict),
434 'payload' : list(dict),
435
435
436 # Results for the user_variables and user_expressions.
436 # Results for the user_variables and user_expressions.
437 'user_variables' : dict,
437 'user_variables' : dict,
438 'user_expressions' : dict,
438 'user_expressions' : dict,
439 }
439 }
440
440
441 .. admonition:: Execution payloads
441 .. admonition:: Execution payloads
442
442
443 The notion of an 'execution payload' is different from a return value of a
443 The notion of an 'execution payload' is different from a return value of a
444 given set of code, which normally is just displayed on the pyout stream
444 given set of code, which normally is just displayed on the pyout stream
445 through the PUB socket. The idea of a payload is to allow special types of
445 through the PUB socket. The idea of a payload is to allow special types of
446 code, typically magics, to populate a data container in the IPython kernel
446 code, typically magics, to populate a data container in the IPython kernel
447 that will be shipped back to the caller via this channel. The kernel
447 that will be shipped back to the caller via this channel. The kernel
448 has an API for this in the PayloadManager::
448 has an API for this in the PayloadManager::
449
449
450 ip.payload_manager.write_payload(payload_dict)
450 ip.payload_manager.write_payload(payload_dict)
451
451
452 which appends a dictionary to the list of payloads.
452 which appends a dictionary to the list of payloads.
453
453
454 The payload API is not yet stabilized,
454 The payload API is not yet stabilized,
455 and should probably not be supported by non-Python kernels at this time.
455 and should probably not be supported by non-Python kernels at this time.
456 In such cases, the payload list should always be empty.
456 In such cases, the payload list should always be empty.
457
457
458
458
459 When status is 'error', the following extra fields are present::
459 When status is 'error', the following extra fields are present::
460
460
461 {
461 {
462 'ename' : str, # Exception name, as a string
462 'ename' : str, # Exception name, as a string
463 'evalue' : str, # Exception value, as a string
463 'evalue' : str, # Exception value, as a string
464
464
465 # The traceback will contain a list of frames, represented each as a
465 # The traceback will contain a list of frames, represented each as a
466 # string. For now we'll stick to the existing design of ultraTB, which
466 # string. For now we'll stick to the existing design of ultraTB, which
467 # controls exception level of detail statefully. But eventually we'll
467 # controls exception level of detail statefully. But eventually we'll
468 # want to grow into a model where more information is collected and
468 # want to grow into a model where more information is collected and
469 # packed into the traceback object, with clients deciding how little or
469 # packed into the traceback object, with clients deciding how little or
470 # how much of it to unpack. But for now, let's start with a simple list
470 # how much of it to unpack. But for now, let's start with a simple list
471 # of strings, since that requires only minimal changes to ultratb as
471 # of strings, since that requires only minimal changes to ultratb as
472 # written.
472 # written.
473 'traceback' : list,
473 'traceback' : list,
474 }
474 }
475
475
476
476
477 When status is 'abort', there are for now no additional data fields. This
477 When status is 'abort', there are for now no additional data fields. This
478 happens when the kernel was interrupted by a signal.
478 happens when the kernel was interrupted by a signal.
479
479
480
480
481 Object information
481 Object information
482 ------------------
482 ------------------
483
483
484 One of IPython's most used capabilities is the introspection of Python objects
484 One of IPython's most used capabilities is the introspection of Python objects
485 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
485 in the user's namespace, typically invoked via the ``?`` and ``??`` characters
486 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
486 (which in reality are shorthands for the ``%pinfo`` magic). This is used often
487 enough that it warrants an explicit message type, especially because frontends
487 enough that it warrants an explicit message type, especially because frontends
488 may want to get object information in response to user keystrokes (like Tab or
488 may want to get object information in response to user keystrokes (like Tab or
489 F1) besides from the user explicitly typing code like ``x??``.
489 F1) besides from the user explicitly typing code like ``x??``.
490
490
491 Message type: ``object_info_request``::
491 Message type: ``object_info_request``::
492
492
493 content = {
493 content = {
494 # The (possibly dotted) name of the object to be searched in all
494 # The (possibly dotted) name of the object to be searched in all
495 # relevant namespaces
495 # relevant namespaces
496 'oname' : str,
496 'oname' : str,
497
497
498 # The level of detail desired. The default (0) is equivalent to typing
498 # The level of detail desired. The default (0) is equivalent to typing
499 # 'x?' at the prompt, 1 is equivalent to 'x??'.
499 # 'x?' at the prompt, 1 is equivalent to 'x??'.
500 'detail_level' : int,
500 'detail_level' : int,
501 }
501 }
502
502
503 The returned information will be a dictionary with keys very similar to the
503 The returned information will be a dictionary with keys very similar to the
504 field names that IPython prints at the terminal.
504 field names that IPython prints at the terminal.
505
505
506 Message type: ``object_info_reply``::
506 Message type: ``object_info_reply``::
507
507
508 content = {
508 content = {
509 # The name the object was requested under
509 # The name the object was requested under
510 'name' : str,
510 'name' : str,
511
511
512 # Boolean flag indicating whether the named object was found or not. If
512 # Boolean flag indicating whether the named object was found or not. If
513 # it's false, all other fields will be empty.
513 # it's false, all other fields will be empty.
514 'found' : bool,
514 'found' : bool,
515
515
516 # Flags for magics and system aliases
516 # Flags for magics and system aliases
517 'ismagic' : bool,
517 'ismagic' : bool,
518 'isalias' : bool,
518 'isalias' : bool,
519
519
520 # The name of the namespace where the object was found ('builtin',
520 # The name of the namespace where the object was found ('builtin',
521 # 'magics', 'alias', 'interactive', etc.)
521 # 'magics', 'alias', 'interactive', etc.)
522 'namespace' : str,
522 'namespace' : str,
523
523
524 # The type name will be type.__name__ for normal Python objects, but it
524 # The type name will be type.__name__ for normal Python objects, but it
525 # can also be a string like 'Magic function' or 'System alias'
525 # can also be a string like 'Magic function' or 'System alias'
526 'type_name' : str,
526 'type_name' : str,
527
527
528 # The string form of the object, possibly truncated for length if
528 # The string form of the object, possibly truncated for length if
529 # detail_level is 0
529 # detail_level is 0
530 'string_form' : str,
530 'string_form' : str,
531
531
532 # For objects with a __class__ attribute this will be set
532 # For objects with a __class__ attribute this will be set
533 'base_class' : str,
533 'base_class' : str,
534
534
535 # For objects with a __len__ attribute this will be set
535 # For objects with a __len__ attribute this will be set
536 'length' : int,
536 'length' : int,
537
537
538 # If the object is a function, class or method whose file we can find,
538 # If the object is a function, class or method whose file we can find,
539 # we give its full path
539 # we give its full path
540 'file' : str,
540 'file' : str,
541
541
542 # For pure Python callable objects, we can reconstruct the object
542 # For pure Python callable objects, we can reconstruct the object
543 # definition line which provides its call signature. For convenience this
543 # definition line which provides its call signature. For convenience this
544 # is returned as a single 'definition' field, but below the raw parts that
544 # is returned as a single 'definition' field, but below the raw parts that
545 # compose it are also returned as the argspec field.
545 # compose it are also returned as the argspec field.
546 'definition' : str,
546 'definition' : str,
547
547
548 # The individual parts that together form the definition string. Clients
548 # The individual parts that together form the definition string. Clients
549 # with rich display capabilities may use this to provide a richer and more
549 # with rich display capabilities may use this to provide a richer and more
550 # precise representation of the definition line (e.g. by highlighting
550 # precise representation of the definition line (e.g. by highlighting
551 # arguments based on the user's cursor position). For non-callable
551 # arguments based on the user's cursor position). For non-callable
552 # objects, this field is empty.
552 # objects, this field is empty.
553 'argspec' : { # The names of all the arguments
553 'argspec' : { # The names of all the arguments
554 args : list,
554 args : list,
555 # The name of the varargs (*args), if any
555 # The name of the varargs (*args), if any
556 varargs : str,
556 varargs : str,
557 # The name of the varkw (**kw), if any
557 # The name of the varkw (**kw), if any
558 varkw : str,
558 varkw : str,
559 # The values (as strings) of all default arguments. Note
559 # The values (as strings) of all default arguments. Note
560 # that these must be matched *in reverse* with the 'args'
560 # that these must be matched *in reverse* with the 'args'
561 # list above, since the first positional args have no default
561 # list above, since the first positional args have no default
562 # value at all.
562 # value at all.
563 defaults : list,
563 defaults : list,
564 },
564 },
565
565
566 # For instances, provide the constructor signature (the definition of
566 # For instances, provide the constructor signature (the definition of
567 # the __init__ method):
567 # the __init__ method):
568 'init_definition' : str,
568 'init_definition' : str,
569
569
570 # Docstrings: for any object (function, method, module, package) with a
570 # Docstrings: for any object (function, method, module, package) with a
571 # docstring, we show it. But in addition, we may provide additional
571 # docstring, we show it. But in addition, we may provide additional
572 # docstrings. For example, for instances we will show the constructor
572 # docstrings. For example, for instances we will show the constructor
573 # and class docstrings as well, if available.
573 # and class docstrings as well, if available.
574 'docstring' : str,
574 'docstring' : str,
575
575
576 # For instances, provide the constructor and class docstrings
576 # For instances, provide the constructor and class docstrings
577 'init_docstring' : str,
577 'init_docstring' : str,
578 'class_docstring' : str,
578 'class_docstring' : str,
579
579
580 # If it's a callable object whose call method has a separate docstring and
580 # If it's a callable object whose call method has a separate docstring and
581 # definition line:
581 # definition line:
582 'call_def' : str,
582 'call_def' : str,
583 'call_docstring' : str,
583 'call_docstring' : str,
584
584
585 # If detail_level was 1, we also try to find the source code that
585 # If detail_level was 1, we also try to find the source code that
586 # defines the object, if possible. The string 'None' will indicate
586 # defines the object, if possible. The string 'None' will indicate
587 # that no source was found.
587 # that no source was found.
588 'source' : str,
588 'source' : str,
589 }
589 }
590
590
591
591
592 Complete
592 Complete
593 --------
593 --------
594
594
595 Message type: ``complete_request``::
595 Message type: ``complete_request``::
596
596
597 content = {
597 content = {
598 # The text to be completed, such as 'a.is'
598 # The text to be completed, such as 'a.is'
599 # this may be an empty string if the frontend does not do any lexing,
599 # this may be an empty string if the frontend does not do any lexing,
600 # in which case the kernel must figure out the completion
600 # in which case the kernel must figure out the completion
601 # based on 'line' and 'cursor_pos'.
601 # based on 'line' and 'cursor_pos'.
602 'text' : str,
602 'text' : str,
603
603
604 # The full line, such as 'print a.is'. This allows completers to
604 # The full line, such as 'print a.is'. This allows completers to
605 # make decisions that may require information about more than just the
605 # make decisions that may require information about more than just the
606 # current word.
606 # current word.
607 'line' : str,
607 'line' : str,
608
608
609 # The entire block of text where the line is. This may be useful in the
609 # The entire block of text where the line is. This may be useful in the
610 # case of multiline completions where more context may be needed. Note: if
610 # case of multiline completions where more context may be needed. Note: if
611 # in practice this field proves unnecessary, remove it to lighten the
611 # in practice this field proves unnecessary, remove it to lighten the
612 # messages.
612 # messages.
613
613
614 'block' : str or null/None,
614 'block' : str or null/None,
615
615
616 # The position of the cursor where the user hit 'TAB' on the line.
616 # The position of the cursor where the user hit 'TAB' on the line.
617 'cursor_pos' : int,
617 'cursor_pos' : int,
618 }
618 }
619
619
620 Message type: ``complete_reply``::
620 Message type: ``complete_reply``::
621
621
622 content = {
622 content = {
623 # The list of all matches to the completion request, such as
623 # The list of all matches to the completion request, such as
624 # ['a.isalnum', 'a.isalpha'] for the above example.
624 # ['a.isalnum', 'a.isalpha'] for the above example.
625 'matches' : list,
625 'matches' : list,
626
626
627 # the substring of the matched text
627 # the substring of the matched text
628 # this is typically the common prefix of the matches,
628 # this is typically the common prefix of the matches,
629 # and the text that is already in the block that would be replaced by the full completion.
629 # and the text that is already in the block that would be replaced by the full completion.
630 # This would be 'a.is' in the above example.
630 # This would be 'a.is' in the above example.
631 'text' : str,
631 'text' : str,
632
632
633 # status should be 'ok' unless an exception was raised during the request,
633 # status should be 'ok' unless an exception was raised during the request,
634 # in which case it should be 'error', along with the usual error message content
634 # in which case it should be 'error', along with the usual error message content
635 # in other messages.
635 # in other messages.
636 'status' : 'ok'
636 'status' : 'ok'
637 }
637 }
638
638
639
639
640 History
640 History
641 -------
641 -------
642
642
643 For clients to explicitly request history from a kernel. The kernel has all
643 For clients to explicitly request history from a kernel. The kernel has all
644 the actual execution history stored in a single location, so clients can
644 the actual execution history stored in a single location, so clients can
645 request it from the kernel when needed.
645 request it from the kernel when needed.
646
646
647 Message type: ``history_request``::
647 Message type: ``history_request``::
648
648
649 content = {
649 content = {
650
650
651 # If True, also return output history in the resulting dict.
651 # If True, also return output history in the resulting dict.
652 'output' : bool,
652 'output' : bool,
653
653
654 # If True, return the raw input history, else the transformed input.
654 # If True, return the raw input history, else the transformed input.
655 'raw' : bool,
655 'raw' : bool,
656
656
657 # So far, this can be 'range', 'tail' or 'search'.
657 # So far, this can be 'range', 'tail' or 'search'.
658 'hist_access_type' : str,
658 'hist_access_type' : str,
659
659
660 # If hist_access_type is 'range', get a range of input cells. session can
660 # If hist_access_type is 'range', get a range of input cells. session can
661 # be a positive session number, or a negative number to count back from
661 # be a positive session number, or a negative number to count back from
662 # the current session.
662 # the current session.
663 'session' : int,
663 'session' : int,
664 # start and stop are line numbers within that session.
664 # start and stop are line numbers within that session.
665 'start' : int,
665 'start' : int,
666 'stop' : int,
666 'stop' : int,
667
667
668 # If hist_access_type is 'tail' or 'search', get the last n cells.
668 # If hist_access_type is 'tail' or 'search', get the last n cells.
669 'n' : int,
669 'n' : int,
670
670
671 # If hist_access_type is 'search', get cells matching the specified glob
671 # If hist_access_type is 'search', get cells matching the specified glob
672 # pattern (with * and ? as wildcards).
672 # pattern (with * and ? as wildcards).
673 'pattern' : str,
673 'pattern' : str,
674
674
675 # If hist_access_type is 'search' and unique is true, do not
675 # If hist_access_type is 'search' and unique is true, do not
676 # include duplicated history. Default is false.
676 # include duplicated history. Default is false.
677 'unique' : bool,
677 'unique' : bool,
678
678
679 }
679 }
680
680
681 .. versionadded:: 4.0
681 .. versionadded:: 4.0
682 The key ``unique`` for ``history_request``.
682 The key ``unique`` for ``history_request``.
683
683
684 Message type: ``history_reply``::
684 Message type: ``history_reply``::
685
685
686 content = {
686 content = {
687 # A list of 3 tuples, either:
687 # A list of 3 tuples, either:
688 # (session, line_number, input) or
688 # (session, line_number, input) or
689 # (session, line_number, (input, output)),
689 # (session, line_number, (input, output)),
690 # depending on whether output was False or True, respectively.
690 # depending on whether output was False or True, respectively.
691 'history' : list,
691 'history' : list,
692 }
692 }
693
693
694
694
695 Connect
695 Connect
696 -------
696 -------
697
697
698 When a client connects to the request/reply socket of the kernel, it can issue
698 When a client connects to the request/reply socket of the kernel, it can issue
699 a connect request to get basic information about the kernel, such as the ports
699 a connect request to get basic information about the kernel, such as the ports
700 the other ZeroMQ sockets are listening on. This allows clients to only have
700 the other ZeroMQ sockets are listening on. This allows clients to only have
701 to know about a single port (the shell channel) to connect to a kernel.
701 to know about a single port (the shell channel) to connect to a kernel.
702
702
703 Message type: ``connect_request``::
703 Message type: ``connect_request``::
704
704
705 content = {
705 content = {
706 }
706 }
707
707
708 Message type: ``connect_reply``::
708 Message type: ``connect_reply``::
709
709
710 content = {
710 content = {
711 'shell_port' : int, # The port the shell ROUTER socket is listening on.
711 'shell_port' : int, # The port the shell ROUTER socket is listening on.
712 'iopub_port' : int, # The port the PUB socket is listening on.
712 'iopub_port' : int, # The port the PUB socket is listening on.
713 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
713 'stdin_port' : int, # The port the stdin ROUTER socket is listening on.
714 'hb_port' : int, # The port the heartbeat socket is listening on.
714 'hb_port' : int, # The port the heartbeat socket is listening on.
715 }
715 }
716
716
717
717
718 Kernel info
718 Kernel info
719 -----------
719 -----------
720
720
721 If a client needs to know information about the kernel, it can
721 If a client needs to know information about the kernel, it can
722 make a request of the kernel's information.
722 make a request of the kernel's information.
723 This message can be used to fetch core information of the
723 This message can be used to fetch core information of the
724 kernel, including language (e.g., Python), language version number and
724 kernel, including language (e.g., Python), language version number and
725 IPython version number, and the IPython message spec version number.
725 IPython version number, and the IPython message spec version number.
726
726
727 Message type: ``kernel_info_request``::
727 Message type: ``kernel_info_request``::
728
728
729 content = {
729 content = {
730 }
730 }
731
731
732 Message type: ``kernel_info_reply``::
732 Message type: ``kernel_info_reply``::
733
733
734 content = {
734 content = {
735 # Version of messaging protocol (mandatory).
735 # Version of messaging protocol (mandatory).
736 # The first integer indicates major version. It is incremented when
736 # The first integer indicates major version. It is incremented when
737 # there is any backward incompatible change.
737 # there is any backward incompatible change.
738 # The second integer indicates minor version. It is incremented when
738 # The second integer indicates minor version. It is incremented when
739 # there is any backward compatible change.
739 # there is any backward compatible change.
740 'protocol_version': [int, int],
740 'protocol_version': [int, int],
741
741
742 # IPython version number (optional).
742 # IPython version number (optional).
743 # Non-python kernel backend may not have this version number.
743 # Non-python kernel backend may not have this version number.
744 # The last component is an extra field, which may be 'dev' or
744 # The last component is an extra field, which may be 'dev' or
745 # 'rc1' in development version. It is an empty string for
745 # 'rc1' in development version. It is an empty string for
746 # released version.
746 # released version.
747 'ipython_version': [int, int, int, str],
747 'ipython_version': [int, int, int, str],
748
748
749 # Language version number (mandatory).
749 # Language version number (mandatory).
750 # It is Python version number (e.g., [2, 7, 3]) for the kernel
750 # It is Python version number (e.g., [2, 7, 3]) for the kernel
751 # included in IPython.
751 # included in IPython.
752 'language_version': [int, ...],
752 'language_version': [int, ...],
753
753
754 # Programming language in which kernel is implemented (mandatory).
754 # Programming language in which kernel is implemented (mandatory).
755 # Kernel included in IPython returns 'python'.
755 # Kernel included in IPython returns 'python'.
756 'language': str,
756 'language': str,
757 }
757 }
758
758
759
759
760 Kernel shutdown
760 Kernel shutdown
761 ---------------
761 ---------------
762
762
763 The clients can request the kernel to shut itself down; this is used in
763 The clients can request the kernel to shut itself down; this is used in
764 multiple cases:
764 multiple cases:
765
765
766 - when the user chooses to close the client application via a menu or window
766 - when the user chooses to close the client application via a menu or window
767 control.
767 control.
768 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
768 - when the user types 'exit' or 'quit' (or their uppercase magic equivalents).
769 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
769 - when the user chooses a GUI method (like the 'Ctrl-C' shortcut in the
770 IPythonQt client) to force a kernel restart to get a clean kernel without
770 IPythonQt client) to force a kernel restart to get a clean kernel without
771 losing client-side state like history or inlined figures.
771 losing client-side state like history or inlined figures.
772
772
773 The client sends a shutdown request to the kernel, and once it receives the
773 The client sends a shutdown request to the kernel, and once it receives the
774 reply message (which is otherwise empty), it can assume that the kernel has
774 reply message (which is otherwise empty), it can assume that the kernel has
775 completed shutdown safely.
775 completed shutdown safely.
776
776
777 Upon their own shutdown, client applications will typically execute a last
777 Upon their own shutdown, client applications will typically execute a last
778 minute sanity check and forcefully terminate any kernel that is still alive, to
778 minute sanity check and forcefully terminate any kernel that is still alive, to
779 avoid leaving stray processes in the user's machine.
779 avoid leaving stray processes in the user's machine.
780
780
781 Message type: ``shutdown_request``::
781 Message type: ``shutdown_request``::
782
782
783 content = {
783 content = {
784 'restart' : bool # whether the shutdown is final, or precedes a restart
784 'restart' : bool # whether the shutdown is final, or precedes a restart
785 }
785 }
786
786
787 Message type: ``shutdown_reply``::
787 Message type: ``shutdown_reply``::
788
788
789 content = {
789 content = {
790 'restart' : bool # whether the shutdown is final, or precedes a restart
790 'restart' : bool # whether the shutdown is final, or precedes a restart
791 }
791 }
792
792
793 .. Note::
793 .. Note::
794
794
795 When the clients detect a dead kernel thanks to inactivity on the heartbeat
795 When the clients detect a dead kernel thanks to inactivity on the heartbeat
796 socket, they simply send a forceful process termination signal, since a dead
796 socket, they simply send a forceful process termination signal, since a dead
797 process is unlikely to respond in any useful way to messages.
797 process is unlikely to respond in any useful way to messages.
798
798
799
799
800 Messages on the PUB/SUB socket
800 Messages on the PUB/SUB socket
801 ==============================
801 ==============================
802
802
803 Streams (stdout, stderr, etc)
803 Streams (stdout, stderr, etc)
804 ------------------------------
804 ------------------------------
805
805
806 Message type: ``stream``::
806 Message type: ``stream``::
807
807
808 content = {
808 content = {
809 # The name of the stream is one of 'stdout', 'stderr'
809 # The name of the stream is one of 'stdout', 'stderr'
810 'name' : str,
810 'name' : str,
811
811
812 # The data is an arbitrary string to be written to that stream
812 # The data is an arbitrary string to be written to that stream
813 'data' : str,
813 'data' : str,
814 }
814 }
815
815
816 Display Data
816 Display Data
817 ------------
817 ------------
818
818
819 This type of message is used to bring back data that should be diplayed (text,
819 This type of message is used to bring back data that should be diplayed (text,
820 html, svg, etc.) in the frontends. This data is published to all frontends.
820 html, svg, etc.) in the frontends. This data is published to all frontends.
821 Each message can have multiple representations of the data; it is up to the
821 Each message can have multiple representations of the data; it is up to the
822 frontend to decide which to use and how. A single message should contain all
822 frontend to decide which to use and how. A single message should contain all
823 possible representations of the same information. Each representation should
823 possible representations of the same information. Each representation should
824 be a JSON'able data structure, and should be a valid MIME type.
824 be a JSON'able data structure, and should be a valid MIME type.
825
825
826 Some questions remain about this design:
826 Some questions remain about this design:
827
827
828 * Do we use this message type for pyout/displayhook? Probably not, because
828 * Do we use this message type for pyout/displayhook? Probably not, because
829 the displayhook also has to handle the Out prompt display. On the other hand
829 the displayhook also has to handle the Out prompt display. On the other hand
830 we could put that information into the metadata secion.
830 we could put that information into the metadata secion.
831
831
832 Message type: ``display_data``::
832 Message type: ``display_data``::
833
833
834 content = {
834 content = {
835
835
836 # Who create the data
836 # Who create the data
837 'source' : str,
837 'source' : str,
838
838
839 # The data dict contains key/value pairs, where the kids are MIME
839 # The data dict contains key/value pairs, where the kids are MIME
840 # types and the values are the raw data of the representation in that
840 # types and the values are the raw data of the representation in that
841 # format.
841 # format.
842 'data' : dict,
842 'data' : dict,
843
843
844 # Any metadata that describes the data
844 # Any metadata that describes the data
845 'metadata' : dict
845 'metadata' : dict
846 }
846 }
847
847
848
848
849 The ``metadata`` contains any metadata that describes the output.
849 The ``metadata`` contains any metadata that describes the output.
850 Global keys are assumed to apply to the output as a whole.
850 Global keys are assumed to apply to the output as a whole.
851 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
851 The ``metadata`` dict can also contain mime-type keys, which will be sub-dictionaries,
852 which are interpreted as applying only to output of that type.
852 which are interpreted as applying only to output of that type.
853 Third parties should put any data they write into a single dict
853 Third parties should put any data they write into a single dict
854 with a reasonably unique name to avoid conflicts.
854 with a reasonably unique name to avoid conflicts.
855
855
856 The only metadata keys currently defined in IPython are the width and height
856 The only metadata keys currently defined in IPython are the width and height
857 of images::
857 of images::
858
858
859 'metadata' : {
859 'metadata' : {
860 'image/png' : {
860 'image/png' : {
861 'width': 640,
861 'width': 640,
862 'height': 480
862 'height': 480
863 }
863 }
864 }
864 }
865
865
866
866
867 Raw Data Publication
867 Raw Data Publication
868 --------------------
868 --------------------
869
869
870 ``display_data`` lets you publish *representations* of data, such as images and html.
870 ``display_data`` lets you publish *representations* of data, such as images and html.
871 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
871 This ``data_pub`` message lets you publish *actual raw data*, sent via message buffers.
872
872
873 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
873 data_pub messages are constructed via the :func:`IPython.lib.datapub.publish_data` function:
874
874
875 .. sourcecode:: python
875 .. sourcecode:: python
876
876
877 from IPython.kernel.zmq.datapub import publish_data
877 from IPython.kernel.zmq.datapub import publish_data
878 ns = dict(x=my_array)
878 ns = dict(x=my_array)
879 publish_data(ns)
879 publish_data(ns)
880
880
881
881
882 Message type: ``data_pub``::
882 Message type: ``data_pub``::
883
883
884 content = {
884 content = {
885 # the keys of the data dict, after it has been unserialized
885 # the keys of the data dict, after it has been unserialized
886 keys = ['a', 'b']
886 keys = ['a', 'b']
887 }
887 }
888 # the namespace dict will be serialized in the message buffers,
888 # the namespace dict will be serialized in the message buffers,
889 # which will have a length of at least one
889 # which will have a length of at least one
890 buffers = ['pdict', ...]
890 buffers = ['pdict', ...]
891
891
892
892
893 The interpretation of a sequence of data_pub messages for a given parent request should be
893 The interpretation of a sequence of data_pub messages for a given parent request should be
894 to update a single namespace with subsequent results.
894 to update a single namespace with subsequent results.
895
895
896 .. note::
896 .. note::
897
897
898 No frontends directly handle data_pub messages at this time.
898 No frontends directly handle data_pub messages at this time.
899 It is currently only used by the client/engines in :mod:`IPython.parallel`,
899 It is currently only used by the client/engines in :mod:`IPython.parallel`,
900 where engines may publish *data* to the Client,
900 where engines may publish *data* to the Client,
901 of which the Client can then publish *representations* via ``display_data``
901 of which the Client can then publish *representations* via ``display_data``
902 to various frontends.
902 to various frontends.
903
903
904 Python inputs
904 Python inputs
905 -------------
905 -------------
906
906
907 These messages are the re-broadcast of the ``execute_request``.
907 These messages are the re-broadcast of the ``execute_request``.
908
908
909 Message type: ``pyin``::
909 Message type: ``pyin``::
910
910
911 content = {
911 content = {
912 'code' : str, # Source code to be executed, one or more lines
912 'code' : str, # Source code to be executed, one or more lines
913
913
914 # The counter for this execution is also provided so that clients can
914 # The counter for this execution is also provided so that clients can
915 # display it, since IPython automatically creates variables called _iN
915 # display it, since IPython automatically creates variables called _iN
916 # (for input prompt In[N]).
916 # (for input prompt In[N]).
917 'execution_count' : int
917 'execution_count' : int
918 }
918 }
919
919
920 Python outputs
920 Python outputs
921 --------------
921 --------------
922
922
923 When Python produces output from code that has been compiled in with the
923 When Python produces output from code that has been compiled in with the
924 'single' flag to :func:`compile`, any expression that produces a value (such as
924 'single' flag to :func:`compile`, any expression that produces a value (such as
925 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
925 ``1+1``) is passed to ``sys.displayhook``, which is a callable that can do with
926 this value whatever it wants. The default behavior of ``sys.displayhook`` in
926 this value whatever it wants. The default behavior of ``sys.displayhook`` in
927 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
927 the Python interactive prompt is to print to ``sys.stdout`` the :func:`repr` of
928 the value as long as it is not ``None`` (which isn't printed at all). In our
928 the value as long as it is not ``None`` (which isn't printed at all). In our
929 case, the kernel instantiates as ``sys.displayhook`` an object which has
929 case, the kernel instantiates as ``sys.displayhook`` an object which has
930 similar behavior, but which instead of printing to stdout, broadcasts these
930 similar behavior, but which instead of printing to stdout, broadcasts these
931 values as ``pyout`` messages for clients to display appropriately.
931 values as ``pyout`` messages for clients to display appropriately.
932
932
933 IPython's displayhook can handle multiple simultaneous formats depending on its
933 IPython's displayhook can handle multiple simultaneous formats depending on its
934 configuration. The default pretty-printed repr text is always given with the
934 configuration. The default pretty-printed repr text is always given with the
935 ``data`` entry in this message. Any other formats are provided in the
935 ``data`` entry in this message. Any other formats are provided in the
936 ``extra_formats`` list. Frontends are free to display any or all of these
936 ``extra_formats`` list. Frontends are free to display any or all of these
937 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
937 according to its capabilities. ``extra_formats`` list contains 3-tuples of an ID
938 string, a type string, and the data. The ID is unique to the formatter
938 string, a type string, and the data. The ID is unique to the formatter
939 implementation that created the data. Frontends will typically ignore the ID
939 implementation that created the data. Frontends will typically ignore the ID
940 unless if it has requested a particular formatter. The type string tells the
940 unless if it has requested a particular formatter. The type string tells the
941 frontend how to interpret the data. It is often, but not always a MIME type.
941 frontend how to interpret the data. It is often, but not always a MIME type.
942 Frontends should ignore types that it does not understand. The data itself is
942 Frontends should ignore types that it does not understand. The data itself is
943 any JSON object and depends on the format. It is often, but not always a string.
943 any JSON object and depends on the format. It is often, but not always a string.
944
944
945 Message type: ``pyout``::
945 Message type: ``pyout``::
946
946
947 content = {
947 content = {
948
948
949 # The counter for this execution is also provided so that clients can
949 # The counter for this execution is also provided so that clients can
950 # display it, since IPython automatically creates variables called _N
950 # display it, since IPython automatically creates variables called _N
951 # (for prompt N).
951 # (for prompt N).
952 'execution_count' : int,
952 'execution_count' : int,
953
953
954 # data and metadata are identical to a display_data message.
954 # data and metadata are identical to a display_data message.
955 # the object being displayed is that passed to the display hook,
955 # the object being displayed is that passed to the display hook,
956 # i.e. the *result* of the execution.
956 # i.e. the *result* of the execution.
957 'data' : dict,
957 'data' : dict,
958 'metadata' : dict,
958 'metadata' : dict,
959 }
959 }
960
960
961 Python errors
961 Python errors
962 -------------
962 -------------
963
963
964 When an error occurs during code execution
964 When an error occurs during code execution
965
965
966 Message type: ``pyerr``::
966 Message type: ``pyerr``::
967
967
968 content = {
968 content = {
969 # Similar content to the execute_reply messages for the 'error' case,
969 # Similar content to the execute_reply messages for the 'error' case,
970 # except the 'status' field is omitted.
970 # except the 'status' field is omitted.
971 }
971 }
972
972
973 Kernel status
973 Kernel status
974 -------------
974 -------------
975
975
976 This message type is used by frontends to monitor the status of the kernel.
976 This message type is used by frontends to monitor the status of the kernel.
977
977
978 Message type: ``status``::
978 Message type: ``status``::
979
979
980 content = {
980 content = {
981 # When the kernel starts to execute code, it will enter the 'busy'
981 # When the kernel starts to execute code, it will enter the 'busy'
982 # state and when it finishes, it will enter the 'idle' state.
982 # state and when it finishes, it will enter the 'idle' state.
983 # The kernel will publish state 'starting' exactly once at process startup.
983 # The kernel will publish state 'starting' exactly once at process startup.
984 execution_state : ('busy', 'idle', 'starting')
984 execution_state : ('busy', 'idle', 'starting')
985 }
985 }
986
986
987 Clear output
988 ------------
989
990 This message type is used to clear the output that is visible on the frontend.
991
992 Message type: ``clear_output``::
993
994 content = {
995
996 # Wait to clear the output until new output is available. Clears the
997 # existing output immediately before the new output is displayed.
998 # Useful for creating simple animations with minimal flickering.
999 'wait' : bool,
1000 }
987
1001
988 Messages on the stdin ROUTER/DEALER sockets
1002 Messages on the stdin ROUTER/DEALER sockets
989 ===========================================
1003 ===========================================
990
1004
991 This is a socket where the request/reply pattern goes in the opposite direction:
1005 This is a socket where the request/reply pattern goes in the opposite direction:
992 from the kernel to a *single* frontend, and its purpose is to allow
1006 from the kernel to a *single* frontend, and its purpose is to allow
993 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
1007 ``raw_input`` and similar operations that read from ``sys.stdin`` on the kernel
994 to be fulfilled by the client. The request should be made to the frontend that
1008 to be fulfilled by the client. The request should be made to the frontend that
995 made the execution request that prompted ``raw_input`` to be called. For now we
1009 made the execution request that prompted ``raw_input`` to be called. For now we
996 will keep these messages as simple as possible, since they only mean to convey
1010 will keep these messages as simple as possible, since they only mean to convey
997 the ``raw_input(prompt)`` call.
1011 the ``raw_input(prompt)`` call.
998
1012
999 Message type: ``input_request``::
1013 Message type: ``input_request``::
1000
1014
1001 content = { 'prompt' : str }
1015 content = { 'prompt' : str }
1002
1016
1003 Message type: ``input_reply``::
1017 Message type: ``input_reply``::
1004
1018
1005 content = { 'value' : str }
1019 content = { 'value' : str }
1006
1020
1007 .. Note::
1021 .. Note::
1008
1022
1009 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
1023 We do not explicitly try to forward the raw ``sys.stdin`` object, because in
1010 practice the kernel should behave like an interactive program. When a
1024 practice the kernel should behave like an interactive program. When a
1011 program is opened on the console, the keyboard effectively takes over the
1025 program is opened on the console, the keyboard effectively takes over the
1012 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1026 ``stdin`` file descriptor, and it can't be used for raw reading anymore.
1013 Since the IPython kernel effectively behaves like a console program (albeit
1027 Since the IPython kernel effectively behaves like a console program (albeit
1014 one whose "keyboard" is actually living in a separate process and
1028 one whose "keyboard" is actually living in a separate process and
1015 transported over the zmq connection), raw ``stdin`` isn't expected to be
1029 transported over the zmq connection), raw ``stdin`` isn't expected to be
1016 available.
1030 available.
1017
1031
1018
1032
1019 Heartbeat for kernels
1033 Heartbeat for kernels
1020 =====================
1034 =====================
1021
1035
1022 Initially we had considered using messages like those above over ZMQ for a
1036 Initially we had considered using messages like those above over ZMQ for a
1023 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
1037 kernel 'heartbeat' (a way to detect quickly and reliably whether a kernel is
1024 alive at all, even if it may be busy executing user code). But this has the
1038 alive at all, even if it may be busy executing user code). But this has the
1025 problem that if the kernel is locked inside extension code, it wouldn't execute
1039 problem that if the kernel is locked inside extension code, it wouldn't execute
1026 the python heartbeat code. But it turns out that we can implement a basic
1040 the python heartbeat code. But it turns out that we can implement a basic
1027 heartbeat with pure ZMQ, without using any Python messaging at all.
1041 heartbeat with pure ZMQ, without using any Python messaging at all.
1028
1042
1029 The monitor sends out a single zmq message (right now, it is a str of the
1043 The monitor sends out a single zmq message (right now, it is a str of the
1030 monitor's lifetime in seconds), and gets the same message right back, prefixed
1044 monitor's lifetime in seconds), and gets the same message right back, prefixed
1031 with the zmq identity of the DEALER socket in the heartbeat process. This can be
1045 with the zmq identity of the DEALER socket in the heartbeat process. This can be
1032 a uuid, or even a full message, but there doesn't seem to be a need for packing
1046 a uuid, or even a full message, but there doesn't seem to be a need for packing
1033 up a message when the sender and receiver are the exact same Python object.
1047 up a message when the sender and receiver are the exact same Python object.
1034
1048
1035 The model is this::
1049 The model is this::
1036
1050
1037 monitor.send(str(self.lifetime)) # '1.2345678910'
1051 monitor.send(str(self.lifetime)) # '1.2345678910'
1038
1052
1039 and the monitor receives some number of messages of the form::
1053 and the monitor receives some number of messages of the form::
1040
1054
1041 ['uuid-abcd-dead-beef', '1.2345678910']
1055 ['uuid-abcd-dead-beef', '1.2345678910']
1042
1056
1043 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
1057 where the first part is the zmq.IDENTITY of the heart's DEALER on the engine, and
1044 the rest is the message sent by the monitor. No Python code ever has any
1058 the rest is the message sent by the monitor. No Python code ever has any
1045 access to the message between the monitor's send, and the monitor's recv.
1059 access to the message between the monitor's send, and the monitor's recv.
1046
1060
1047
1061
1048 ToDo
1062 ToDo
1049 ====
1063 ====
1050
1064
1051 Missing things include:
1065 Missing things include:
1052
1066
1053 * Important: finish thinking through the payload concept and API.
1067 * Important: finish thinking through the payload concept and API.
1054
1068
1055 * Important: ensure that we have a good solution for magics like %edit. It's
1069 * Important: ensure that we have a good solution for magics like %edit. It's
1056 likely that with the payload concept we can build a full solution, but not
1070 likely that with the payload concept we can build a full solution, but not
1057 100% clear yet.
1071 100% clear yet.
1058
1072
1059 * Finishing the details of the heartbeat protocol.
1073 * Finishing the details of the heartbeat protocol.
1060
1074
1061 * Signal handling: specify what kind of information kernel should broadcast (or
1075 * Signal handling: specify what kind of information kernel should broadcast (or
1062 not) when it receives signals.
1076 not) when it receives signals.
1063
1077
1064 .. include:: ../links.txt
1078 .. include:: ../links.txt
General Comments 0
You need to be logged in to leave comments. Login now