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