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