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