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