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