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