##// END OF EJS Templates
Merge pull request #10654 from ipython/auto-backport-of-pr-10625...
Matthias Bussonnier -
r23772:12ae1852 merge
parent child Browse files
Show More
@@ -1,1224 +1,1224
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 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 try:
9 try:
10 from base64 import encodebytes as base64_encode
10 from base64 import encodebytes as base64_encode
11 except ImportError:
11 except ImportError:
12 from base64 import encodestring as base64_encode
12 from base64 import encodestring as base64_encode
13
13
14 from binascii import b2a_hex
14 from binascii import b2a_hex
15 import json
15 import json
16 import mimetypes
16 import mimetypes
17 import os
17 import os
18 import struct
18 import struct
19 import sys
19 import sys
20 import warnings
20 import warnings
21
21
22 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
22 from IPython.utils.py3compat import (string_types, cast_bytes_py2, cast_unicode,
23 unicode_type)
23 unicode_type)
24 from IPython.testing.skipdoctest import skip_doctest
24 from IPython.testing.skipdoctest import skip_doctest
25
25
26 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
26 __all__ = ['display', 'display_pretty', 'display_html', 'display_markdown',
27 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
27 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
28 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
28 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
29 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
29 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'JSON', 'Javascript',
30 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close',
30 'Image', 'clear_output', 'set_matplotlib_formats', 'set_matplotlib_close',
31 'publish_display_data', 'update_display', 'DisplayHandle']
31 'publish_display_data', 'update_display', 'DisplayHandle']
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # utility functions
34 # utility functions
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 def _safe_exists(path):
37 def _safe_exists(path):
38 """Check path, but don't let exceptions raise"""
38 """Check path, but don't let exceptions raise"""
39 try:
39 try:
40 return os.path.exists(path)
40 return os.path.exists(path)
41 except Exception:
41 except Exception:
42 return False
42 return False
43
43
44 def _merge(d1, d2):
44 def _merge(d1, d2):
45 """Like update, but merges sub-dicts instead of clobbering at the top level.
45 """Like update, but merges sub-dicts instead of clobbering at the top level.
46
46
47 Updates d1 in-place
47 Updates d1 in-place
48 """
48 """
49
49
50 if not isinstance(d2, dict) or not isinstance(d1, dict):
50 if not isinstance(d2, dict) or not isinstance(d1, dict):
51 return d2
51 return d2
52 for key, value in d2.items():
52 for key, value in d2.items():
53 d1[key] = _merge(d1.get(key), value)
53 d1[key] = _merge(d1.get(key), value)
54 return d1
54 return d1
55
55
56 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
56 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
57 """internal implementation of all display_foo methods
57 """internal implementation of all display_foo methods
58
58
59 Parameters
59 Parameters
60 ----------
60 ----------
61 mimetype : str
61 mimetype : str
62 The mimetype to be published (e.g. 'image/png')
62 The mimetype to be published (e.g. 'image/png')
63 objs : tuple of objects
63 objs : tuple of objects
64 The Python objects to display, or if raw=True raw text data to
64 The Python objects to display, or if raw=True raw text data to
65 display.
65 display.
66 raw : bool
66 raw : bool
67 Are the data objects raw data or Python objects that need to be
67 Are the data objects raw data or Python objects that need to be
68 formatted before display? [default: False]
68 formatted before display? [default: False]
69 metadata : dict (optional)
69 metadata : dict (optional)
70 Metadata to be associated with the specific mimetype output.
70 Metadata to be associated with the specific mimetype output.
71 """
71 """
72 if metadata:
72 if metadata:
73 metadata = {mimetype: metadata}
73 metadata = {mimetype: metadata}
74 if raw:
74 if raw:
75 # turn list of pngdata into list of { 'image/png': pngdata }
75 # turn list of pngdata into list of { 'image/png': pngdata }
76 objs = [ {mimetype: obj} for obj in objs ]
76 objs = [ {mimetype: obj} for obj in objs ]
77 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
77 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
78
78
79 #-----------------------------------------------------------------------------
79 #-----------------------------------------------------------------------------
80 # Main functions
80 # Main functions
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 # use * to indicate transient is keyword-only
82 # use * to indicate transient is keyword-only
83 def publish_display_data(data, metadata=None, source=None, **kwargs):
83 def publish_display_data(data, metadata=None, source=None, **kwargs):
84 """Publish data and metadata to all frontends.
84 """Publish data and metadata to all frontends.
85
85
86 See the ``display_data`` message in the messaging documentation for
86 See the ``display_data`` message in the messaging documentation for
87 more details about this message type.
87 more details about this message type.
88
88
89 The following MIME types are currently implemented:
89 The following MIME types are currently implemented:
90
90
91 * text/plain
91 * text/plain
92 * text/html
92 * text/html
93 * text/markdown
93 * text/markdown
94 * text/latex
94 * text/latex
95 * application/json
95 * application/json
96 * application/javascript
96 * application/javascript
97 * image/png
97 * image/png
98 * image/jpeg
98 * image/jpeg
99 * image/svg+xml
99 * image/svg+xml
100
100
101 Parameters
101 Parameters
102 ----------
102 ----------
103 data : dict
103 data : dict
104 A dictionary having keys that are valid MIME types (like
104 A dictionary having keys that are valid MIME types (like
105 'text/plain' or 'image/svg+xml') and values that are the data for
105 'text/plain' or 'image/svg+xml') and values that are the data for
106 that MIME type. The data itself must be a JSON'able data
106 that MIME type. The data itself must be a JSON'able data
107 structure. Minimally all data should have the 'text/plain' data,
107 structure. Minimally all data should have the 'text/plain' data,
108 which can be displayed by all frontends. If more than the plain
108 which can be displayed by all frontends. If more than the plain
109 text is given, it is up to the frontend to decide which
109 text is given, it is up to the frontend to decide which
110 representation to use.
110 representation to use.
111 metadata : dict
111 metadata : dict
112 A dictionary for metadata related to the data. This can contain
112 A dictionary for metadata related to the data. This can contain
113 arbitrary key, value pairs that frontends can use to interpret
113 arbitrary key, value pairs that frontends can use to interpret
114 the data. mime-type keys matching those in data can be used
114 the data. mime-type keys matching those in data can be used
115 to specify metadata about particular representations.
115 to specify metadata about particular representations.
116 source : str, deprecated
116 source : str, deprecated
117 Unused.
117 Unused.
118 transient : dict, keyword-only
118 transient : dict, keyword-only
119 A dictionary of transient data, such as display_id.
119 A dictionary of transient data, such as display_id.
120 """
120 """
121 from IPython.core.interactiveshell import InteractiveShell
121 from IPython.core.interactiveshell import InteractiveShell
122
122
123 display_pub = InteractiveShell.instance().display_pub
123 display_pub = InteractiveShell.instance().display_pub
124
124
125 # only pass transient if supplied,
125 # only pass transient if supplied,
126 # to avoid errors with older ipykernel.
126 # to avoid errors with older ipykernel.
127 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
127 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
128
128
129 display_pub.publish(
129 display_pub.publish(
130 data=data,
130 data=data,
131 metadata=metadata,
131 metadata=metadata,
132 **kwargs
132 **kwargs
133 )
133 )
134
134
135
135
136 def _new_id():
136 def _new_id():
137 """Generate a new random text id with urandom"""
137 """Generate a new random text id with urandom"""
138 return b2a_hex(os.urandom(16)).decode('ascii')
138 return b2a_hex(os.urandom(16)).decode('ascii')
139
139
140
140
141 def display(*objs, **kwargs):
141 def display(*objs, **kwargs):
142 """Display a Python object in all frontends.
142 """Display a Python object in all frontends.
143
143
144 By default all representations will be computed and sent to the frontends.
144 By default all representations will be computed and sent to the frontends.
145 Frontends can decide which representation is used and how.
145 Frontends can decide which representation is used and how.
146
146
147 In terminal IPython this will be similar to using :func:`print`, for use in richer
147 In terminal IPython this will be similar to using :func:`print`, for use in richer
148 frontends see Jupyter notebook examples with rich display logic.
148 frontends see Jupyter notebook examples with rich display logic.
149
149
150 Parameters
150 Parameters
151 ----------
151 ----------
152 objs : tuple of objects
152 objs : tuple of objects
153 The Python objects to display.
153 The Python objects to display.
154 raw : bool, optional
154 raw : bool, optional
155 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
155 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
156 or Python objects that need to be formatted before display? [default: False]
156 or Python objects that need to be formatted before display? [default: False]
157 include : list, tuple or set, optional
157 include : list, tuple or set, optional
158 A list of format type strings (MIME types) to include in the
158 A list of format type strings (MIME types) to include in the
159 format data dict. If this is set *only* the format types included
159 format data dict. If this is set *only* the format types included
160 in this list will be computed.
160 in this list will be computed.
161 exclude : list, tuple or set, optional
161 exclude : list, tuple or set, optional
162 A list of format type strings (MIME types) to exclude in the format
162 A list of format type strings (MIME types) to exclude in the format
163 data dict. If this is set all format types will be computed,
163 data dict. If this is set all format types will be computed,
164 except for those included in this argument.
164 except for those included in this argument.
165 metadata : dict, optional
165 metadata : dict, optional
166 A dictionary of metadata to associate with the output.
166 A dictionary of metadata to associate with the output.
167 mime-type keys in this dictionary will be associated with the individual
167 mime-type keys in this dictionary will be associated with the individual
168 representation formats, if they exist.
168 representation formats, if they exist.
169 transient : dict, optional
169 transient : dict, optional
170 A dictionary of transient data to associate with the output.
170 A dictionary of transient data to associate with the output.
171 Data in this dict should not be persisted to files (e.g. notebooks).
171 Data in this dict should not be persisted to files (e.g. notebooks).
172 display_id : str, bool optional
172 display_id : str, bool optional
173 Set an id for the display.
173 Set an id for the display.
174 This id can be used for updating this display area later via update_display.
174 This id can be used for updating this display area later via update_display.
175 If given as `True`, generate a new `display_id`
175 If given as `True`, generate a new `display_id`
176 kwargs: additional keyword-args, optional
176 kwargs: additional keyword-args, optional
177 Additional keyword-arguments are passed through to the display publisher.
177 Additional keyword-arguments are passed through to the display publisher.
178
178
179 Returns
179 Returns
180 -------
180 -------
181
181
182 handle: DisplayHandle
182 handle: DisplayHandle
183 Returns a handle on updatable displays for use with :func:`update_display`,
183 Returns a handle on updatable displays for use with :func:`update_display`,
184 if `display_id` is given. Returns :any:`None` if no `display_id` is given
184 if `display_id` is given. Returns :any:`None` if no `display_id` is given
185 (default).
185 (default).
186
186
187 Examples
187 Examples
188 --------
188 --------
189
189
190 >>> class Json(object):
190 >>> class Json(object):
191 ... def __init__(self, json):
191 ... def __init__(self, json):
192 ... self.json = json
192 ... self.json = json
193 ... def _repr_pretty_(self, pp, cycle):
193 ... def _repr_pretty_(self, pp, cycle):
194 ... import json
194 ... import json
195 ... pp.text(json.dumps(self.json, indent=2))
195 ... pp.text(json.dumps(self.json, indent=2))
196 ... def __repr__(self):
196 ... def __repr__(self):
197 ... return str(self.json)
197 ... return str(self.json)
198 ...
198 ...
199
199
200 >>> d = Json({1:2, 3: {4:5}})
200 >>> d = Json({1:2, 3: {4:5}})
201
201
202 >>> print(d)
202 >>> print(d)
203 {1: 2, 3: {4: 5}}
203 {1: 2, 3: {4: 5}}
204
204
205 >>> display(d)
205 >>> display(d)
206 {
206 {
207 "1": 2,
207 "1": 2,
208 "3": {
208 "3": {
209 "4": 5
209 "4": 5
210 }
210 }
211 }
211 }
212
212
213 >>> def int_formatter(integer, pp, cycle):
213 >>> def int_formatter(integer, pp, cycle):
214 ... pp.text('I'*integer)
214 ... pp.text('I'*integer)
215
215
216 >>> plain = get_ipython().display_formatter.formatters['text/plain']
216 >>> plain = get_ipython().display_formatter.formatters['text/plain']
217 >>> plain.for_type(int, int_formatter)
217 >>> plain.for_type(int, int_formatter)
218 <function _repr_pprint at 0x...>
218 <function _repr_pprint at 0x...>
219 >>> display(7-5)
219 >>> display(7-5)
220 II
220 II
221
221
222 >>> del plain.type_printers[int]
222 >>> del plain.type_printers[int]
223 >>> display(7-5)
223 >>> display(7-5)
224 2
224 2
225
225
226 See Also
226 See Also
227 --------
227 --------
228
228
229 :func:`update_display`
229 :func:`update_display`
230
230
231 Notes
231 Notes
232 -----
232 -----
233
233
234 In Python, objects can declare their textual representation using the
234 In Python, objects can declare their textual representation using the
235 `__repr__` method. IPython expands on this idea and allows objects to declare
235 `__repr__` method. IPython expands on this idea and allows objects to declare
236 other, rich representations including:
236 other, rich representations including:
237
237
238 - HTML
238 - HTML
239 - JSON
239 - JSON
240 - PNG
240 - PNG
241 - JPEG
241 - JPEG
242 - SVG
242 - SVG
243 - LaTeX
243 - LaTeX
244
244
245 A single object can declare some or all of these representations; all are
245 A single object can declare some or all of these representations; all are
246 handled by IPython's display system.
246 handled by IPython's display system.
247
247
248 The main idea of the first approach is that you have to implement special
248 The main idea of the first approach is that you have to implement special
249 display methods when you define your class, one for each representation you
249 display methods when you define your class, one for each representation you
250 want to use. Here is a list of the names of the special methods and the
250 want to use. Here is a list of the names of the special methods and the
251 values they must return:
251 values they must return:
252
252
253 - `_repr_html_`: return raw HTML as a string
253 - `_repr_html_`: return raw HTML as a string
254 - `_repr_json_`: return a JSONable dict
254 - `_repr_json_`: return a JSONable dict
255 - `_repr_jpeg_`: return raw JPEG data
255 - `_repr_jpeg_`: return raw JPEG data
256 - `_repr_png_`: return raw PNG data
256 - `_repr_png_`: return raw PNG data
257 - `_repr_svg_`: return raw SVG data as a string
257 - `_repr_svg_`: return raw SVG data as a string
258 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
258 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
259 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
259 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
260 from all mimetypes to data
260 from all mimetypes to data
261
261
262 When you are directly writing your own classes, you can adapt them for
262 When you are directly writing your own classes, you can adapt them for
263 display in IPython by following the above approach. But in practice, you
263 display in IPython by following the above approach. But in practice, you
264 often need to work with existing classes that you can't easily modify.
264 often need to work with existing classes that you can't easily modify.
265
265
266 You can refer to the documentation on IPython display formatters in order to
266 You can refer to the documentation on IPython display formatters in order to
267 register custom formatters for already existing types.
267 register custom formatters for already existing types.
268
268
269 .. versionadded:: 5.4 display available without import
269 .. versionadded:: 5.4 display available without import
270 .. versionadded:: 6.1 display available without import
270 .. versionadded:: 6.1 display available without import
271
271
272 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
272 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
273 the user without import. If you are using display in a document that might
273 the user without import. If you are using display in a document that might
274 be used in a pure python context or with older version of IPython, use the
274 be used in a pure python context or with older version of IPython, use the
275 following import at the top of your file::
275 following import at the top of your file::
276
276
277 from IPython.display import display
277 from IPython.display import display
278
278
279 """
279 """
280 raw = kwargs.pop('raw', False)
280 raw = kwargs.pop('raw', False)
281 include = kwargs.pop('include', None)
281 include = kwargs.pop('include', None)
282 exclude = kwargs.pop('exclude', None)
282 exclude = kwargs.pop('exclude', None)
283 metadata = kwargs.pop('metadata', None)
283 metadata = kwargs.pop('metadata', None)
284 transient = kwargs.pop('transient', None)
284 transient = kwargs.pop('transient', None)
285 display_id = kwargs.pop('display_id', None)
285 display_id = kwargs.pop('display_id', None)
286 if transient is None:
286 if transient is None:
287 transient = {}
287 transient = {}
288 if display_id:
288 if display_id:
289 if display_id is True:
289 if display_id is True:
290 display_id = _new_id()
290 display_id = _new_id()
291 transient['display_id'] = display_id
291 transient['display_id'] = display_id
292 if kwargs.get('update') and 'display_id' not in transient:
292 if kwargs.get('update') and 'display_id' not in transient:
293 raise TypeError('display_id required for update_display')
293 raise TypeError('display_id required for update_display')
294 if transient:
294 if transient:
295 kwargs['transient'] = transient
295 kwargs['transient'] = transient
296
296
297 from IPython.core.interactiveshell import InteractiveShell
297 from IPython.core.interactiveshell import InteractiveShell
298
298
299 if not raw:
299 if not raw:
300 format = InteractiveShell.instance().display_formatter.format
300 format = InteractiveShell.instance().display_formatter.format
301
301
302 for obj in objs:
302 for obj in objs:
303 if raw:
303 if raw:
304 publish_display_data(data=obj, metadata=metadata, **kwargs)
304 publish_display_data(data=obj, metadata=metadata, **kwargs)
305 else:
305 else:
306 format_dict, md_dict = format(obj, include=include, exclude=exclude)
306 format_dict, md_dict = format(obj, include=include, exclude=exclude)
307 if not format_dict:
307 if not format_dict:
308 # nothing to display (e.g. _ipython_display_ took over)
308 # nothing to display (e.g. _ipython_display_ took over)
309 continue
309 continue
310 if metadata:
310 if metadata:
311 # kwarg-specified metadata gets precedence
311 # kwarg-specified metadata gets precedence
312 _merge(md_dict, metadata)
312 _merge(md_dict, metadata)
313 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
313 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
314 if display_id:
314 if display_id:
315 return DisplayHandle(display_id)
315 return DisplayHandle(display_id)
316
316
317
317
318 # use * for keyword-only display_id arg
318 # use * for keyword-only display_id arg
319 def update_display(obj, **kwargs):
319 def update_display(obj, **kwargs):
320 """Update an existing display by id
320 """Update an existing display by id
321
321
322 Parameters
322 Parameters
323 ----------
323 ----------
324
324
325 obj:
325 obj:
326 The object with which to update the display
326 The object with which to update the display
327 display_id: keyword-only
327 display_id: keyword-only
328 The id of the display to update
328 The id of the display to update
329
329
330 See Also
330 See Also
331 --------
331 --------
332
332
333 :func:`display`
333 :func:`display`
334 """
334 """
335 sentinel = object()
335 sentinel = object()
336 display_id = kwargs.pop('display_id', sentinel)
336 display_id = kwargs.pop('display_id', sentinel)
337 if display_id is sentinel:
337 if display_id is sentinel:
338 raise TypeError("update_display() missing 1 required keyword-only argument: 'display_id'")
338 raise TypeError("update_display() missing 1 required keyword-only argument: 'display_id'")
339 kwargs['update'] = True
339 kwargs['update'] = True
340 display(obj, display_id=display_id, **kwargs)
340 display(obj, display_id=display_id, **kwargs)
341
341
342
342
343 class DisplayHandle(object):
343 class DisplayHandle(object):
344 """A handle on an updatable display
344 """A handle on an updatable display
345
345
346 Call `.update(obj)` to display a new object.
346 Call `.update(obj)` to display a new object.
347
347
348 Call `.display(obj`) to add a new instance of this display,
348 Call `.display(obj`) to add a new instance of this display,
349 and update existing instances.
349 and update existing instances.
350
350
351 See Also
351 See Also
352 --------
352 --------
353
353
354 :func:`display`, :func:`update_display`
354 :func:`display`, :func:`update_display`
355
355
356 """
356 """
357
357
358 def __init__(self, display_id=None):
358 def __init__(self, display_id=None):
359 if display_id is None:
359 if display_id is None:
360 display_id = _new_id()
360 display_id = _new_id()
361 self.display_id = display_id
361 self.display_id = display_id
362
362
363 def __repr__(self):
363 def __repr__(self):
364 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
364 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
365
365
366 def display(self, obj, **kwargs):
366 def display(self, obj, **kwargs):
367 """Make a new display with my id, updating existing instances.
367 """Make a new display with my id, updating existing instances.
368
368
369 Parameters
369 Parameters
370 ----------
370 ----------
371
371
372 obj:
372 obj:
373 object to display
373 object to display
374 **kwargs:
374 **kwargs:
375 additional keyword arguments passed to display
375 additional keyword arguments passed to display
376 """
376 """
377 display(obj, display_id=self.display_id, **kwargs)
377 display(obj, display_id=self.display_id, **kwargs)
378
378
379 def update(self, obj, **kwargs):
379 def update(self, obj, **kwargs):
380 """Update existing displays with my id
380 """Update existing displays with my id
381
381
382 Parameters
382 Parameters
383 ----------
383 ----------
384
384
385 obj:
385 obj:
386 object to display
386 object to display
387 **kwargs:
387 **kwargs:
388 additional keyword arguments passed to update_display
388 additional keyword arguments passed to update_display
389 """
389 """
390 update_display(obj, display_id=self.display_id, **kwargs)
390 update_display(obj, display_id=self.display_id, **kwargs)
391
391
392
392
393 def display_pretty(*objs, **kwargs):
393 def display_pretty(*objs, **kwargs):
394 """Display the pretty (default) representation of an object.
394 """Display the pretty (default) representation of an object.
395
395
396 Parameters
396 Parameters
397 ----------
397 ----------
398 objs : tuple of objects
398 objs : tuple of objects
399 The Python objects to display, or if raw=True raw text data to
399 The Python objects to display, or if raw=True raw text data to
400 display.
400 display.
401 raw : bool
401 raw : bool
402 Are the data objects raw data or Python objects that need to be
402 Are the data objects raw data or Python objects that need to be
403 formatted before display? [default: False]
403 formatted before display? [default: False]
404 metadata : dict (optional)
404 metadata : dict (optional)
405 Metadata to be associated with the specific mimetype output.
405 Metadata to be associated with the specific mimetype output.
406 """
406 """
407 _display_mimetype('text/plain', objs, **kwargs)
407 _display_mimetype('text/plain', objs, **kwargs)
408
408
409
409
410 def display_html(*objs, **kwargs):
410 def display_html(*objs, **kwargs):
411 """Display the HTML representation of an object.
411 """Display the HTML representation of an object.
412
412
413 Note: If raw=False and the object does not have a HTML
413 Note: If raw=False and the object does not have a HTML
414 representation, no HTML will be shown.
414 representation, no HTML will be shown.
415
415
416 Parameters
416 Parameters
417 ----------
417 ----------
418 objs : tuple of objects
418 objs : tuple of objects
419 The Python objects to display, or if raw=True raw HTML data to
419 The Python objects to display, or if raw=True raw HTML data to
420 display.
420 display.
421 raw : bool
421 raw : bool
422 Are the data objects raw data or Python objects that need to be
422 Are the data objects raw data or Python objects that need to be
423 formatted before display? [default: False]
423 formatted before display? [default: False]
424 metadata : dict (optional)
424 metadata : dict (optional)
425 Metadata to be associated with the specific mimetype output.
425 Metadata to be associated with the specific mimetype output.
426 """
426 """
427 _display_mimetype('text/html', objs, **kwargs)
427 _display_mimetype('text/html', objs, **kwargs)
428
428
429
429
430 def display_markdown(*objs, **kwargs):
430 def display_markdown(*objs, **kwargs):
431 """Displays the Markdown representation of an object.
431 """Displays the Markdown representation of an object.
432
432
433 Parameters
433 Parameters
434 ----------
434 ----------
435 objs : tuple of objects
435 objs : tuple of objects
436 The Python objects to display, or if raw=True raw markdown data to
436 The Python objects to display, or if raw=True raw markdown data to
437 display.
437 display.
438 raw : bool
438 raw : bool
439 Are the data objects raw data or Python objects that need to be
439 Are the data objects raw data or Python objects that need to be
440 formatted before display? [default: False]
440 formatted before display? [default: False]
441 metadata : dict (optional)
441 metadata : dict (optional)
442 Metadata to be associated with the specific mimetype output.
442 Metadata to be associated with the specific mimetype output.
443 """
443 """
444
444
445 _display_mimetype('text/markdown', objs, **kwargs)
445 _display_mimetype('text/markdown', objs, **kwargs)
446
446
447
447
448 def display_svg(*objs, **kwargs):
448 def display_svg(*objs, **kwargs):
449 """Display the SVG representation of an object.
449 """Display the SVG representation of an object.
450
450
451 Parameters
451 Parameters
452 ----------
452 ----------
453 objs : tuple of objects
453 objs : tuple of objects
454 The Python objects to display, or if raw=True raw svg data to
454 The Python objects to display, or if raw=True raw svg data to
455 display.
455 display.
456 raw : bool
456 raw : bool
457 Are the data objects raw data or Python objects that need to be
457 Are the data objects raw data or Python objects that need to be
458 formatted before display? [default: False]
458 formatted before display? [default: False]
459 metadata : dict (optional)
459 metadata : dict (optional)
460 Metadata to be associated with the specific mimetype output.
460 Metadata to be associated with the specific mimetype output.
461 """
461 """
462 _display_mimetype('image/svg+xml', objs, **kwargs)
462 _display_mimetype('image/svg+xml', objs, **kwargs)
463
463
464
464
465 def display_png(*objs, **kwargs):
465 def display_png(*objs, **kwargs):
466 """Display the PNG representation of an object.
466 """Display the PNG representation of an object.
467
467
468 Parameters
468 Parameters
469 ----------
469 ----------
470 objs : tuple of objects
470 objs : tuple of objects
471 The Python objects to display, or if raw=True raw png data to
471 The Python objects to display, or if raw=True raw png data to
472 display.
472 display.
473 raw : bool
473 raw : bool
474 Are the data objects raw data or Python objects that need to be
474 Are the data objects raw data or Python objects that need to be
475 formatted before display? [default: False]
475 formatted before display? [default: False]
476 metadata : dict (optional)
476 metadata : dict (optional)
477 Metadata to be associated with the specific mimetype output.
477 Metadata to be associated with the specific mimetype output.
478 """
478 """
479 _display_mimetype('image/png', objs, **kwargs)
479 _display_mimetype('image/png', objs, **kwargs)
480
480
481
481
482 def display_jpeg(*objs, **kwargs):
482 def display_jpeg(*objs, **kwargs):
483 """Display the JPEG representation of an object.
483 """Display the JPEG representation of an object.
484
484
485 Parameters
485 Parameters
486 ----------
486 ----------
487 objs : tuple of objects
487 objs : tuple of objects
488 The Python objects to display, or if raw=True raw JPEG data to
488 The Python objects to display, or if raw=True raw JPEG data to
489 display.
489 display.
490 raw : bool
490 raw : bool
491 Are the data objects raw data or Python objects that need to be
491 Are the data objects raw data or Python objects that need to be
492 formatted before display? [default: False]
492 formatted before display? [default: False]
493 metadata : dict (optional)
493 metadata : dict (optional)
494 Metadata to be associated with the specific mimetype output.
494 Metadata to be associated with the specific mimetype output.
495 """
495 """
496 _display_mimetype('image/jpeg', objs, **kwargs)
496 _display_mimetype('image/jpeg', objs, **kwargs)
497
497
498
498
499 def display_latex(*objs, **kwargs):
499 def display_latex(*objs, **kwargs):
500 """Display the LaTeX representation of an object.
500 """Display the LaTeX representation of an object.
501
501
502 Parameters
502 Parameters
503 ----------
503 ----------
504 objs : tuple of objects
504 objs : tuple of objects
505 The Python objects to display, or if raw=True raw latex data to
505 The Python objects to display, or if raw=True raw latex data to
506 display.
506 display.
507 raw : bool
507 raw : bool
508 Are the data objects raw data or Python objects that need to be
508 Are the data objects raw data or Python objects that need to be
509 formatted before display? [default: False]
509 formatted before display? [default: False]
510 metadata : dict (optional)
510 metadata : dict (optional)
511 Metadata to be associated with the specific mimetype output.
511 Metadata to be associated with the specific mimetype output.
512 """
512 """
513 _display_mimetype('text/latex', objs, **kwargs)
513 _display_mimetype('text/latex', objs, **kwargs)
514
514
515
515
516 def display_json(*objs, **kwargs):
516 def display_json(*objs, **kwargs):
517 """Display the JSON representation of an object.
517 """Display the JSON representation of an object.
518
518
519 Note that not many frontends support displaying JSON.
519 Note that not many frontends support displaying JSON.
520
520
521 Parameters
521 Parameters
522 ----------
522 ----------
523 objs : tuple of objects
523 objs : tuple of objects
524 The Python objects to display, or if raw=True raw json data to
524 The Python objects to display, or if raw=True raw json data to
525 display.
525 display.
526 raw : bool
526 raw : bool
527 Are the data objects raw data or Python objects that need to be
527 Are the data objects raw data or Python objects that need to be
528 formatted before display? [default: False]
528 formatted before display? [default: False]
529 metadata : dict (optional)
529 metadata : dict (optional)
530 Metadata to be associated with the specific mimetype output.
530 Metadata to be associated with the specific mimetype output.
531 """
531 """
532 _display_mimetype('application/json', objs, **kwargs)
532 _display_mimetype('application/json', objs, **kwargs)
533
533
534
534
535 def display_javascript(*objs, **kwargs):
535 def display_javascript(*objs, **kwargs):
536 """Display the Javascript representation of an object.
536 """Display the Javascript representation of an object.
537
537
538 Parameters
538 Parameters
539 ----------
539 ----------
540 objs : tuple of objects
540 objs : tuple of objects
541 The Python objects to display, or if raw=True raw javascript data to
541 The Python objects to display, or if raw=True raw javascript data to
542 display.
542 display.
543 raw : bool
543 raw : bool
544 Are the data objects raw data or Python objects that need to be
544 Are the data objects raw data or Python objects that need to be
545 formatted before display? [default: False]
545 formatted before display? [default: False]
546 metadata : dict (optional)
546 metadata : dict (optional)
547 Metadata to be associated with the specific mimetype output.
547 Metadata to be associated with the specific mimetype output.
548 """
548 """
549 _display_mimetype('application/javascript', objs, **kwargs)
549 _display_mimetype('application/javascript', objs, **kwargs)
550
550
551
551
552 def display_pdf(*objs, **kwargs):
552 def display_pdf(*objs, **kwargs):
553 """Display the PDF representation of an object.
553 """Display the PDF representation of an object.
554
554
555 Parameters
555 Parameters
556 ----------
556 ----------
557 objs : tuple of objects
557 objs : tuple of objects
558 The Python objects to display, or if raw=True raw javascript data to
558 The Python objects to display, or if raw=True raw javascript data to
559 display.
559 display.
560 raw : bool
560 raw : bool
561 Are the data objects raw data or Python objects that need to be
561 Are the data objects raw data or Python objects that need to be
562 formatted before display? [default: False]
562 formatted before display? [default: False]
563 metadata : dict (optional)
563 metadata : dict (optional)
564 Metadata to be associated with the specific mimetype output.
564 Metadata to be associated with the specific mimetype output.
565 """
565 """
566 _display_mimetype('application/pdf', objs, **kwargs)
566 _display_mimetype('application/pdf', objs, **kwargs)
567
567
568
568
569 #-----------------------------------------------------------------------------
569 #-----------------------------------------------------------------------------
570 # Smart classes
570 # Smart classes
571 #-----------------------------------------------------------------------------
571 #-----------------------------------------------------------------------------
572
572
573
573
574 class DisplayObject(object):
574 class DisplayObject(object):
575 """An object that wraps data to be displayed."""
575 """An object that wraps data to be displayed."""
576
576
577 _read_flags = 'r'
577 _read_flags = 'r'
578 _show_mem_addr = False
578 _show_mem_addr = False
579
579
580 def __init__(self, data=None, url=None, filename=None):
580 def __init__(self, data=None, url=None, filename=None):
581 """Create a display object given raw data.
581 """Create a display object given raw data.
582
582
583 When this object is returned by an expression or passed to the
583 When this object is returned by an expression or passed to the
584 display function, it will result in the data being displayed
584 display function, it will result in the data being displayed
585 in the frontend. The MIME type of the data should match the
585 in the frontend. The MIME type of the data should match the
586 subclasses used, so the Png subclass should be used for 'image/png'
586 subclasses used, so the Png subclass should be used for 'image/png'
587 data. If the data is a URL, the data will first be downloaded
587 data. If the data is a URL, the data will first be downloaded
588 and then displayed. If
588 and then displayed. If
589
589
590 Parameters
590 Parameters
591 ----------
591 ----------
592 data : unicode, str or bytes
592 data : unicode, str or bytes
593 The raw data or a URL or file to load the data from
593 The raw data or a URL or file to load the data from
594 url : unicode
594 url : unicode
595 A URL to download the data from.
595 A URL to download the data from.
596 filename : unicode
596 filename : unicode
597 Path to a local file to load the data from.
597 Path to a local file to load the data from.
598 """
598 """
599 if data is not None and isinstance(data, string_types):
599 if data is not None and isinstance(data, string_types):
600 if data.startswith('http') and url is None:
600 if data.startswith('http') and url is None:
601 url = data
601 url = data
602 filename = None
602 filename = None
603 data = None
603 data = None
604 elif _safe_exists(data) and filename is None:
604 elif _safe_exists(data) and filename is None:
605 url = None
605 url = None
606 filename = data
606 filename = data
607 data = None
607 data = None
608
608
609 self.data = data
609 self.data = data
610 self.url = url
610 self.url = url
611 self.filename = None if filename is None else unicode_type(filename)
611 self.filename = None if filename is None else unicode_type(filename)
612
612
613 self.reload()
613 self.reload()
614 self._check_data()
614 self._check_data()
615
615
616 def __repr__(self):
616 def __repr__(self):
617 if not self._show_mem_addr:
617 if not self._show_mem_addr:
618 cls = self.__class__
618 cls = self.__class__
619 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
619 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
620 else:
620 else:
621 r = super(DisplayObject, self).__repr__()
621 r = super(DisplayObject, self).__repr__()
622 return r
622 return r
623
623
624 def _check_data(self):
624 def _check_data(self):
625 """Override in subclasses if there's something to check."""
625 """Override in subclasses if there's something to check."""
626 pass
626 pass
627
627
628 def reload(self):
628 def reload(self):
629 """Reload the raw data from file or URL."""
629 """Reload the raw data from file or URL."""
630 if self.filename is not None:
630 if self.filename is not None:
631 with open(self.filename, self._read_flags) as f:
631 with open(self.filename, self._read_flags) as f:
632 self.data = f.read()
632 self.data = f.read()
633 elif self.url is not None:
633 elif self.url is not None:
634 try:
634 try:
635 try:
635 try:
636 from urllib.request import urlopen # Py3
636 from urllib.request import urlopen # Py3
637 except ImportError:
637 except ImportError:
638 from urllib2 import urlopen
638 from urllib2 import urlopen
639 response = urlopen(self.url)
639 response = urlopen(self.url)
640 self.data = response.read()
640 self.data = response.read()
641 # extract encoding from header, if there is one:
641 # extract encoding from header, if there is one:
642 encoding = None
642 encoding = None
643 for sub in response.headers['content-type'].split(';'):
643 for sub in response.headers['content-type'].split(';'):
644 sub = sub.strip()
644 sub = sub.strip()
645 if sub.startswith('charset'):
645 if sub.startswith('charset'):
646 encoding = sub.split('=')[-1].strip()
646 encoding = sub.split('=')[-1].strip()
647 break
647 break
648 # decode data, if an encoding was specified
648 # decode data, if an encoding was specified
649 if encoding:
649 if encoding:
650 self.data = self.data.decode(encoding, 'replace')
650 self.data = self.data.decode(encoding, 'replace')
651 except:
651 except:
652 self.data = None
652 self.data = None
653
653
654 class TextDisplayObject(DisplayObject):
654 class TextDisplayObject(DisplayObject):
655 """Validate that display data is text"""
655 """Validate that display data is text"""
656 def _check_data(self):
656 def _check_data(self):
657 if self.data is not None and not isinstance(self.data, string_types):
657 if self.data is not None and not isinstance(self.data, string_types):
658 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
658 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
659
659
660 class Pretty(TextDisplayObject):
660 class Pretty(TextDisplayObject):
661
661
662 def _repr_pretty_(self):
662 def _repr_pretty_(self, pp, cycle):
663 return self.data
663 return pp.text(self.data)
664
664
665
665
666 class HTML(TextDisplayObject):
666 class HTML(TextDisplayObject):
667
667
668 def _repr_html_(self):
668 def _repr_html_(self):
669 return self.data
669 return self.data
670
670
671 def __html__(self):
671 def __html__(self):
672 """
672 """
673 This method exists to inform other HTML-using modules (e.g. Markupsafe,
673 This method exists to inform other HTML-using modules (e.g. Markupsafe,
674 htmltag, etc) that this object is HTML and does not need things like
674 htmltag, etc) that this object is HTML and does not need things like
675 special characters (<>&) escaped.
675 special characters (<>&) escaped.
676 """
676 """
677 return self._repr_html_()
677 return self._repr_html_()
678
678
679
679
680 class Markdown(TextDisplayObject):
680 class Markdown(TextDisplayObject):
681
681
682 def _repr_markdown_(self):
682 def _repr_markdown_(self):
683 return self.data
683 return self.data
684
684
685
685
686 class Math(TextDisplayObject):
686 class Math(TextDisplayObject):
687
687
688 def _repr_latex_(self):
688 def _repr_latex_(self):
689 s = self.data.strip('$')
689 s = self.data.strip('$')
690 return "$$%s$$" % s
690 return "$$%s$$" % s
691
691
692
692
693 class Latex(TextDisplayObject):
693 class Latex(TextDisplayObject):
694
694
695 def _repr_latex_(self):
695 def _repr_latex_(self):
696 return self.data
696 return self.data
697
697
698
698
699 class SVG(DisplayObject):
699 class SVG(DisplayObject):
700
700
701 _read_flags = 'rb'
701 _read_flags = 'rb'
702 # wrap data in a property, which extracts the <svg> tag, discarding
702 # wrap data in a property, which extracts the <svg> tag, discarding
703 # document headers
703 # document headers
704 _data = None
704 _data = None
705
705
706 @property
706 @property
707 def data(self):
707 def data(self):
708 return self._data
708 return self._data
709
709
710 @data.setter
710 @data.setter
711 def data(self, svg):
711 def data(self, svg):
712 if svg is None:
712 if svg is None:
713 self._data = None
713 self._data = None
714 return
714 return
715 # parse into dom object
715 # parse into dom object
716 from xml.dom import minidom
716 from xml.dom import minidom
717 svg = cast_bytes_py2(svg)
717 svg = cast_bytes_py2(svg)
718 x = minidom.parseString(svg)
718 x = minidom.parseString(svg)
719 # get svg tag (should be 1)
719 # get svg tag (should be 1)
720 found_svg = x.getElementsByTagName('svg')
720 found_svg = x.getElementsByTagName('svg')
721 if found_svg:
721 if found_svg:
722 svg = found_svg[0].toxml()
722 svg = found_svg[0].toxml()
723 else:
723 else:
724 # fallback on the input, trust the user
724 # fallback on the input, trust the user
725 # but this is probably an error.
725 # but this is probably an error.
726 pass
726 pass
727 svg = cast_unicode(svg)
727 svg = cast_unicode(svg)
728 self._data = svg
728 self._data = svg
729
729
730 def _repr_svg_(self):
730 def _repr_svg_(self):
731 return self.data
731 return self.data
732
732
733
733
734 class JSON(DisplayObject):
734 class JSON(DisplayObject):
735 """JSON expects a JSON-able dict or list
735 """JSON expects a JSON-able dict or list
736
736
737 not an already-serialized JSON string.
737 not an already-serialized JSON string.
738
738
739 Scalar types (None, number, string) are not allowed, only dict or list containers.
739 Scalar types (None, number, string) are not allowed, only dict or list containers.
740 """
740 """
741 # wrap data in a property, which warns about passing already-serialized JSON
741 # wrap data in a property, which warns about passing already-serialized JSON
742 _data = None
742 _data = None
743 def _check_data(self):
743 def _check_data(self):
744 if self.data is not None and not isinstance(self.data, (dict, list)):
744 if self.data is not None and not isinstance(self.data, (dict, list)):
745 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
745 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
746
746
747 @property
747 @property
748 def data(self):
748 def data(self):
749 return self._data
749 return self._data
750
750
751 @data.setter
751 @data.setter
752 def data(self, data):
752 def data(self, data):
753 if isinstance(data, string_types):
753 if isinstance(data, string_types):
754 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
754 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
755 data = json.loads(data)
755 data = json.loads(data)
756 self._data = data
756 self._data = data
757
757
758 def _repr_json_(self):
758 def _repr_json_(self):
759 return self.data
759 return self.data
760
760
761 css_t = """$("head").append($("<link/>").attr({
761 css_t = """$("head").append($("<link/>").attr({
762 rel: "stylesheet",
762 rel: "stylesheet",
763 type: "text/css",
763 type: "text/css",
764 href: "%s"
764 href: "%s"
765 }));
765 }));
766 """
766 """
767
767
768 lib_t1 = """$.getScript("%s", function () {
768 lib_t1 = """$.getScript("%s", function () {
769 """
769 """
770 lib_t2 = """});
770 lib_t2 = """});
771 """
771 """
772
772
773 class Javascript(TextDisplayObject):
773 class Javascript(TextDisplayObject):
774
774
775 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
775 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
776 """Create a Javascript display object given raw data.
776 """Create a Javascript display object given raw data.
777
777
778 When this object is returned by an expression or passed to the
778 When this object is returned by an expression or passed to the
779 display function, it will result in the data being displayed
779 display function, it will result in the data being displayed
780 in the frontend. If the data is a URL, the data will first be
780 in the frontend. If the data is a URL, the data will first be
781 downloaded and then displayed.
781 downloaded and then displayed.
782
782
783 In the Notebook, the containing element will be available as `element`,
783 In the Notebook, the containing element will be available as `element`,
784 and jQuery will be available. Content appended to `element` will be
784 and jQuery will be available. Content appended to `element` will be
785 visible in the output area.
785 visible in the output area.
786
786
787 Parameters
787 Parameters
788 ----------
788 ----------
789 data : unicode, str or bytes
789 data : unicode, str or bytes
790 The Javascript source code or a URL to download it from.
790 The Javascript source code or a URL to download it from.
791 url : unicode
791 url : unicode
792 A URL to download the data from.
792 A URL to download the data from.
793 filename : unicode
793 filename : unicode
794 Path to a local file to load the data from.
794 Path to a local file to load the data from.
795 lib : list or str
795 lib : list or str
796 A sequence of Javascript library URLs to load asynchronously before
796 A sequence of Javascript library URLs to load asynchronously before
797 running the source code. The full URLs of the libraries should
797 running the source code. The full URLs of the libraries should
798 be given. A single Javascript library URL can also be given as a
798 be given. A single Javascript library URL can also be given as a
799 string.
799 string.
800 css: : list or str
800 css: : list or str
801 A sequence of css files to load before running the source code.
801 A sequence of css files to load before running the source code.
802 The full URLs of the css files should be given. A single css URL
802 The full URLs of the css files should be given. A single css URL
803 can also be given as a string.
803 can also be given as a string.
804 """
804 """
805 if isinstance(lib, string_types):
805 if isinstance(lib, string_types):
806 lib = [lib]
806 lib = [lib]
807 elif lib is None:
807 elif lib is None:
808 lib = []
808 lib = []
809 if isinstance(css, string_types):
809 if isinstance(css, string_types):
810 css = [css]
810 css = [css]
811 elif css is None:
811 elif css is None:
812 css = []
812 css = []
813 if not isinstance(lib, (list,tuple)):
813 if not isinstance(lib, (list,tuple)):
814 raise TypeError('expected sequence, got: %r' % lib)
814 raise TypeError('expected sequence, got: %r' % lib)
815 if not isinstance(css, (list,tuple)):
815 if not isinstance(css, (list,tuple)):
816 raise TypeError('expected sequence, got: %r' % css)
816 raise TypeError('expected sequence, got: %r' % css)
817 self.lib = lib
817 self.lib = lib
818 self.css = css
818 self.css = css
819 super(Javascript, self).__init__(data=data, url=url, filename=filename)
819 super(Javascript, self).__init__(data=data, url=url, filename=filename)
820
820
821 def _repr_javascript_(self):
821 def _repr_javascript_(self):
822 r = ''
822 r = ''
823 for c in self.css:
823 for c in self.css:
824 r += css_t % c
824 r += css_t % c
825 for l in self.lib:
825 for l in self.lib:
826 r += lib_t1 % l
826 r += lib_t1 % l
827 r += self.data
827 r += self.data
828 r += lib_t2*len(self.lib)
828 r += lib_t2*len(self.lib)
829 return r
829 return r
830
830
831 # constants for identifying png/jpeg data
831 # constants for identifying png/jpeg data
832 _PNG = b'\x89PNG\r\n\x1a\n'
832 _PNG = b'\x89PNG\r\n\x1a\n'
833 _JPEG = b'\xff\xd8'
833 _JPEG = b'\xff\xd8'
834
834
835 def _pngxy(data):
835 def _pngxy(data):
836 """read the (width, height) from a PNG header"""
836 """read the (width, height) from a PNG header"""
837 ihdr = data.index(b'IHDR')
837 ihdr = data.index(b'IHDR')
838 # next 8 bytes are width/height
838 # next 8 bytes are width/height
839 w4h4 = data[ihdr+4:ihdr+12]
839 w4h4 = data[ihdr+4:ihdr+12]
840 return struct.unpack('>ii', w4h4)
840 return struct.unpack('>ii', w4h4)
841
841
842 def _jpegxy(data):
842 def _jpegxy(data):
843 """read the (width, height) from a JPEG header"""
843 """read the (width, height) from a JPEG header"""
844 # adapted from http://www.64lines.com/jpeg-width-height
844 # adapted from http://www.64lines.com/jpeg-width-height
845
845
846 idx = 4
846 idx = 4
847 while True:
847 while True:
848 block_size = struct.unpack('>H', data[idx:idx+2])[0]
848 block_size = struct.unpack('>H', data[idx:idx+2])[0]
849 idx = idx + block_size
849 idx = idx + block_size
850 if data[idx:idx+2] == b'\xFF\xC0':
850 if data[idx:idx+2] == b'\xFF\xC0':
851 # found Start of Frame
851 # found Start of Frame
852 iSOF = idx
852 iSOF = idx
853 break
853 break
854 else:
854 else:
855 # read another block
855 # read another block
856 idx += 2
856 idx += 2
857
857
858 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
858 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
859 return w, h
859 return w, h
860
860
861 class Image(DisplayObject):
861 class Image(DisplayObject):
862
862
863 _read_flags = 'rb'
863 _read_flags = 'rb'
864 _FMT_JPEG = u'jpeg'
864 _FMT_JPEG = u'jpeg'
865 _FMT_PNG = u'png'
865 _FMT_PNG = u'png'
866 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
866 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
867
867
868 def __init__(self, data=None, url=None, filename=None, format=None,
868 def __init__(self, data=None, url=None, filename=None, format=None,
869 embed=None, width=None, height=None, retina=False,
869 embed=None, width=None, height=None, retina=False,
870 unconfined=False, metadata=None):
870 unconfined=False, metadata=None):
871 """Create a PNG/JPEG image object given raw data.
871 """Create a PNG/JPEG image object given raw data.
872
872
873 When this object is returned by an input cell or passed to the
873 When this object is returned by an input cell or passed to the
874 display function, it will result in the image being displayed
874 display function, it will result in the image being displayed
875 in the frontend.
875 in the frontend.
876
876
877 Parameters
877 Parameters
878 ----------
878 ----------
879 data : unicode, str or bytes
879 data : unicode, str or bytes
880 The raw image data or a URL or filename to load the data from.
880 The raw image data or a URL or filename to load the data from.
881 This always results in embedded image data.
881 This always results in embedded image data.
882 url : unicode
882 url : unicode
883 A URL to download the data from. If you specify `url=`,
883 A URL to download the data from. If you specify `url=`,
884 the image data will not be embedded unless you also specify `embed=True`.
884 the image data will not be embedded unless you also specify `embed=True`.
885 filename : unicode
885 filename : unicode
886 Path to a local file to load the data from.
886 Path to a local file to load the data from.
887 Images from a file are always embedded.
887 Images from a file are always embedded.
888 format : unicode
888 format : unicode
889 The format of the image data (png/jpeg/jpg). If a filename or URL is given
889 The format of the image data (png/jpeg/jpg). If a filename or URL is given
890 for format will be inferred from the filename extension.
890 for format will be inferred from the filename extension.
891 embed : bool
891 embed : bool
892 Should the image data be embedded using a data URI (True) or be
892 Should the image data be embedded using a data URI (True) or be
893 loaded using an <img> tag. Set this to True if you want the image
893 loaded using an <img> tag. Set this to True if you want the image
894 to be viewable later with no internet connection in the notebook.
894 to be viewable later with no internet connection in the notebook.
895
895
896 Default is `True`, unless the keyword argument `url` is set, then
896 Default is `True`, unless the keyword argument `url` is set, then
897 default value is `False`.
897 default value is `False`.
898
898
899 Note that QtConsole is not able to display images if `embed` is set to `False`
899 Note that QtConsole is not able to display images if `embed` is set to `False`
900 width : int
900 width : int
901 Width in pixels to which to constrain the image in html
901 Width in pixels to which to constrain the image in html
902 height : int
902 height : int
903 Height in pixels to which to constrain the image in html
903 Height in pixels to which to constrain the image in html
904 retina : bool
904 retina : bool
905 Automatically set the width and height to half of the measured
905 Automatically set the width and height to half of the measured
906 width and height.
906 width and height.
907 This only works for embedded images because it reads the width/height
907 This only works for embedded images because it reads the width/height
908 from image data.
908 from image data.
909 For non-embedded images, you can just set the desired display width
909 For non-embedded images, you can just set the desired display width
910 and height directly.
910 and height directly.
911 unconfined: bool
911 unconfined: bool
912 Set unconfined=True to disable max-width confinement of the image.
912 Set unconfined=True to disable max-width confinement of the image.
913 metadata: dict
913 metadata: dict
914 Specify extra metadata to attach to the image.
914 Specify extra metadata to attach to the image.
915
915
916 Examples
916 Examples
917 --------
917 --------
918 # embedded image data, works in qtconsole and notebook
918 # embedded image data, works in qtconsole and notebook
919 # when passed positionally, the first arg can be any of raw image data,
919 # when passed positionally, the first arg can be any of raw image data,
920 # a URL, or a filename from which to load image data.
920 # a URL, or a filename from which to load image data.
921 # The result is always embedding image data for inline images.
921 # The result is always embedding image data for inline images.
922 Image('http://www.google.fr/images/srpr/logo3w.png')
922 Image('http://www.google.fr/images/srpr/logo3w.png')
923 Image('/path/to/image.jpg')
923 Image('/path/to/image.jpg')
924 Image(b'RAW_PNG_DATA...')
924 Image(b'RAW_PNG_DATA...')
925
925
926 # Specifying Image(url=...) does not embed the image data,
926 # Specifying Image(url=...) does not embed the image data,
927 # it only generates `<img>` tag with a link to the source.
927 # it only generates `<img>` tag with a link to the source.
928 # This will not work in the qtconsole or offline.
928 # This will not work in the qtconsole or offline.
929 Image(url='http://www.google.fr/images/srpr/logo3w.png')
929 Image(url='http://www.google.fr/images/srpr/logo3w.png')
930
930
931 """
931 """
932 if filename is not None:
932 if filename is not None:
933 ext = self._find_ext(filename)
933 ext = self._find_ext(filename)
934 elif url is not None:
934 elif url is not None:
935 ext = self._find_ext(url)
935 ext = self._find_ext(url)
936 elif data is None:
936 elif data is None:
937 raise ValueError("No image data found. Expecting filename, url, or data.")
937 raise ValueError("No image data found. Expecting filename, url, or data.")
938 elif isinstance(data, string_types) and (
938 elif isinstance(data, string_types) and (
939 data.startswith('http') or _safe_exists(data)
939 data.startswith('http') or _safe_exists(data)
940 ):
940 ):
941 ext = self._find_ext(data)
941 ext = self._find_ext(data)
942 else:
942 else:
943 ext = None
943 ext = None
944
944
945 if format is None:
945 if format is None:
946 if ext is not None:
946 if ext is not None:
947 if ext == u'jpg' or ext == u'jpeg':
947 if ext == u'jpg' or ext == u'jpeg':
948 format = self._FMT_JPEG
948 format = self._FMT_JPEG
949 if ext == u'png':
949 if ext == u'png':
950 format = self._FMT_PNG
950 format = self._FMT_PNG
951 else:
951 else:
952 format = ext.lower()
952 format = ext.lower()
953 elif isinstance(data, bytes):
953 elif isinstance(data, bytes):
954 # infer image type from image data header,
954 # infer image type from image data header,
955 # only if format has not been specified.
955 # only if format has not been specified.
956 if data[:2] == _JPEG:
956 if data[:2] == _JPEG:
957 format = self._FMT_JPEG
957 format = self._FMT_JPEG
958
958
959 # failed to detect format, default png
959 # failed to detect format, default png
960 if format is None:
960 if format is None:
961 format = 'png'
961 format = 'png'
962
962
963 if format.lower() == 'jpg':
963 if format.lower() == 'jpg':
964 # jpg->jpeg
964 # jpg->jpeg
965 format = self._FMT_JPEG
965 format = self._FMT_JPEG
966
966
967 self.format = unicode_type(format).lower()
967 self.format = unicode_type(format).lower()
968 self.embed = embed if embed is not None else (url is None)
968 self.embed = embed if embed is not None else (url is None)
969
969
970 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
970 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
971 raise ValueError("Cannot embed the '%s' image format" % (self.format))
971 raise ValueError("Cannot embed the '%s' image format" % (self.format))
972 self.width = width
972 self.width = width
973 self.height = height
973 self.height = height
974 self.retina = retina
974 self.retina = retina
975 self.unconfined = unconfined
975 self.unconfined = unconfined
976 self.metadata = metadata
976 self.metadata = metadata
977 super(Image, self).__init__(data=data, url=url, filename=filename)
977 super(Image, self).__init__(data=data, url=url, filename=filename)
978
978
979 if retina:
979 if retina:
980 self._retina_shape()
980 self._retina_shape()
981
981
982 def _retina_shape(self):
982 def _retina_shape(self):
983 """load pixel-doubled width and height from image data"""
983 """load pixel-doubled width and height from image data"""
984 if not self.embed:
984 if not self.embed:
985 return
985 return
986 if self.format == 'png':
986 if self.format == 'png':
987 w, h = _pngxy(self.data)
987 w, h = _pngxy(self.data)
988 elif self.format == 'jpeg':
988 elif self.format == 'jpeg':
989 w, h = _jpegxy(self.data)
989 w, h = _jpegxy(self.data)
990 else:
990 else:
991 # retina only supports png
991 # retina only supports png
992 return
992 return
993 self.width = w // 2
993 self.width = w // 2
994 self.height = h // 2
994 self.height = h // 2
995
995
996 def reload(self):
996 def reload(self):
997 """Reload the raw data from file or URL."""
997 """Reload the raw data from file or URL."""
998 if self.embed:
998 if self.embed:
999 super(Image,self).reload()
999 super(Image,self).reload()
1000 if self.retina:
1000 if self.retina:
1001 self._retina_shape()
1001 self._retina_shape()
1002
1002
1003 def _repr_html_(self):
1003 def _repr_html_(self):
1004 if not self.embed:
1004 if not self.embed:
1005 width = height = klass = ''
1005 width = height = klass = ''
1006 if self.width:
1006 if self.width:
1007 width = ' width="%d"' % self.width
1007 width = ' width="%d"' % self.width
1008 if self.height:
1008 if self.height:
1009 height = ' height="%d"' % self.height
1009 height = ' height="%d"' % self.height
1010 if self.unconfined:
1010 if self.unconfined:
1011 klass = ' class="unconfined"'
1011 klass = ' class="unconfined"'
1012 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1012 return u'<img src="{url}"{width}{height}{klass}/>'.format(
1013 url=self.url,
1013 url=self.url,
1014 width=width,
1014 width=width,
1015 height=height,
1015 height=height,
1016 klass=klass,
1016 klass=klass,
1017 )
1017 )
1018
1018
1019 def _data_and_metadata(self):
1019 def _data_and_metadata(self):
1020 """shortcut for returning metadata with shape information, if defined"""
1020 """shortcut for returning metadata with shape information, if defined"""
1021 md = {}
1021 md = {}
1022 if self.width:
1022 if self.width:
1023 md['width'] = self.width
1023 md['width'] = self.width
1024 if self.height:
1024 if self.height:
1025 md['height'] = self.height
1025 md['height'] = self.height
1026 if self.unconfined:
1026 if self.unconfined:
1027 md['unconfined'] = self.unconfined
1027 md['unconfined'] = self.unconfined
1028 if self.metadata:
1028 if self.metadata:
1029 md.update(self.metadata)
1029 md.update(self.metadata)
1030 if md:
1030 if md:
1031 return self.data, md
1031 return self.data, md
1032 else:
1032 else:
1033 return self.data
1033 return self.data
1034
1034
1035 def _repr_png_(self):
1035 def _repr_png_(self):
1036 if self.embed and self.format == u'png':
1036 if self.embed and self.format == u'png':
1037 return self._data_and_metadata()
1037 return self._data_and_metadata()
1038
1038
1039 def _repr_jpeg_(self):
1039 def _repr_jpeg_(self):
1040 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
1040 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
1041 return self._data_and_metadata()
1041 return self._data_and_metadata()
1042
1042
1043 def _find_ext(self, s):
1043 def _find_ext(self, s):
1044 return unicode_type(s.split('.')[-1].lower())
1044 return unicode_type(s.split('.')[-1].lower())
1045
1045
1046 class Video(DisplayObject):
1046 class Video(DisplayObject):
1047
1047
1048 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1048 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1049 """Create a video object given raw data or an URL.
1049 """Create a video object given raw data or an URL.
1050
1050
1051 When this object is returned by an input cell or passed to the
1051 When this object is returned by an input cell or passed to the
1052 display function, it will result in the video being displayed
1052 display function, it will result in the video being displayed
1053 in the frontend.
1053 in the frontend.
1054
1054
1055 Parameters
1055 Parameters
1056 ----------
1056 ----------
1057 data : unicode, str or bytes
1057 data : unicode, str or bytes
1058 The raw video data or a URL or filename to load the data from.
1058 The raw video data or a URL or filename to load the data from.
1059 Raw data will require passing `embed=True`.
1059 Raw data will require passing `embed=True`.
1060 url : unicode
1060 url : unicode
1061 A URL for the video. If you specify `url=`,
1061 A URL for the video. If you specify `url=`,
1062 the image data will not be embedded.
1062 the image data will not be embedded.
1063 filename : unicode
1063 filename : unicode
1064 Path to a local file containing the video.
1064 Path to a local file containing the video.
1065 Will be interpreted as a local URL unless `embed=True`.
1065 Will be interpreted as a local URL unless `embed=True`.
1066 embed : bool
1066 embed : bool
1067 Should the video be embedded using a data URI (True) or be
1067 Should the video be embedded using a data URI (True) or be
1068 loaded using a <video> tag (False).
1068 loaded using a <video> tag (False).
1069
1069
1070 Since videos are large, embedding them should be avoided, if possible.
1070 Since videos are large, embedding them should be avoided, if possible.
1071 You must confirm embedding as your intention by passing `embed=True`.
1071 You must confirm embedding as your intention by passing `embed=True`.
1072
1072
1073 Local files can be displayed with URLs without embedding the content, via::
1073 Local files can be displayed with URLs without embedding the content, via::
1074
1074
1075 Video('./video.mp4')
1075 Video('./video.mp4')
1076
1076
1077 mimetype: unicode
1077 mimetype: unicode
1078 Specify the mimetype for embedded videos.
1078 Specify the mimetype for embedded videos.
1079 Default will be guessed from file extension, if available.
1079 Default will be guessed from file extension, if available.
1080
1080
1081 Examples
1081 Examples
1082 --------
1082 --------
1083
1083
1084 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1084 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1085 Video('path/to/video.mp4')
1085 Video('path/to/video.mp4')
1086 Video('path/to/video.mp4', embed=True)
1086 Video('path/to/video.mp4', embed=True)
1087 Video(b'raw-videodata', embed=True)
1087 Video(b'raw-videodata', embed=True)
1088 """
1088 """
1089 if url is None and isinstance(data, string_types) and data.startswith(('http:', 'https:')):
1089 if url is None and isinstance(data, string_types) and data.startswith(('http:', 'https:')):
1090 url = data
1090 url = data
1091 data = None
1091 data = None
1092 elif os.path.exists(data):
1092 elif os.path.exists(data):
1093 filename = data
1093 filename = data
1094 data = None
1094 data = None
1095
1095
1096 if data and not embed:
1096 if data and not embed:
1097 msg = ''.join([
1097 msg = ''.join([
1098 "To embed videos, you must pass embed=True ",
1098 "To embed videos, you must pass embed=True ",
1099 "(this may make your notebook files huge)\n",
1099 "(this may make your notebook files huge)\n",
1100 "Consider passing Video(url='...')",
1100 "Consider passing Video(url='...')",
1101 ])
1101 ])
1102 raise ValueError(msg)
1102 raise ValueError(msg)
1103
1103
1104 self.mimetype = mimetype
1104 self.mimetype = mimetype
1105 self.embed = embed
1105 self.embed = embed
1106 super(Video, self).__init__(data=data, url=url, filename=filename)
1106 super(Video, self).__init__(data=data, url=url, filename=filename)
1107
1107
1108 def _repr_html_(self):
1108 def _repr_html_(self):
1109 # External URLs and potentially local files are not embedded into the
1109 # External URLs and potentially local files are not embedded into the
1110 # notebook output.
1110 # notebook output.
1111 if not self.embed:
1111 if not self.embed:
1112 url = self.url if self.url is not None else self.filename
1112 url = self.url if self.url is not None else self.filename
1113 output = """<video src="{0}" controls>
1113 output = """<video src="{0}" controls>
1114 Your browser does not support the <code>video</code> element.
1114 Your browser does not support the <code>video</code> element.
1115 </video>""".format(url)
1115 </video>""".format(url)
1116 return output
1116 return output
1117
1117
1118 # Embedded videos are base64-encoded.
1118 # Embedded videos are base64-encoded.
1119 mimetype = self.mimetype
1119 mimetype = self.mimetype
1120 if self.filename is not None:
1120 if self.filename is not None:
1121 if not mimetype:
1121 if not mimetype:
1122 mimetype, _ = mimetypes.guess_type(self.filename)
1122 mimetype, _ = mimetypes.guess_type(self.filename)
1123
1123
1124 with open(self.filename, 'rb') as f:
1124 with open(self.filename, 'rb') as f:
1125 video = f.read()
1125 video = f.read()
1126 else:
1126 else:
1127 video = self.data
1127 video = self.data
1128 if isinstance(video, unicode_type):
1128 if isinstance(video, unicode_type):
1129 # unicode input is already b64-encoded
1129 # unicode input is already b64-encoded
1130 b64_video = video
1130 b64_video = video
1131 else:
1131 else:
1132 b64_video = base64_encode(video).decode('ascii').rstrip()
1132 b64_video = base64_encode(video).decode('ascii').rstrip()
1133
1133
1134 output = """<video controls>
1134 output = """<video controls>
1135 <source src="data:{0};base64,{1}" type="{0}">
1135 <source src="data:{0};base64,{1}" type="{0}">
1136 Your browser does not support the video tag.
1136 Your browser does not support the video tag.
1137 </video>""".format(mimetype, b64_video)
1137 </video>""".format(mimetype, b64_video)
1138 return output
1138 return output
1139
1139
1140 def reload(self):
1140 def reload(self):
1141 # TODO
1141 # TODO
1142 pass
1142 pass
1143
1143
1144 def _repr_png_(self):
1144 def _repr_png_(self):
1145 # TODO
1145 # TODO
1146 pass
1146 pass
1147 def _repr_jpeg_(self):
1147 def _repr_jpeg_(self):
1148 # TODO
1148 # TODO
1149 pass
1149 pass
1150
1150
1151 def clear_output(wait=False):
1151 def clear_output(wait=False):
1152 """Clear the output of the current cell receiving output.
1152 """Clear the output of the current cell receiving output.
1153
1153
1154 Parameters
1154 Parameters
1155 ----------
1155 ----------
1156 wait : bool [default: false]
1156 wait : bool [default: false]
1157 Wait to clear the output until new output is available to replace it."""
1157 Wait to clear the output until new output is available to replace it."""
1158 from IPython.core.interactiveshell import InteractiveShell
1158 from IPython.core.interactiveshell import InteractiveShell
1159 if InteractiveShell.initialized():
1159 if InteractiveShell.initialized():
1160 InteractiveShell.instance().display_pub.clear_output(wait)
1160 InteractiveShell.instance().display_pub.clear_output(wait)
1161 else:
1161 else:
1162 print('\033[2K\r', end='')
1162 print('\033[2K\r', end='')
1163 sys.stdout.flush()
1163 sys.stdout.flush()
1164 print('\033[2K\r', end='')
1164 print('\033[2K\r', end='')
1165 sys.stderr.flush()
1165 sys.stderr.flush()
1166
1166
1167
1167
1168 @skip_doctest
1168 @skip_doctest
1169 def set_matplotlib_formats(*formats, **kwargs):
1169 def set_matplotlib_formats(*formats, **kwargs):
1170 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1170 """Select figure formats for the inline backend. Optionally pass quality for JPEG.
1171
1171
1172 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1172 For example, this enables PNG and JPEG output with a JPEG quality of 90%::
1173
1173
1174 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1174 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1175
1175
1176 To set this in your config files use the following::
1176 To set this in your config files use the following::
1177
1177
1178 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1178 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1179 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1179 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1180
1180
1181 Parameters
1181 Parameters
1182 ----------
1182 ----------
1183 *formats : strs
1183 *formats : strs
1184 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1184 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1185 **kwargs :
1185 **kwargs :
1186 Keyword args will be relayed to ``figure.canvas.print_figure``.
1186 Keyword args will be relayed to ``figure.canvas.print_figure``.
1187 """
1187 """
1188 from IPython.core.interactiveshell import InteractiveShell
1188 from IPython.core.interactiveshell import InteractiveShell
1189 from IPython.core.pylabtools import select_figure_formats
1189 from IPython.core.pylabtools import select_figure_formats
1190 # build kwargs, starting with InlineBackend config
1190 # build kwargs, starting with InlineBackend config
1191 kw = {}
1191 kw = {}
1192 from ipykernel.pylab.config import InlineBackend
1192 from ipykernel.pylab.config import InlineBackend
1193 cfg = InlineBackend.instance()
1193 cfg = InlineBackend.instance()
1194 kw.update(cfg.print_figure_kwargs)
1194 kw.update(cfg.print_figure_kwargs)
1195 kw.update(**kwargs)
1195 kw.update(**kwargs)
1196 shell = InteractiveShell.instance()
1196 shell = InteractiveShell.instance()
1197 select_figure_formats(shell, formats, **kw)
1197 select_figure_formats(shell, formats, **kw)
1198
1198
1199 @skip_doctest
1199 @skip_doctest
1200 def set_matplotlib_close(close=True):
1200 def set_matplotlib_close(close=True):
1201 """Set whether the inline backend closes all figures automatically or not.
1201 """Set whether the inline backend closes all figures automatically or not.
1202
1202
1203 By default, the inline backend used in the IPython Notebook will close all
1203 By default, the inline backend used in the IPython Notebook will close all
1204 matplotlib figures automatically after each cell is run. This means that
1204 matplotlib figures automatically after each cell is run. This means that
1205 plots in different cells won't interfere. Sometimes, you may want to make
1205 plots in different cells won't interfere. Sometimes, you may want to make
1206 a plot in one cell and then refine it in later cells. This can be accomplished
1206 a plot in one cell and then refine it in later cells. This can be accomplished
1207 by::
1207 by::
1208
1208
1209 In [1]: set_matplotlib_close(False)
1209 In [1]: set_matplotlib_close(False)
1210
1210
1211 To set this in your config files use the following::
1211 To set this in your config files use the following::
1212
1212
1213 c.InlineBackend.close_figures = False
1213 c.InlineBackend.close_figures = False
1214
1214
1215 Parameters
1215 Parameters
1216 ----------
1216 ----------
1217 close : bool
1217 close : bool
1218 Should all matplotlib figures be automatically closed after each cell is
1218 Should all matplotlib figures be automatically closed after each cell is
1219 run?
1219 run?
1220 """
1220 """
1221 from ipykernel.pylab.config import InlineBackend
1221 from ipykernel.pylab.config import InlineBackend
1222 cfg = InlineBackend.instance()
1222 cfg = InlineBackend.instance()
1223 cfg.close_figures = close
1223 cfg.close_figures = close
1224
1224
@@ -1,331 +1,338
1 # Copyright (c) IPython Development Team.
1 # Copyright (c) IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
2 # Distributed under the terms of the Modified BSD License.
3
3
4 import json
4 import json
5 import os
5 import os
6 import sys
6 import sys
7 import warnings
7 import warnings
8
8
9 import nose.tools as nt
9 import nose.tools as nt
10
10
11 from IPython.core import display
11 from IPython.core import display
12 from IPython.core.getipython import get_ipython
12 from IPython.core.getipython import get_ipython
13 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
13 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
14 from IPython import paths as ipath
14 from IPython import paths as ipath
15 from IPython.testing.tools import AssertPrints, AssertNotPrints
15 from IPython.testing.tools import AssertPrints, AssertNotPrints
16
16
17 import IPython.testing.decorators as dec
17 import IPython.testing.decorators as dec
18
18
19 if sys.version_info < (3,):
19 if sys.version_info < (3,):
20 import mock
20 import mock
21 else:
21 else:
22 from unittest import mock
22 from unittest import mock
23
23
24 def test_image_size():
24 def test_image_size():
25 """Simple test for display.Image(args, width=x,height=y)"""
25 """Simple test for display.Image(args, width=x,height=y)"""
26 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
26 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
27 img = display.Image(url=thisurl, width=200, height=200)
27 img = display.Image(url=thisurl, width=200, height=200)
28 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
28 nt.assert_equal(u'<img src="%s" width="200" height="200"/>' % (thisurl), img._repr_html_())
29 img = display.Image(url=thisurl, width=200)
29 img = display.Image(url=thisurl, width=200)
30 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
30 nt.assert_equal(u'<img src="%s" width="200"/>' % (thisurl), img._repr_html_())
31 img = display.Image(url=thisurl)
31 img = display.Image(url=thisurl)
32 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
32 nt.assert_equal(u'<img src="%s"/>' % (thisurl), img._repr_html_())
33 img = display.Image(url=thisurl, unconfined=True)
33 img = display.Image(url=thisurl, unconfined=True)
34 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
34 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
35
35
36 def test_retina_png():
36 def test_retina_png():
37 here = os.path.dirname(__file__)
37 here = os.path.dirname(__file__)
38 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
38 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
39 nt.assert_equal(img.height, 1)
39 nt.assert_equal(img.height, 1)
40 nt.assert_equal(img.width, 1)
40 nt.assert_equal(img.width, 1)
41 data, md = img._repr_png_()
41 data, md = img._repr_png_()
42 nt.assert_equal(md['width'], 1)
42 nt.assert_equal(md['width'], 1)
43 nt.assert_equal(md['height'], 1)
43 nt.assert_equal(md['height'], 1)
44
44
45 def test_retina_jpeg():
45 def test_retina_jpeg():
46 here = os.path.dirname(__file__)
46 here = os.path.dirname(__file__)
47 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
47 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
48 nt.assert_equal(img.height, 1)
48 nt.assert_equal(img.height, 1)
49 nt.assert_equal(img.width, 1)
49 nt.assert_equal(img.width, 1)
50 data, md = img._repr_jpeg_()
50 data, md = img._repr_jpeg_()
51 nt.assert_equal(md['width'], 1)
51 nt.assert_equal(md['width'], 1)
52 nt.assert_equal(md['height'], 1)
52 nt.assert_equal(md['height'], 1)
53
53
54 def test_base64image():
54 def test_base64image():
55 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
55 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
56
56
57 def test_image_filename_defaults():
57 def test_image_filename_defaults():
58 '''test format constraint, and validity of jpeg and png'''
58 '''test format constraint, and validity of jpeg and png'''
59 tpath = ipath.get_ipython_package_dir()
59 tpath = ipath.get_ipython_package_dir()
60 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
60 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
61 embed=True)
61 embed=True)
62 nt.assert_raises(ValueError, display.Image)
62 nt.assert_raises(ValueError, display.Image)
63 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
63 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
64 # check boths paths to allow packages to test at build and install time
64 # check boths paths to allow packages to test at build and install time
65 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
65 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
66 img = display.Image(filename=imgfile)
66 img = display.Image(filename=imgfile)
67 nt.assert_equal('png', img.format)
67 nt.assert_equal('png', img.format)
68 nt.assert_is_not_none(img._repr_png_())
68 nt.assert_is_not_none(img._repr_png_())
69 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
69 img = display.Image(filename=os.path.join(tpath, 'testing/tests/logo.jpg'), embed=False)
70 nt.assert_equal('jpeg', img.format)
70 nt.assert_equal('jpeg', img.format)
71 nt.assert_is_none(img._repr_jpeg_())
71 nt.assert_is_none(img._repr_jpeg_())
72
72
73 def _get_inline_config():
73 def _get_inline_config():
74 from ipykernel.pylab.config import InlineBackend
74 from ipykernel.pylab.config import InlineBackend
75 return InlineBackend.instance()
75 return InlineBackend.instance()
76
76
77 @dec.skip_without('matplotlib')
77 @dec.skip_without('matplotlib')
78 def test_set_matplotlib_close():
78 def test_set_matplotlib_close():
79 cfg = _get_inline_config()
79 cfg = _get_inline_config()
80 cfg.close_figures = False
80 cfg.close_figures = False
81 display.set_matplotlib_close()
81 display.set_matplotlib_close()
82 assert cfg.close_figures
82 assert cfg.close_figures
83 display.set_matplotlib_close(False)
83 display.set_matplotlib_close(False)
84 assert not cfg.close_figures
84 assert not cfg.close_figures
85
85
86 _fmt_mime_map = {
86 _fmt_mime_map = {
87 'png': 'image/png',
87 'png': 'image/png',
88 'jpeg': 'image/jpeg',
88 'jpeg': 'image/jpeg',
89 'pdf': 'application/pdf',
89 'pdf': 'application/pdf',
90 'retina': 'image/png',
90 'retina': 'image/png',
91 'svg': 'image/svg+xml',
91 'svg': 'image/svg+xml',
92 }
92 }
93
93
94 @dec.skip_without('matplotlib')
94 @dec.skip_without('matplotlib')
95 def test_set_matplotlib_formats():
95 def test_set_matplotlib_formats():
96 from matplotlib.figure import Figure
96 from matplotlib.figure import Figure
97 formatters = get_ipython().display_formatter.formatters
97 formatters = get_ipython().display_formatter.formatters
98 for formats in [
98 for formats in [
99 ('png',),
99 ('png',),
100 ('pdf', 'svg'),
100 ('pdf', 'svg'),
101 ('jpeg', 'retina', 'png'),
101 ('jpeg', 'retina', 'png'),
102 (),
102 (),
103 ]:
103 ]:
104 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
104 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
105 display.set_matplotlib_formats(*formats)
105 display.set_matplotlib_formats(*formats)
106 for mime, f in formatters.items():
106 for mime, f in formatters.items():
107 if mime in active_mimes:
107 if mime in active_mimes:
108 nt.assert_in(Figure, f)
108 nt.assert_in(Figure, f)
109 else:
109 else:
110 nt.assert_not_in(Figure, f)
110 nt.assert_not_in(Figure, f)
111
111
112 @dec.skip_without('matplotlib')
112 @dec.skip_without('matplotlib')
113 def test_set_matplotlib_formats_kwargs():
113 def test_set_matplotlib_formats_kwargs():
114 from matplotlib.figure import Figure
114 from matplotlib.figure import Figure
115 ip = get_ipython()
115 ip = get_ipython()
116 cfg = _get_inline_config()
116 cfg = _get_inline_config()
117 cfg.print_figure_kwargs.update(dict(foo='bar'))
117 cfg.print_figure_kwargs.update(dict(foo='bar'))
118 kwargs = dict(quality=10)
118 kwargs = dict(quality=10)
119 display.set_matplotlib_formats('png', **kwargs)
119 display.set_matplotlib_formats('png', **kwargs)
120 formatter = ip.display_formatter.formatters['image/png']
120 formatter = ip.display_formatter.formatters['image/png']
121 f = formatter.lookup_by_type(Figure)
121 f = formatter.lookup_by_type(Figure)
122 cell = f.__closure__[0].cell_contents
122 cell = f.__closure__[0].cell_contents
123 expected = kwargs
123 expected = kwargs
124 expected.update(cfg.print_figure_kwargs)
124 expected.update(cfg.print_figure_kwargs)
125 nt.assert_equal(cell, expected)
125 nt.assert_equal(cell, expected)
126
126
127 def test_display_available():
127 def test_display_available():
128 """
128 """
129 Test that display is available without import
129 Test that display is available without import
130
130
131 We don't really care if it's in builtin or anything else, but it should
131 We don't really care if it's in builtin or anything else, but it should
132 always be available.
132 always be available.
133 """
133 """
134 ip = get_ipython()
134 ip = get_ipython()
135 with AssertNotPrints('NameError'):
135 with AssertNotPrints('NameError'):
136 ip.run_cell('display')
136 ip.run_cell('display')
137 try:
137 try:
138 ip.run_cell('del display')
138 ip.run_cell('del display')
139 except NameError:
139 except NameError:
140 pass # it's ok, it might be in builtins
140 pass # it's ok, it might be in builtins
141 # even if deleted it should be back
141 # even if deleted it should be back
142 with AssertNotPrints('NameError'):
142 with AssertNotPrints('NameError'):
143 ip.run_cell('display')
143 ip.run_cell('display')
144
144
145 def test_textdisplayobj_pretty_repr():
146 p = display.Pretty("This is a simple test")
147 nt.assert_equal(repr(p), '<IPython.core.display.Pretty object>')
148 nt.assert_equal(p.data, 'This is a simple test')
149
150 p._show_mem_addr = True
151 nt.assert_equal(repr(p), object.__repr__(p))
145
152
146 def test_displayobject_repr():
153 def test_displayobject_repr():
147 h = display.HTML('<br />')
154 h = display.HTML('<br />')
148 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
155 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
149 h._show_mem_addr = True
156 h._show_mem_addr = True
150 nt.assert_equal(repr(h), object.__repr__(h))
157 nt.assert_equal(repr(h), object.__repr__(h))
151 h._show_mem_addr = False
158 h._show_mem_addr = False
152 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
159 nt.assert_equal(repr(h), '<IPython.core.display.HTML object>')
153
160
154 j = display.Javascript('')
161 j = display.Javascript('')
155 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
162 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
156 j._show_mem_addr = True
163 j._show_mem_addr = True
157 nt.assert_equal(repr(j), object.__repr__(j))
164 nt.assert_equal(repr(j), object.__repr__(j))
158 j._show_mem_addr = False
165 j._show_mem_addr = False
159 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
166 nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>')
160
167
161 def test_json():
168 def test_json():
162 d = {'a': 5}
169 d = {'a': 5}
163 lis = [d]
170 lis = [d]
164 j = display.JSON(d)
171 j = display.JSON(d)
165 nt.assert_equal(j._repr_json_(), d)
172 nt.assert_equal(j._repr_json_(), d)
166
173
167 with warnings.catch_warnings(record=True) as w:
174 with warnings.catch_warnings(record=True) as w:
168 warnings.simplefilter("always")
175 warnings.simplefilter("always")
169 j = display.JSON(json.dumps(d))
176 j = display.JSON(json.dumps(d))
170 nt.assert_equal(len(w), 1)
177 nt.assert_equal(len(w), 1)
171 nt.assert_equal(j._repr_json_(), d)
178 nt.assert_equal(j._repr_json_(), d)
172
179
173 j = display.JSON(lis)
180 j = display.JSON(lis)
174 nt.assert_equal(j._repr_json_(), lis)
181 nt.assert_equal(j._repr_json_(), lis)
175
182
176 with warnings.catch_warnings(record=True) as w:
183 with warnings.catch_warnings(record=True) as w:
177 warnings.simplefilter("always")
184 warnings.simplefilter("always")
178 j = display.JSON(json.dumps(lis))
185 j = display.JSON(json.dumps(lis))
179 nt.assert_equal(len(w), 1)
186 nt.assert_equal(len(w), 1)
180 nt.assert_equal(j._repr_json_(), lis)
187 nt.assert_equal(j._repr_json_(), lis)
181
188
182 def test_video_embedding():
189 def test_video_embedding():
183 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
190 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
184 v = display.Video("http://ignored")
191 v = display.Video("http://ignored")
185 assert not v.embed
192 assert not v.embed
186 html = v._repr_html_()
193 html = v._repr_html_()
187 nt.assert_not_in('src="data:', html)
194 nt.assert_not_in('src="data:', html)
188 nt.assert_in('src="http://ignored"', html)
195 nt.assert_in('src="http://ignored"', html)
189
196
190 with nt.assert_raises(ValueError):
197 with nt.assert_raises(ValueError):
191 v = display.Video(b'abc')
198 v = display.Video(b'abc')
192
199
193 with NamedFileInTemporaryDirectory('test.mp4') as f:
200 with NamedFileInTemporaryDirectory('test.mp4') as f:
194 f.write(b'abc')
201 f.write(b'abc')
195 f.close()
202 f.close()
196
203
197 v = display.Video(f.name)
204 v = display.Video(f.name)
198 assert not v.embed
205 assert not v.embed
199 html = v._repr_html_()
206 html = v._repr_html_()
200 nt.assert_not_in('src="data:', html)
207 nt.assert_not_in('src="data:', html)
201
208
202 v = display.Video(f.name, embed=True)
209 v = display.Video(f.name, embed=True)
203 html = v._repr_html_()
210 html = v._repr_html_()
204 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
211 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
205
212
206 v = display.Video(f.name, embed=True, mimetype='video/other')
213 v = display.Video(f.name, embed=True, mimetype='video/other')
207 html = v._repr_html_()
214 html = v._repr_html_()
208 nt.assert_in('src="data:video/other;base64,YWJj"',html)
215 nt.assert_in('src="data:video/other;base64,YWJj"',html)
209
216
210 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
217 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
211 html = v._repr_html_()
218 html = v._repr_html_()
212 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
219 nt.assert_in('src="data:video/mp4;base64,YWJj"',html)
213
220
214 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
221 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
215 html = v._repr_html_()
222 html = v._repr_html_()
216 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
223 nt.assert_in('src="data:video/xyz;base64,YWJj"',html)
217
224
218
225
219 def test_display_id():
226 def test_display_id():
220 ip = get_ipython()
227 ip = get_ipython()
221 with mock.patch.object(ip.display_pub, 'publish') as pub:
228 with mock.patch.object(ip.display_pub, 'publish') as pub:
222 handle = display.display('x')
229 handle = display.display('x')
223 nt.assert_is(handle, None)
230 nt.assert_is(handle, None)
224 handle = display.display('y', display_id='secret')
231 handle = display.display('y', display_id='secret')
225 nt.assert_is_instance(handle, display.DisplayHandle)
232 nt.assert_is_instance(handle, display.DisplayHandle)
226 handle2 = display.display('z', display_id=True)
233 handle2 = display.display('z', display_id=True)
227 nt.assert_is_instance(handle2, display.DisplayHandle)
234 nt.assert_is_instance(handle2, display.DisplayHandle)
228 nt.assert_not_equal(handle.display_id, handle2.display_id)
235 nt.assert_not_equal(handle.display_id, handle2.display_id)
229
236
230 nt.assert_equal(pub.call_count, 3)
237 nt.assert_equal(pub.call_count, 3)
231 args, kwargs = pub.call_args_list[0]
238 args, kwargs = pub.call_args_list[0]
232 nt.assert_equal(args, ())
239 nt.assert_equal(args, ())
233 nt.assert_equal(kwargs, {
240 nt.assert_equal(kwargs, {
234 'data': {
241 'data': {
235 'text/plain': repr('x')
242 'text/plain': repr('x')
236 },
243 },
237 'metadata': {},
244 'metadata': {},
238 })
245 })
239 args, kwargs = pub.call_args_list[1]
246 args, kwargs = pub.call_args_list[1]
240 nt.assert_equal(args, ())
247 nt.assert_equal(args, ())
241 nt.assert_equal(kwargs, {
248 nt.assert_equal(kwargs, {
242 'data': {
249 'data': {
243 'text/plain': repr('y')
250 'text/plain': repr('y')
244 },
251 },
245 'metadata': {},
252 'metadata': {},
246 'transient': {
253 'transient': {
247 'display_id': handle.display_id,
254 'display_id': handle.display_id,
248 },
255 },
249 })
256 })
250 args, kwargs = pub.call_args_list[2]
257 args, kwargs = pub.call_args_list[2]
251 nt.assert_equal(args, ())
258 nt.assert_equal(args, ())
252 nt.assert_equal(kwargs, {
259 nt.assert_equal(kwargs, {
253 'data': {
260 'data': {
254 'text/plain': repr('z')
261 'text/plain': repr('z')
255 },
262 },
256 'metadata': {},
263 'metadata': {},
257 'transient': {
264 'transient': {
258 'display_id': handle2.display_id,
265 'display_id': handle2.display_id,
259 },
266 },
260 })
267 })
261
268
262
269
263 def test_update_display():
270 def test_update_display():
264 ip = get_ipython()
271 ip = get_ipython()
265 with mock.patch.object(ip.display_pub, 'publish') as pub:
272 with mock.patch.object(ip.display_pub, 'publish') as pub:
266 with nt.assert_raises(TypeError):
273 with nt.assert_raises(TypeError):
267 display.update_display('x')
274 display.update_display('x')
268 display.update_display('x', display_id='1')
275 display.update_display('x', display_id='1')
269 display.update_display('y', display_id='2')
276 display.update_display('y', display_id='2')
270 args, kwargs = pub.call_args_list[0]
277 args, kwargs = pub.call_args_list[0]
271 nt.assert_equal(args, ())
278 nt.assert_equal(args, ())
272 nt.assert_equal(kwargs, {
279 nt.assert_equal(kwargs, {
273 'data': {
280 'data': {
274 'text/plain': repr('x')
281 'text/plain': repr('x')
275 },
282 },
276 'metadata': {},
283 'metadata': {},
277 'transient': {
284 'transient': {
278 'display_id': '1',
285 'display_id': '1',
279 },
286 },
280 'update': True,
287 'update': True,
281 })
288 })
282 args, kwargs = pub.call_args_list[1]
289 args, kwargs = pub.call_args_list[1]
283 nt.assert_equal(args, ())
290 nt.assert_equal(args, ())
284 nt.assert_equal(kwargs, {
291 nt.assert_equal(kwargs, {
285 'data': {
292 'data': {
286 'text/plain': repr('y')
293 'text/plain': repr('y')
287 },
294 },
288 'metadata': {},
295 'metadata': {},
289 'transient': {
296 'transient': {
290 'display_id': '2',
297 'display_id': '2',
291 },
298 },
292 'update': True,
299 'update': True,
293 })
300 })
294
301
295
302
296 def test_display_handle():
303 def test_display_handle():
297 ip = get_ipython()
304 ip = get_ipython()
298 handle = display.DisplayHandle()
305 handle = display.DisplayHandle()
299 if sys.version_info < (3,):
306 if sys.version_info < (3,):
300 nt.assert_is_instance(handle.display_id, unicode)
307 nt.assert_is_instance(handle.display_id, unicode)
301 else:
308 else:
302 nt.assert_is_instance(handle.display_id, str)
309 nt.assert_is_instance(handle.display_id, str)
303 handle = display.DisplayHandle('my-id')
310 handle = display.DisplayHandle('my-id')
304 nt.assert_equal(handle.display_id, 'my-id')
311 nt.assert_equal(handle.display_id, 'my-id')
305 with mock.patch.object(ip.display_pub, 'publish') as pub:
312 with mock.patch.object(ip.display_pub, 'publish') as pub:
306 handle.display('x')
313 handle.display('x')
307 handle.update('y')
314 handle.update('y')
308
315
309 args, kwargs = pub.call_args_list[0]
316 args, kwargs = pub.call_args_list[0]
310 nt.assert_equal(args, ())
317 nt.assert_equal(args, ())
311 nt.assert_equal(kwargs, {
318 nt.assert_equal(kwargs, {
312 'data': {
319 'data': {
313 'text/plain': repr('x')
320 'text/plain': repr('x')
314 },
321 },
315 'metadata': {},
322 'metadata': {},
316 'transient': {
323 'transient': {
317 'display_id': handle.display_id,
324 'display_id': handle.display_id,
318 }
325 }
319 })
326 })
320 args, kwargs = pub.call_args_list[1]
327 args, kwargs = pub.call_args_list[1]
321 nt.assert_equal(args, ())
328 nt.assert_equal(args, ())
322 nt.assert_equal(kwargs, {
329 nt.assert_equal(kwargs, {
323 'data': {
330 'data': {
324 'text/plain': repr('y')
331 'text/plain': repr('y')
325 },
332 },
326 'metadata': {},
333 'metadata': {},
327 'transient': {
334 'transient': {
328 'display_id': handle.display_id,
335 'display_id': handle.display_id,
329 },
336 },
330 'update': True,
337 'update': True,
331 })
338 })
General Comments 0
You need to be logged in to leave comments. Login now