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