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