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